aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGlyn Normington2010-05-21 10:55:25 (EDT)
committerGlyn Normington2010-05-21 10:55:25 (EDT)
commit3b3b3cce3d53e2f33a3d867712cc3da678546f32 (patch)
treedaa601618ad67b582f7db27d3de45dabfc0b4a60
downloadorg.eclipse.gemini.web.gemini-web-container-3b3b3cce3d53e2f33a3d867712cc3da678546f32.zip
org.eclipse.gemini.web.gemini-web-container-3b3b3cce3d53e2f33a3d867712cc3da678546f32.tar.gz
org.eclipse.gemini.web.gemini-web-container-3b3b3cce3d53e2f33a3d867712cc3da678546f32.tar.bz2
initial checkin from dm Server Gemini web container repository commit c2384706fe667c135a021f427496cda6ef81a5ce
-rw-r--r--.gitignore6
-rw-r--r--.gitmodules3
-rw-r--r--build-web-container/build.xml30
-rwxr-xr-xbuild-web-container/copyJavaxToOsgi.sh16
-rwxr-xr-xbuild-web-container/copyToOsgi.sh26
-rw-r--r--build-web-container/java5.profile138
-rw-r--r--build-web-container/runner.bundles44
-rw-r--r--build.properties14
-rw-r--r--build.versions34
-rw-r--r--org.eclipse.gemini.web.core/.classpath45
-rw-r--r--org.eclipse.gemini.web.core/.project35
-rw-r--r--org.eclipse.gemini.web.core/.settings/com.springsource.server.ide.bundlor.core.prefs3
-rw-r--r--org.eclipse.gemini.web.core/.settings/org.eclipse.wst.common.project.facet.core.xml4
-rw-r--r--org.eclipse.gemini.web.core/.settings/org.springframework.ide.eclipse.core.prefs67
-rw-r--r--org.eclipse.gemini.web.core/.springBeans13
-rw-r--r--org.eclipse.gemini.web.core/build.xml13
-rw-r--r--org.eclipse.gemini.web.core/findbugs-exclude.xml23
-rw-r--r--org.eclipse.gemini.web.core/ivy.xml34
-rw-r--r--org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/core/ConnectorDescriptor.java51
-rw-r--r--org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/core/InstallationOptions.java202
-rw-r--r--org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/core/WebApplication.java62
-rw-r--r--org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/core/WebApplicationStartFailedException.java42
-rw-r--r--org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/core/WebBundleManifestTransformer.java48
-rw-r--r--org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/core/WebContainer.java143
-rw-r--r--org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/core/WebContainerProperties.java41
-rw-r--r--org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/core/spi/ContextPathExistsException.java38
-rw-r--r--org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/core/spi/ServletContainer.java50
-rw-r--r--org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/core/spi/ServletContainerException.java40
-rw-r--r--org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/core/spi/WebApplicationHandle.java49
-rw-r--r--org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/internal/EventManager.java156
-rw-r--r--org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/internal/StandardWebApplication.java160
-rw-r--r--org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/internal/StandardWebContainer.java79
-rw-r--r--org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/internal/SystemBundleExportsResolver.java79
-rw-r--r--org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/internal/WebApplicationStartFailureRetryController.java104
-rw-r--r--org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/internal/WebContainerActivator.java130
-rw-r--r--org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/internal/WebContainerUtils.java207
-rw-r--r--org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/internal/template/ServiceCallback.java21
-rw-r--r--org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/internal/template/ServiceTemplate.java70
-rw-r--r--org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/internal/template/ServiceUnavailableException.java42
-rw-r--r--org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/internal/url/ChainingWebBundleManifestTransformer.java44
-rw-r--r--org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/internal/url/DefaultsWebBundleManifestTransformer.java108
-rw-r--r--org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/internal/url/PackageMergeUtils.java81
-rw-r--r--org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/internal/url/PackagesInWarScanner.java46
-rw-r--r--org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/internal/url/Path.java165
-rw-r--r--org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/internal/url/SpecificationWebBundleManifestTransformer.java218
-rw-r--r--org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/internal/url/SystemBundleExportsImportingWebBundleManifestTransformer.java55
-rw-r--r--org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/internal/url/WebBundleScanner.java298
-rw-r--r--org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/internal/url/WebBundleScannerCallback.java24
-rw-r--r--org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/internal/url/WebBundleUrl.java130
-rw-r--r--org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/internal/url/WebBundleUrlStreamHandlerService.java134
-rw-r--r--org.eclipse.gemini.web.core/src/main/resources/META-INF/MANIFEST.MF25
-rw-r--r--org.eclipse.gemini.web.core/src/test/java/org/eclipse/gemini/web/internal/EventManagerTests.java122
-rw-r--r--org.eclipse.gemini.web.core/src/test/java/org/eclipse/gemini/web/internal/ManifestAsserts.java71
-rw-r--r--org.eclipse.gemini.web.core/src/test/java/org/eclipse/gemini/web/internal/SystemBundleExportsResolverTests.java114
-rw-r--r--org.eclipse.gemini.web.core/src/test/java/org/eclipse/gemini/web/internal/WebContainerUtilsTests.java251
-rw-r--r--org.eclipse.gemini.web.core/src/test/java/org/eclipse/gemini/web/internal/url/DefaultsWebBundleManifestTransformerTests.java121
-rw-r--r--org.eclipse.gemini.web.core/src/test/java/org/eclipse/gemini/web/internal/url/InstallationOptionsTests.java108
-rw-r--r--org.eclipse.gemini.web.core/src/test/java/org/eclipse/gemini/web/internal/url/PackagesInWarScannerTests.java95
-rw-r--r--org.eclipse.gemini.web.core/src/test/java/org/eclipse/gemini/web/internal/url/PathTests.java115
-rw-r--r--org.eclipse.gemini.web.core/src/test/java/org/eclipse/gemini/web/internal/url/SpecificationWebBundleManifestTransformerTests.java240
-rw-r--r--org.eclipse.gemini.web.core/src/test/java/org/eclipse/gemini/web/internal/url/SystemBundleExportsImportingWebBundleManifestTransformerTests.java133
-rw-r--r--org.eclipse.gemini.web.core/src/test/java/org/eclipse/gemini/web/internal/url/WebBundleScannerTests.java481
-rw-r--r--org.eclipse.gemini.web.core/src/test/java/org/eclipse/gemini/web/internal/url/WebBundleUrlStreamHandlerServiceTests.java74
-rw-r--r--org.eclipse.gemini.web.core/src/test/java/org/eclipse/gemini/web/internal/url/WebBundleUrlTests.java110
-rw-r--r--org.eclipse.gemini.web.core/src/test/resources/META-INF/TEST.MF7
-rw-r--r--org.eclipse.gemini.web.core/src/test/resources/contains-system-bundle-package.war/WEB-INF/classes/from/classes/Bar.class0
-rw-r--r--org.eclipse.gemini.web.core/src/test/resources/contains-system-bundle-package.war/WEB-INF/lib/a.jarbin0 -> 626 bytes
-rw-r--r--org.eclipse.gemini.web.core/src/test/resources/simple-manifest-only.jarbin0 -> 601 bytes
-rw-r--r--org.eclipse.gemini.web.core/src/test/resources/simple-war.warbin0 -> 406387 bytes
-rw-r--r--org.eclipse.gemini.web.core/template.mf18
-rw-r--r--org.eclipse.gemini.web.extender/.classpath30
-rw-r--r--org.eclipse.gemini.web.extender/.project35
-rw-r--r--org.eclipse.gemini.web.extender/.settings/com.springsource.server.ide.jdt.core.xml2
-rw-r--r--org.eclipse.gemini.web.extender/.settings/org.eclipse.wst.common.project.facet.core.xml4
-rw-r--r--org.eclipse.gemini.web.extender/.settings/org.springframework.ide.eclipse.core.prefs67
-rw-r--r--org.eclipse.gemini.web.extender/.springBeans13
-rw-r--r--org.eclipse.gemini.web.extender/build.xml13
-rw-r--r--org.eclipse.gemini.web.extender/findbugs-exclude.xml23
-rw-r--r--org.eclipse.gemini.web.extender/ivy.xml32
-rw-r--r--org.eclipse.gemini.web.extender/src/main/java/org/eclipse/gemini/web/extender/ExtenderActivator.java72
-rw-r--r--org.eclipse.gemini.web.extender/src/main/java/org/eclipse/gemini/web/extender/WebContainerBundleCustomizer.java66
-rw-r--r--org.eclipse.gemini.web.extender/src/main/resources/META-INF/MANIFEST.MF12
-rw-r--r--org.eclipse.gemini.web.extender/src/test/java/org/eclipse/gemini/web/extender/WebContainerCustomizerTests.java122
-rw-r--r--org.eclipse.gemini.web.extender/src/test/resources/.gitignore0
-rw-r--r--org.eclipse.gemini.web.extender/src/test/resources/META-INF/TEST.MF5
-rw-r--r--org.eclipse.gemini.web.extender/template.mf11
-rw-r--r--org.eclipse.gemini.web.test/.classpath28
-rw-r--r--org.eclipse.gemini.web.test/.project35
-rw-r--r--org.eclipse.gemini.web.test/.settings/org.eclipse.wst.common.project.facet.core.xml4
-rw-r--r--org.eclipse.gemini.web.test/.springBeans13
-rw-r--r--org.eclipse.gemini.web.test/build.xml11
-rw-r--r--org.eclipse.gemini.web.test/ivy.xml41
-rw-r--r--org.eclipse.gemini.web.test/src/main/java/.gitignore0
-rw-r--r--org.eclipse.gemini.web.test/src/main/resources/.gitignore0
-rw-r--r--org.eclipse.gemini.web.test/src/test/java/org/eclipse/gemini/web/test/WarInstallationTests.java50
-rw-r--r--org.eclipse.gemini.web.test/src/test/java/org/eclipse/gemini/web/test/extender/ClassPathDependencyTests.java97
-rw-r--r--org.eclipse.gemini.web.test/src/test/java/org/eclipse/gemini/web/test/extender/OverlappingWebContextPathsTests.java147
-rw-r--r--org.eclipse.gemini.web.test/src/test/java/org/eclipse/gemini/web/test/extender/StreamBasedExtenderTests.java379
-rw-r--r--org.eclipse.gemini.web.test/src/test/java/org/eclipse/gemini/web/test/extender/UrlBasedExtenderTests.java396
-rw-r--r--org.eclipse.gemini.web.test/src/test/java/org/eclipse/gemini/web/test/tomcat/TomcatServletContainerTests.java341
-rw-r--r--org.eclipse.gemini.web.test/src/test/resources/META-INF/MANIFEST.MF12
-rw-r--r--org.eclipse.gemini.web.test/src/test/resources/META-INF/test.config.properties51
-rw-r--r--org.eclipse.gemini.web.test/src/test/resources/classpathdeps.warbin0 -> 2014 bytes
-rw-r--r--org.eclipse.gemini.web.test/src/test/resources/cyclicclasspathdeps.warbin0 -> 2041 bytes
-rw-r--r--org.eclipse.gemini.web.test/src/test/resources/empty.warbin0 -> 929 bytes
-rw-r--r--org.eclipse.gemini.web.test/src/test/resources/ivysettings.xml52
-rw-r--r--org.eclipse.gemini.web.test/src/test/resources/java6-server.profile170
-rw-r--r--org.eclipse.gemini.web.test/src/test/resources/no-manifest.warbin0 -> 490918 bytes
-rw-r--r--org.eclipse.gemini.web.test/src/test/resources/specified-context-path-1.warbin0 -> 489712 bytes
-rw-r--r--org.eclipse.gemini.web.test/src/test/resources/specified-context-path-2.warbin0 -> 489708 bytes
-rw-r--r--org.eclipse.gemini.web.test/src/test/resources/war-with-another-servlet.warbin0 -> 105775 bytes
-rw-r--r--org.eclipse.gemini.web.test/src/test/resources/war-with-jsp.warbin0 -> 488093 bytes
-rw-r--r--org.eclipse.gemini.web.test/src/test/resources/war-with-servlet.warbin0 -> 105768 bytes
-rw-r--r--org.eclipse.gemini.web.test/src/test/resources/war-with-tld-from-dependency.warbin0 -> 1331 bytes
-rw-r--r--org.eclipse.gemini.web.test/src/test/resources/war-with-tld-import-system-packages.warbin0 -> 353536 bytes
-rw-r--r--org.eclipse.gemini.web.test/src/test/resources/war-with-tld.warbin0 -> 353446 bytes
-rw-r--r--org.eclipse.gemini.web.test/template.mf7
-rw-r--r--org.eclipse.gemini.web.tomcat/.classpath46
-rw-r--r--org.eclipse.gemini.web.tomcat/.project35
-rw-r--r--org.eclipse.gemini.web.tomcat/.settings/org.eclipse.wst.common.project.facet.core.xml4
-rw-r--r--org.eclipse.gemini.web.tomcat/.springBeans13
-rw-r--r--org.eclipse.gemini.web.tomcat/build.xml13
-rw-r--r--org.eclipse.gemini.web.tomcat/findbugs-exclude.xml23
-rw-r--r--org.eclipse.gemini.web.tomcat/ivy.xml55
-rw-r--r--org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/Activator.java113
-rw-r--r--org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/BundleDependenciesJarScanner.java133
-rw-r--r--org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/ChainingJarScanner.java49
-rw-r--r--org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/DelegatingClassLoaderCustomizer.java94
-rw-r--r--org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/Tomcat.java214
-rw-r--r--org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/TomcatConfigLocator.java125
-rw-r--r--org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/TomcatConnectorDescriptor.java77
-rw-r--r--org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/TomcatMBeanManager.java85
-rw-r--r--org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/TomcatServletContainer.java255
-rw-r--r--org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/TomcatServletContainerFactory.java32
-rw-r--r--org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/TomcatWebContainerProperties.java101
-rw-r--r--org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/loading/AbstractReadOnlyDirContext.java138
-rw-r--r--org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/loading/BaseWebappLoader.java327
-rw-r--r--org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/loading/BundleClassPathURLExtractor.java67
-rw-r--r--org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/loading/BundleDirContext.java152
-rw-r--r--org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/loading/BundleEntry.java179
-rw-r--r--org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/loading/BundleEntryAttributes.java148
-rw-r--r--org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/loading/BundleWebappClassLoader.java326
-rw-r--r--org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/loading/BundleWebappLoader.java248
-rw-r--r--org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/loading/ChainedClassLoader.java173
-rw-r--r--org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/loading/DirContextURLStreamHandlerService.java35
-rw-r--r--org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/loading/StandardWebBundleClassLoaderFactory.java54
-rw-r--r--org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/loading/URLResource.java44
-rw-r--r--org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/support/BundleDependencyDeterminer.java42
-rw-r--r--org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/support/BundleFileResolver.java38
-rw-r--r--org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/support/BundleFileResolverFactory.java44
-rw-r--r--org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/support/EquinoxBundleFileResolver.java55
-rw-r--r--org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/support/PackageAdminBundleDependencyDeterminer.java76
-rw-r--r--org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/spi/ClassLoaderCustomizer.java36
-rw-r--r--org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/spi/WebBundleClassLoaderFactory.java32
-rw-r--r--org.eclipse.gemini.web.tomcat/src/main/resources/META-INF/MANIFEST.MF32
-rw-r--r--org.eclipse.gemini.web.tomcat/src/main/resources/META-INF/tomcat/default-server.xml76
-rw-r--r--org.eclipse.gemini.web.tomcat/src/main/resources/web-embed.xml1186
-rw-r--r--org.eclipse.gemini.web.tomcat/src/test/java/org/eclipse/gemini/web/tomcat/internal/BundleDependenciesJarScannerTests.java134
-rw-r--r--org.eclipse.gemini.web.tomcat/src/test/java/org/eclipse/gemini/web/tomcat/internal/DummyManagedConnector.java36
-rw-r--r--org.eclipse.gemini.web.tomcat/src/test/java/org/eclipse/gemini/web/tomcat/internal/DummyManagedConnectorMBean.java29
-rw-r--r--org.eclipse.gemini.web.tomcat/src/test/java/org/eclipse/gemini/web/tomcat/internal/TomcatConfigLocatorTests.java87
-rw-r--r--org.eclipse.gemini.web.tomcat/src/test/java/org/eclipse/gemini/web/tomcat/internal/TomcatMBeanManagerTests.java62
-rw-r--r--org.eclipse.gemini.web.tomcat/src/test/java/org/eclipse/gemini/web/tomcat/internal/TomcatServletContainerFactoryTests.java47
-rw-r--r--org.eclipse.gemini.web.tomcat/src/test/java/org/eclipse/gemini/web/tomcat/internal/TomcatWebContainerPropertiesTests.java82
-rw-r--r--org.eclipse.gemini.web.tomcat/src/test/java/org/eclipse/gemini/web/tomcat/internal/loading/BundleClassPathURLExtractorTests.java65
-rw-r--r--org.eclipse.gemini.web.tomcat/src/test/java/org/eclipse/gemini/web/tomcat/internal/loading/BundleDirContextTests.java205
-rw-r--r--org.eclipse.gemini.web.tomcat/src/test/java/org/eclipse/gemini/web/tomcat/internal/loading/BundleEntryTests.java171
-rw-r--r--org.eclipse.gemini.web.tomcat/src/test/java/org/eclipse/gemini/web/tomcat/internal/loading/ChainedClassLoaderTests.java57
-rw-r--r--org.eclipse.gemini.web.tomcat/src/test/resources/META-INF/TEST.MF5
-rw-r--r--org.eclipse.gemini.web.tomcat/src/test/resources/a/b/c.txt0
-rw-r--r--org.eclipse.gemini.web.tomcat/src/test/resources/invalid-server.xml1
-rw-r--r--org.eclipse.gemini.web.tomcat/src/test/resources/server-with-props.xml3
-rw-r--r--org.eclipse.gemini.web.tomcat/src/test/resources/server.xml41
-rw-r--r--org.eclipse.gemini.web.tomcat/src/test/resources/sub/another.sub/two.txt0
-rw-r--r--org.eclipse.gemini.web.tomcat/src/test/resources/sub/one.txt1
-rw-r--r--org.eclipse.gemini.web.tomcat/template.mf31
-rw-r--r--org.eclipse.gemini.web/build.xml8
-rw-r--r--org.eclipse.gemini.web/ivy.xml22
-rw-r--r--test-bundles/build.properties15
-rw-r--r--test-bundles/simple-manifest-only/build.xml9
-rw-r--r--test-bundles/simple-manifest-only/ivy.xml22
-rw-r--r--test-bundles/simple-manifest-only/src/main/resources/META-INF/MANIFEST.MF3
-rw-r--r--test-bundles/simple-war/build.xml9
-rw-r--r--test-bundles/simple-war/ivy.xml24
-rw-r--r--test-bundles/simple-war/src/main/webapp/index.html8
-rw-r--r--test-bundles/war-with-another-servlet/.classpath7
-rw-r--r--test-bundles/war-with-another-servlet/.project17
-rw-r--r--test-bundles/war-with-another-servlet/build.xml9
-rw-r--r--test-bundles/war-with-another-servlet/ivy.xml25
-rw-r--r--test-bundles/war-with-another-servlet/src/main/java/test/servlet/TestServlet.java39
-rw-r--r--test-bundles/war-with-another-servlet/src/main/webapp/WEB-INF/web.xml19
-rw-r--r--test-bundles/war-with-jsp/build.xml9
-rw-r--r--test-bundles/war-with-jsp/ivy.xml25
-rw-r--r--test-bundles/war-with-jsp/src/main/webapp/index.jsp8
-rw-r--r--test-bundles/war-with-servlet/.classpath7
-rw-r--r--test-bundles/war-with-servlet/.project17
-rw-r--r--test-bundles/war-with-servlet/build.xml9
-rw-r--r--test-bundles/war-with-servlet/ivy.xml25
-rw-r--r--test-bundles/war-with-servlet/src/main/java/test/servlet/TestServlet.java39
-rw-r--r--test-bundles/war-with-servlet/src/main/webapp/WEB-INF/web.xml19
-rw-r--r--test-bundles/war-with-tld/build.xml9
-rw-r--r--test-bundles/war-with-tld/ivy.xml27
-rw-r--r--test-bundles/war-with-tld/src/main/webapp/test.jsp12
m---------virgo-build0
204 files changed, 14565 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..7f71dcd
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,6 @@
+target
+ivy-cache
+integration-repo
+*.iml
+*.iws
+*.ipr
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000..1f9ea18
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "virgo-build"]
+ path = virgo-build
+ url = git://git.eclipse.org/gitroot/virgo/org.eclipse.virgo.virgo-build.git
diff --git a/build-web-container/build.xml b/build-web-container/build.xml
new file mode 100644
index 0000000..96a0577
--- /dev/null
+++ b/build-web-container/build.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project name="build-web-container" xmlns:ivy="antlib:org.apache.ivy.ant">
+
+ <path id="unit.test.bundles">
+ <pathelement location="../org.eclipse.gemini.web.core"/>
+ <pathelement location="../org.eclipse.gemini.web.tomcat"/>
+ <pathelement location="../org.eclipse.gemini.web.extender"/>
+ </path>
+
+ <path id="bundles">
+ <path refid="unit.test.bundles"/>
+ <pathelement location="../org.eclipse.gemini.web"/>
+ <pathelement location="../org.eclipse.gemini.web.test"/>
+ </path>
+
+ <property file="${basedir}/../build.properties"/>
+ <property file="${basedir}/../build.versions"/>
+
+ <import file="${basedir}/../virgo-build/multi-bundle/default.xml"/>
+
+ <property name="collect.output.dir" value="${target.dir}/bundles"/>
+
+ <target name="precommit" depends="clean, clean-integration, test, findbugs"/>
+
+ <target name="collect" depends="ivy.init">
+ <mkdir dir="${collect.output.dir}"/>
+ <ivy:retrieve organisation="org.eclipse.gemini.web" module="org.eclipse.gemini.web" inline="true" type="jar" pattern="${collect.output.dir}/[artifact]-[revision].[ext]"/>
+ </target>
+
+</project>
diff --git a/build-web-container/copyJavaxToOsgi.sh b/build-web-container/copyJavaxToOsgi.sh
new file mode 100755
index 0000000..bd53d51
--- /dev/null
+++ b/build-web-container/copyJavaxToOsgi.sh
@@ -0,0 +1,16 @@
+# copies the results of a web container build (ant clean collect) to the relevant directories of the given OSGi trunk
+REPO=$1/licensed/repo
+SRC=target/bundles
+cp $SRC/com.springsource.javax.activation-1.1.0.jar $REPO/com.springsource.javax.activation/com.springsource.javax.activation-1.1.0.jar
+cp $SRC/com.springsource.javax.annotation-1.0.0.jar $REPO/com.springsource.javax.annotation/com.springsource.javax.annotation-1.0.0.jar
+cp $SRC/com.springsource.javax.ejb-3.0.0.jar $REPO/com.springsource.javax.ejb/com.springsource.javax.ejb-3.0.0.jar
+cp $SRC/com.springsource.javax.el-1.0.0.jar $REPO/com.springsource.javax.el/com.springsource.javax.el-1.0.0.jar
+cp $SRC/com.springsource.javax.persistence-1.0.0.jar $REPO/com.springsource.javax.persistence/com.springsource.javax.persistence-1.0.0.jar
+cp $SRC/com.springsource.javax.servlet-2.5.0.jar $REPO/com.springsource.javax.servlet/com.springsource.javax.servlet-2.5.0.jar
+cp $SRC/com.springsource.javax.servlet.jsp-2.1.0.jar $REPO/com.springsource.javax.servlet.jsp/com.springsource.javax.servlet.jsp-2.1.0.jar
+cp $SRC/com.springsource.javax.transaction-1.1.0.jar $REPO/com.springsource.javax.transaction/com.springsource.javax.transaction-1.1.0.jar
+cp $SRC/com.springsource.javax.xml.bind-2.1.7.jar $REPO/com.springsource.javax.xml.bind/com.springsource.javax.xml.bind-2.1.7.jar
+cp $SRC/com.springsource.javax.xml.rpc-1.1.0.jar $REPO/com.springsource.javax.xml.rpc/com.springsource.javax.xml.rpc-1.1.0.jar
+cp $SRC/com.springsource.javax.xml.soap-1.3.0.jar $REPO/com.springsource.javax.xml.soap/com.springsource.javax.xml.soap-1.3.0.jar
+cp $SRC/com.springsource.javax.xml.stream-1.0.1.jar $REPO/com.springsource.javax.xml.stream/com.springsource.javax.xml.stream-1.0.1.jar
+cp $SRC/com.springsource.javax.xml.ws-2.1.1.jar $REPO/com.springsource.javax.xml.ws/com.springsource.javax.xml.ws-2.1.1.jar
diff --git a/build-web-container/copyToOsgi.sh b/build-web-container/copyToOsgi.sh
new file mode 100755
index 0000000..642dac7
--- /dev/null
+++ b/build-web-container/copyToOsgi.sh
@@ -0,0 +1,26 @@
+# copies the results of a web container build (ant clean collect) to the relevant directories of the given OSGi trunk
+REPO=$1/licensed/repo
+SRC=target/bundles
+cp $SRC/org.eclipse.virgo.util.osgi-2.1.0.*.jar $REPO/org.eclipse.virgo.util.osgi/org.eclipse.virgo.util.osgi-2.1.0.jar
+cp $SRC/org.eclipse.virgo.util.common-2.1.0.*.jar $REPO/org.eclipse.virgo.util.common/org.eclipse.virgo.util.common-2.1.0.jar
+cp $SRC/org.eclipse.virgo.util.io-2.1.0.*.jar $REPO/org.eclipse.virgo.util.io/org.eclipse.virgo.util.io-2.1.0.jar
+cp $SRC/org.eclipse.virgo.util.math-2.1.0.*.jar $REPO/org.eclipse.virgo.util.math/org.eclipse.virgo.util.math-2.1.0.jar
+cp $SRC/org.eclipse.virgo.util.parser.manifest-2.1.0.*.jar $REPO/org.eclipse.virgo.util.parser.manifest/org.eclipse.virgo.util.parser.manifest-2.1.0.jar
+cp $SRC/org.springframework.aop-2.5.6.*.jar $REPO/org.springframework.aop/org.springframework.aop-2.5.6.jar
+cp $SRC/org.springframework.beans-2.5.6.*.jar $REPO/org.springframework.beans/org.springframework.beans-2.5.6.jar
+cp $SRC/org.springframework.context-2.5.6.*.jar $REPO/org.springframework.context/org.springframework.context-2.5.6.jar
+cp $SRC/org.springframework.core-2.5.6.*.jar $REPO/org.springframework.core/org.springframework.core-2.5.6.jar
+cp $SRC/org.springframework.osgi.core-1.2.0.jar $REPO/org.springframework.osgi.core/org.springframework.osgi.core-1.2.0.jar
+cp $SRC/org.springframework.osgi.io-1.2.0.jar $REPO/org.springframework.osgi.io/org.springframework.osgi.io-1.2.0.jar
+cp $SRC/com.springsource.org.apache.catalina.ha.springsource-6.0.20.*.jar $REPO/com.springsource.org.apache.catalina.ha.springsource/com.springsource.org.apache.catalina.ha.springsource-6.0.20.jar
+cp $SRC/com.springsource.org.apache.catalina.springsource-6.0.20.*.jar $REPO/com.springsource.org.apache.catalina.springsource/com.springsource.org.apache.catalina.springsource-6.0.20.jar
+cp $SRC/com.springsource.org.apache.catalina.tribes.springsource-6.0.20.*.jar $REPO/com.springsource.org.apache.catalina.tribes.springsource/com.springsource.org.apache.catalina.tribes.springsource-6.0.20.jar
+cp $SRC/com.springsource.org.apache.coyote.springsource-6.0.20.*.jar $REPO/com.springsource.org.apache.coyote.springsource/com.springsource.org.apache.coyote.springsource-6.0.20.jar
+cp $SRC/com.springsource.org.apache.el.springsource-6.0.20.*.jar $REPO/com.springsource.org.apache.el.springsource/com.springsource.org.apache.el.springsource-6.0.20.jar
+cp $SRC/com.springsource.org.apache.jasper.org.eclipse.jdt.springsource-6.0.20.*.jar $REPO/com.springsource.org.apache.jasper.org.eclipse.jdt.springsource/com.springsource.org.apache.jasper.org.eclipse.jdt.springsource-6.0.20.jar
+cp $SRC/com.springsource.org.apache.jasper.springsource-6.0.20.*.jar $REPO/com.springsource.org.apache.jasper.springsource/com.springsource.org.apache.jasper.springsource-6.0.20.jar
+cp $SRC/com.springsource.org.apache.juli.extras.springsource-6.0.20.*.jar $REPO/com.springsource.org.apache.juli.extras.springsource/com.springsource.org.apache.juli.extras.springsource-6.0.20.jar
+cp $SRC/com.springsource.org.apache.commons.logging-1.1.1.jar $REPO/com.springsource.org.apache.commons.logging/com.springsource.org.apache.commons.logging-1.1.1.jar
+cp $SRC/org.eclipse.gemini.web.core-1.0.0.*.jar $REPO/org.eclipse.gemini.web.core/org.eclipse.gemini.web.core-1.0.0.jar
+cp $SRC/org.eclipse.gemini.web.extender-1.0.0.*.jar $REPO/org.eclipse.gemini.web.extender/org.eclipse.gemini.web.extender-1.0.0.jar
+cp $SRC/org.eclipse.gemini.web.tomcat-1.0.0.*.jar $REPO/org.eclipse.gemini.web.tomcat/org.eclipse.gemini.web.tomcat-1.0.0.jar
diff --git a/build-web-container/java5.profile b/build-web-container/java5.profile
new file mode 100644
index 0000000..d95dac2
--- /dev/null
+++ b/build-web-container/java5.profile
@@ -0,0 +1,138 @@
+org.osgi.framework.system.packages = \
+ javax.accessibility,\
+ javax.crypto,\
+ javax.crypto.interfaces,\
+ javax.crypto.spec,\
+ javax.imageio,\
+ javax.imageio.event,\
+ javax.imageio.metadata,\
+ javax.imageio.plugins.bmp,\
+ javax.imageio.plugins.jpeg,\
+ javax.imageio.spi,\
+ javax.imageio.stream,\
+ javax.management,\
+ javax.management.loading,\
+ javax.management.modelmbean,\
+ javax.management.monitor,\
+ javax.management.openmbean,\
+ javax.management.relation,\
+ javax.management.remote,\
+ javax.management.remote.rmi,\
+ javax.management.timer,\
+ javax.naming,\
+ javax.naming.directory,\
+ javax.naming.event,\
+ javax.naming.ldap,\
+ javax.naming.spi,\
+ javax.net,\
+ javax.net.ssl,\
+ javax.print,\
+ javax.print.attribute,\
+ javax.print.attribute.standard,\
+ javax.print.event,\
+ javax.rmi,\
+ javax.rmi.CORBA,\
+ javax.rmi.ssl,\
+ javax.security.auth,\
+ javax.security.auth.callback,\
+ javax.security.auth.kerberos,\
+ javax.security.auth.login,\
+ javax.security.auth.spi,\
+ javax.security.auth.x500,\
+ javax.security.cert,\
+ javax.security.sasl,\
+ javax.sound.midi,\
+ javax.sound.midi.spi,\
+ javax.sound.sampled,\
+ javax.sound.sampled.spi,\
+ javax.sql,\
+ javax.sql.rowset,\
+ javax.sql.rowset.serial,\
+ javax.sql.rowset.spi,\
+ javax.swing,\
+ javax.swing.border,\
+ javax.swing.colorchooser,\
+ javax.swing.event,\
+ javax.swing.filechooser,\
+ javax.swing.plaf,\
+ javax.swing.plaf.basic,\
+ javax.swing.plaf.metal,\
+ javax.swing.plaf.multi,\
+ javax.swing.plaf.synth,\
+ javax.swing.table,\
+ javax.swing.text,\
+ javax.swing.text.html,\
+ javax.swing.text.html.parser,\
+ javax.swing.text.rtf,\
+ javax.swing.tree,\
+ javax.swing.undo,\
+ javax.transaction,\
+ javax.transaction;version="1.0.1",\
+ javax.transaction;version="1.1.0",\
+ javax.transaction.xa,\
+ javax.transaction.xa;version="1.0.1",\
+ javax.transaction.xa;version="1.1.0",\
+ javax.xml,\
+ javax.xml;version="1.0.1",\
+ javax.xml.datatype,\
+ javax.xml.namespace,\
+ javax.xml.parsers,\
+ javax.xml.transform,\
+ javax.xml.transform.dom,\
+ javax.xml.transform.sax,\
+ javax.xml.transform.stream,\
+ javax.xml.validation,\
+ javax.xml.xpath,\
+ org.ietf.jgss,\
+ org.omg.CORBA,\
+ org.omg.CORBA_2_3,\
+ org.omg.CORBA_2_3.portable,\
+ org.omg.CORBA.DynAnyPackage,\
+ org.omg.CORBA.ORBPackage,\
+ org.omg.CORBA.portable,\
+ org.omg.CORBA.TypeCodePackage,\
+ org.omg.CosNaming,\
+ org.omg.CosNaming.NamingContextExtPackage,\
+ org.omg.CosNaming.NamingContextPackage,\
+ org.omg.Dynamic,\
+ org.omg.DynamicAny,\
+ org.omg.DynamicAny.DynAnyFactoryPackage,\
+ org.omg.DynamicAny.DynAnyPackage,\
+ org.omg.IOP,\
+ org.omg.IOP.CodecFactoryPackage,\
+ org.omg.IOP.CodecPackage,\
+ org.omg.Messaging,\
+ org.omg.PortableInterceptor,\
+ org.omg.PortableInterceptor.ORBInitInfoPackage,\
+ org.omg.PortableServer,\
+ org.omg.PortableServer.CurrentPackage,\
+ org.omg.PortableServer.POAManagerPackage,\
+ org.omg.PortableServer.POAPackage,\
+ org.omg.PortableServer.portable,\
+ org.omg.PortableServer.ServantLocatorPackage,\
+ org.omg.SendingContext,\
+ org.omg.stub.java.rmi,\
+ org.w3c.dom,\
+ org.w3c.dom.bootstrap,\
+ org.w3c.dom.css,\
+ org.w3c.dom.events,\
+ org.w3c.dom.html,\
+ org.w3c.dom.ls,\
+ org.w3c.dom.ranges,\
+ org.w3c.dom.stylesheets,\
+ org.w3c.dom.traversal,\
+ org.w3c.dom.views ,\
+ org.xml.sax,\
+ org.xml.sax.ext,\
+ org.xml.sax.helpers
+org.osgi.framework.bootdelegation = \
+ com.sun.*,\
+ org.apache.xerces.jaxp.*,\
+ sun.*
+org.osgi.framework.executionenvironment = \
+ OSGi/Minimum-1.0,\
+ OSGi/Minimum-1.1,\
+ J2SE-1.3,\
+ J2SE-1.4,\
+ J2SE-1.5
+osgi.java.profile.name = SpringSource-dm-Server-Java5
diff --git a/build-web-container/runner.bundles b/build-web-container/runner.bundles
new file mode 100644
index 0000000..2d43056
--- /dev/null
+++ b/build-web-container/runner.bundles
@@ -0,0 +1,44 @@
+file:../ivy-cache/repository/org.slf4j/com.springsource.slf4j.api/1.5.10/com.springsource.slf4j.api-1.5.10.jar
+file:../ivy-cache/repository/org.slf4j/com.springsource.slf4j.nop/1.5.10/com.springsource.slf4j.nop-1.5.10.jar
+file:../ivy-cache/repository/org.eclipse.virgo.util/org.eclipse.virgo.util.common/2.1.0.D-20100311163244/org.eclipse.virgo.util.common-2.1.0.D-20100311163244.jar
+file:../ivy-cache/repository/org.eclipse.virgo.util/org.eclipse.virgo.util.io/2.1.0.D-20100311163244/org.eclipse.virgo.util.io-2.1.0.D-20100311163244.jar
+file:../ivy-cache/repository/org.eclipse.virgo.util/org.eclipse.virgo.util.math/2.1.0.D-20100311163244/org.eclipse.virgo.util.math-2.1.0.D-20100311163244.jar
+file:../ivy-cache/repository/org.eclipse.virgo.util/org.eclipse.virgo.util.osgi/2.1.0.D-20100311163244/org.eclipse.virgo.util.osgi-2.1.0.D-20100311163244.jar
+file:../ivy-cache/repository/org.eclipse.virgo.util/org.eclipse.virgo.util.parser.manifest/2.1.0.D-20100311163244/org.eclipse.virgo.util.parser.manifest-2.1.0.D-20100311163244.jar
+file:../ivy-cache/repository/javax.activation/com.springsource.javax.activation/1.1.1/com.springsource.javax.activation-1.1.1.jar
+file:../ivy-cache/repository/javax.annotation/com.springsource.javax.annotation/1.0.0/com.springsource.javax.annotation-1.0.0.jar
+file:../ivy-cache/repository/javax.ejb/com.springsource.javax.ejb/3.0.0/com.springsource.javax.ejb-3.0.0.jar
+file:../ivy-cache/repository/javax.el/com.springsource.javax.el/1.0.0/com.springsource.javax.el-1.0.0.jar
+file:../ivy-cache/repository/javax.mail/com.springsource.javax.mail/1.4.0/com.springsource.javax.mail-1.4.0.jar
+file:../ivy-cache/repository/javax.persistence/com.springsource.javax.persistence/1.0.0/com.springsource.javax.persistence-1.0.0.jar
+file:../ivy-cache/repository/javax.servlet/com.springsource.javax.servlet.jsp/2.1.0/com.springsource.javax.servlet.jsp-2.1.0.jar
+file:../ivy-cache/repository/javax.servlet/com.springsource.javax.servlet/2.5.0/com.springsource.javax.servlet-2.5.0.jar
+file:../ivy-cache/repository/javax.transaction/com.springsource.javax.transaction/1.1.0/com.springsource.javax.transaction-1.1.0.jar
+file:../ivy-cache/repository/javax.xml.bind/com.springsource.javax.xml.bind/2.1.7/com.springsource.javax.xml.bind-2.1.7.jar
+file:../ivy-cache/repository/javax.xml.rpc/com.springsource.javax.xml.rpc/1.1.0/com.springsource.javax.xml.rpc-1.1.0.jar
+file:../ivy-cache/repository/javax.xml.soap/com.springsource.javax.xml.soap/1.3.0/com.springsource.javax.xml.soap-1.3.0.jar
+file:../ivy-cache/repository/javax.xml.stream/com.springsource.javax.xml.stream/1.0.1/com.springsource.javax.xml.stream-1.0.1.jar
+file:../ivy-cache/repository/javax.xml.ws/com.springsource.javax.xml.ws/2.1.1/com.springsource.javax.xml.ws-2.1.1.jar
+file:../ivy-cache/repository/org.aopalliance/com.springsource.org.aopalliance/1.0.0/com.springsource.org.aopalliance-1.0.0.jar
+file:../ivy-cache/repository/org.apache.catalina.springsource/com.springsource.org.apache.catalina.ha.springsource/6.0.20.S2-r5956/com.springsource.org.apache.catalina.ha.springsource-6.0.20.S2-r5956.jar
+file:../ivy-cache/repository/org.apache.catalina.springsource/com.springsource.org.apache.catalina.tribes.springsource/6.0.20.S2-r5956/com.springsource.org.apache.catalina.tribes.springsource-6.0.20.S2-r5956.jar
+file:../ivy-cache/repository/org.apache.catalina.springsource/com.springsource.org.apache.catalina.springsource/6.0.20.S2-r5956/com.springsource.org.apache.catalina.springsource-6.0.20.S2-r5956.jar
+file:../ivy-cache/repository/org.apache.commons/com.springsource.org.apache.commons.logging/1.1.1/com.springsource.org.apache.commons.logging-1.1.1.jar
+file:../ivy-cache/repository/org.apache.coyote.springsource/com.springsource.org.apache.coyote.springsource/6.0.20.S2-r5956/com.springsource.org.apache.coyote.springsource-6.0.20.S2-r5956.jar
+file:../ivy-cache/repository/org.apache.el.springsource/com.springsource.org.apache.el.springsource/6.0.20.S2-r5956/com.springsource.org.apache.el.springsource-6.0.20.S2-r5956.jar
+file:../ivy-cache/repository/org.apache.jasper.springsource/com.springsource.org.apache.jasper.org.eclipse.jdt.springsource/6.0.20.S2-r5956/com.springsource.org.apache.jasper.org.eclipse.jdt.springsource-6.0.20.S2-r5956.jar
+file:../ivy-cache/repository/org.apache.jasper.springsource/com.springsource.org.apache.jasper.springsource/6.0.20.S2-r5956/com.springsource.org.apache.jasper.springsource-6.0.20.S2-r5956.jar
+file:../ivy-cache/repository/org.apache.juli.springsource/com.springsource.org.apache.juli.extras.springsource/6.0.20.S2-r5956/com.springsource.org.apache.juli.extras.springsource-6.0.20.S2-r5956.jar
+file:../ivy-cache/repository/org.eclipse.jdt/com.springsource.org.eclipse.jdt.core.compiler.batch/3.3.0/com.springsource.org.eclipse.jdt.core.compiler.batch-3.3.0.jar
+file:../ivy-cache/repository/org.springframework.osgi/org.springframework.osgi.core/1.2.0/org.springframework.osgi.core-1.2.0.jar
+file:../ivy-cache/repository/org.springframework.osgi/org.springframework.osgi.io/1.2.0/org.springframework.osgi.io-1.2.0.jar
+file:../ivy-cache/repository/org.springframework/org.springframework.aop/2.5.6.SEC01/org.springframework.aop-2.5.6.SEC01.jar
+file:../ivy-cache/repository/org.springframework/org.springframework.beans/2.5.6.SEC01/org.springframework.beans-2.5.6.SEC01.jar
+file:../ivy-cache/repository/org.springframework/org.springframework.context/2.5.6.SEC01/org.springframework.context-2.5.6.SEC01.jar
+file:../ivy-cache/repository/org.springframework/org.springframework.core/2.5.6.SEC01/org.springframework.core-2.5.6.SEC01.jar
+file:target/bundles/org.eclipse.gemini.web.core.jar
+file:target/bundles/org.eclipse.gemini.web.extender.jar
+file:target/bundles/org.eclipse.gemini.web.tomcat.jar
+
+-Dorg.ops4j.pax.runner.platform.clean=true
+-Dosgi.java.profile=file:java5.profile
diff --git a/build.properties b/build.properties
new file mode 100644
index 0000000..527379b
--- /dev/null
+++ b/build.properties
@@ -0,0 +1,14 @@
+version=1.0.0
+release.type=integration
+javadoc.exclude.package.names=**/internal/**,**/internal
+ivy.cache=ivy-cache
+ivy.cache.dir=${basedir}/../${ivy.cache}
+integration.repo.dir=${basedir}/../integration-repo
+source.version=1.5
+test.vm.args= -Xmx1024M -XX:MaxPermSize=512M -XX:+HeapDumpOnOutOfMemoryError
+findbugs.enforce=true
+clover.enforce=true
+clover.coverage=40%
+local.repository.dir=${basedir}/../../ivy-repository
+
+#debug string to append to test.vm.args= -Xdebug -Xrunjdwp:transport=dt_socket,address=8000,server=y,suspend=y
diff --git a/build.versions b/build.versions
new file mode 100644
index 0000000..1a740ce
--- /dev/null
+++ b/build.versions
@@ -0,0 +1,34 @@
+# Test
+org.junit=4.7.0
+org.easymock=2.3.0
+org.eclipse.virgo.teststubs=1.0.0.D-20100420091314
+org.eclipse.virgo.test=2.1.0.D-20100420091951
+
+# Compile
+org.eclipse.virgo.osgi=2.1.0.D-20100420091535
+org.eclipse.virgo.util=2.1.0.D-20100420091708
+javax.activation=1.1.1
+javax.annotation=1.0.0
+javax.ejb=3.0.0
+javax.el=1.0.0
+javax.mail=1.4.0
+javax.persistence=1.0.0
+javax.servlet.jsp=2.1.0
+javax.servlet.jstl=1.1.2
+javax.servlet=2.5.0
+javax.transaction=1.1.0
+javax.xml.bind=2.1.7
+javax.xml.rpc=1.1.0
+javax.xml.soap=1.3.0
+javax.xml.stream=1.0.1
+javax.xml.ws=2.1.1
+org.aopalliance=1.0.0
+org.apache.commons.logging=1.1.1
+org.apache.felix.configadmin=1.0.10
+org.apache.felix.eventadmin=1.0.0
+org.apache.tomcat=6.0.20.S2-r5956
+org.eclipse.jdt=3.3.0
+org.eclipse.osgi=3.5.1.R35x_v20091005
+org.slf4j=1.5.10
+org.springframework.osgi=1.2.0
+org.springframework=3.0.0.RELEASE
diff --git a/org.eclipse.gemini.web.core/.classpath b/org.eclipse.gemini.web.core/.classpath
new file mode 100644
index 0000000..dfeb4f0
--- /dev/null
+++ b/org.eclipse.gemini.web.core/.classpath
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="src" path="src/main/java">
+ <attributes>
+ <attribute name="com.springsource.server.ide.jdt.core.test.classpathentry" value="false"/>
+ </attributes>
+ </classpathentry>
+ <classpathentry kind="src" output="target/test-classes" path="src/test/resources">
+ <attributes>
+ <attribute name="com.springsource.server.ide.jdt.core.test.classpathentry" value="true"/>
+ </attributes>
+ </classpathentry>
+ <classpathentry kind="src" output="target/test-classes" path="src/test/java">
+ <attributes>
+ <attribute name="com.springsource.server.ide.jdt.core.test.classpathentry" value="true"/>
+ </attributes>
+ </classpathentry>
+ <classpathentry kind="src" path="src/main/resources">
+ <attributes>
+ <attribute name="com.springsource.server.ide.jdt.core.test.classpathentry" value="false"/>
+ </attributes>
+ </classpathentry>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+ <classpathentry kind="var" path="WEB_CONTAINER_IVY_CACHE/org.junit/com.springsource.org.junit/4.7.0/com.springsource.org.junit-4.7.0.jar" sourcepath="/WEB_CONTAINER_IVY_CACHE/org.junit/com.springsource.org.junit/4.5.0/com.springsource.org.junit-sources-4.5.0.jar"/>
+ <classpathentry kind="var" path="WEB_CONTAINER_IVY_CACHE/org.eclipse.osgi/org.eclipse.osgi/3.5.1.R35x_v20091005/org.eclipse.osgi-3.5.1.R35x_v20091005.jar" sourcepath="/WEB_CONTAINER_IVY_CACHE/org.eclipse.osgi/org.eclipse.osgi/3.5.1.R35x_v20091005/org.eclipse.osgi-sources-3.5.1.R35x_v20091005.jar"/>
+ <classpathentry kind="var" path="WEB_CONTAINER_IVY_CACHE/org.eclipse.virgo.util/org.eclipse.virgo.util.osgi/2.1.0.D-20100420091708/org.eclipse.virgo.util.osgi-2.1.0.D-20100420091708.jar" sourcepath="/WEB_CONTAINER_IVY_CACHE/org.eclispe.virgo.util/org.eclipse.virgo.util.osgi/2.0.0.RELEASE/org.eclipse.virgo.util.osgi-sources-2.0.0.RELEASE.jar"/>
+ <classpathentry kind="var" path="WEB_CONTAINER_IVY_CACHE/org.eclipse.virgo.util/org.eclipse.virgo.util.io/2.1.0.D-20100420091708/org.eclipse.virgo.util.io-2.1.0.D-20100420091708.jar" sourcepath="/WEB_CONTAINER_IVY_CACHE/org.eclispe.virgo.util/org.eclipse.virgo.util.io/2.0.0.RELEASE/org.eclipse.virgo.util.io-sources-2.0.0.RELEASE.jar"/>
+ <classpathentry kind="var" path="WEB_CONTAINER_IVY_CACHE/org.eclipse.virgo.util/org.eclipse.virgo.util.common/2.1.0.D-20100420091708/org.eclipse.virgo.util.common-2.1.0.D-20100420091708.jar" sourcepath="/WEB_CONTAINER_IVY_CACHE/org.eclispe.virgo.util/org.eclipse.virgo.util.common/2.0.0.RELEASE/org.eclipse.virgo.util.common-sources-2.0.0.RELEASE.jar"/>
+ <classpathentry kind="var" path="WEB_CONTAINER_IVY_CACHE/org.slf4j/com.springsource.slf4j.api/1.5.10/com.springsource.slf4j.api-1.5.10.jar" sourcepath="/WEB_CONTAINER_IVY_CACHE/org.slf4j/com.springsource.slf4j.api/1.5.10/com.springsource.slf4j.api-sources-1.5.10.jar"/>
+ <classpathentry kind="var" path="WEB_CONTAINER_IVY_CACHE/org.apache.catalina.springsource/com.springsource.org.apache.catalina.springsource/6.0.20.S2-r5956/com.springsource.org.apache.catalina.springsource-6.0.20.S2-r5956.jar" sourcepath="/WEB_CONTAINER_IVY_CACHE/org.apache.catalina.springsource/com.springsource.org.apache.catalina.springsource/6.0.20.S2-r5956/com.springsource.org.apache.catalina.springsource-sources-6.0.20.S2-r5956.jar"/>
+ <classpathentry kind="var" path="WEB_CONTAINER_IVY_CACHE/org.apache.juli.springsource/com.springsource.org.apache.juli.extras.springsource/6.0.20.S2-r5956/com.springsource.org.apache.juli.extras.springsource-6.0.20.S2-r5956.jar"/>
+ <classpathentry kind="var" path="WEB_CONTAINER_IVY_CACHE/org.apache.jasper.springsource/com.springsource.org.apache.jasper.springsource/6.0.20.S2-r5956/com.springsource.org.apache.jasper.springsource-6.0.20.S2-r5956.jar"/>
+ <classpathentry kind="var" path="WEB_CONTAINER_IVY_CACHE/org.apache.jasper.springsource/com.springsource.org.apache.jasper.org.eclipse.jdt.springsource/6.0.20.S2-r5956/com.springsource.org.apache.jasper.org.eclipse.jdt.springsource-6.0.20.S2-r5956.jar"/>
+ <classpathentry kind="var" path="WEB_CONTAINER_IVY_CACHE/org.apache.catalina.springsource/com.springsource.org.apache.catalina.ha.springsource/6.0.20.S2-r5956/com.springsource.org.apache.catalina.ha.springsource-6.0.20.S2-r5956.jar"/>
+ <classpathentry kind="var" path="WEB_CONTAINER_IVY_CACHE/org.apache.catalina.springsource/com.springsource.org.apache.catalina.tribes.springsource/6.0.20.S2-r5956/com.springsource.org.apache.catalina.tribes.springsource-6.0.20.S2-r5956.jar"/>
+ <classpathentry kind="var" path="WEB_CONTAINER_IVY_CACHE/org.apache.coyote.springsource/com.springsource.org.apache.coyote.springsource/6.0.20.S2-r5956/com.springsource.org.apache.coyote.springsource-6.0.20.S2-r5956.jar"/>
+ <classpathentry kind="var" path="WEB_CONTAINER_IVY_CACHE/javax.servlet/com.springsource.javax.servlet/2.5.0/com.springsource.javax.servlet-2.5.0.jar" sourcepath="/WEB_CONTAINER_IVY_CACHE/javax.servlet/com.springsource.javax.servlet/2.5.0/com.springsource.javax.servlet-sources-2.5.0.jar"/>
+ <classpathentry kind="var" path="WEB_CONTAINER_IVY_CACHE/org.easymock/com.springsource.org.easymock/2.3.0/com.springsource.org.easymock-2.3.0.jar" sourcepath="/WEB_CONTAINER_IVY_CACHE/org.easymock/com.springsource.org.easymock/2.3.0/com.springsource.org.easymock-sources-2.3.0.jar"/>
+ <classpathentry kind="var" path="WEB_CONTAINER_IVY_CACHE/org.apache.felix/org.apache.felix.eventadmin/1.0.0/org.apache.felix.eventadmin-1.0.0.jar" sourcepath="/WEB_CONTAINER_IVY_CACHE/org.apache.felix/org.apache.felix.eventadmin/1.0.0/org.apache.felix.eventadmin-sources-1.0.0.jar"/>
+ <classpathentry kind="var" path="WEB_CONTAINER_IVY_CACHE/org.slf4j/com.springsource.slf4j.nop/1.5.10/com.springsource.slf4j.nop-1.5.10.jar"/>
+ <classpathentry kind="var" path="WEB_CONTAINER_IVY_CACHE/org.eclipse.virgo.teststubs/org.eclipse.virgo.teststubs.osgi/1.0.0.D-20100420091314/org.eclipse.virgo.teststubs.osgi-1.0.0.D-20100420091314.jar" sourcepath="/WEB_CONTAINER_IVY_CACHE/org.eclispe.virgo.osgi.test/org.eclipse.virgo.teststubs.osgi/1.0.0.CI-B40/org.eclipse.virgo.teststubs.osgi-sources-1.0.0.CI-B40.jar"/>
+ <classpathentry kind="var" path="WEB_CONTAINER_IVY_CACHE/org.aspectj/com.springsource.org.aspectj.runtime/1.6.6.RELEASE/com.springsource.org.aspectj.runtime-1.6.6.RELEASE.jar"/>
+ <classpathentry kind="var" path="WEB_CONTAINER_IVY_CACHE/org.eclipse.virgo.util/org.eclipse.virgo.util.parser.manifest/2.1.0.D-20100420091708/org.eclipse.virgo.util.parser.manifest-2.1.0.D-20100420091708.jar"/>
+ <classpathentry kind="output" path="target/classes"/>
+</classpath>
diff --git a/org.eclipse.gemini.web.core/.project b/org.eclipse.gemini.web.core/.project
new file mode 100644
index 0000000..844a9de
--- /dev/null
+++ b/org.eclipse.gemini.web.core/.project
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>org.eclipse.gemini.web.core</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.wst.common.project.facet.core.builder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.springframework.ide.eclipse.core.springbuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>com.springsource.server.ide.bundlor.core.builder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>com.springsource.server.ide.facet.core.bundlenature</nature>
+ <nature>org.springframework.ide.eclipse.core.springnature</nature>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ <nature>org.eclipse.wst.common.project.facet.core.nature</nature>
+ </natures>
+</projectDescription>
diff --git a/org.eclipse.gemini.web.core/.settings/com.springsource.server.ide.bundlor.core.prefs b/org.eclipse.gemini.web.core/.settings/com.springsource.server.ide.bundlor.core.prefs
new file mode 100644
index 0000000..35eb0de
--- /dev/null
+++ b/org.eclipse.gemini.web.core/.settings/com.springsource.server.ide.bundlor.core.prefs
@@ -0,0 +1,3 @@
+#Wed Apr 15 15:27:27 BST 2009
+com.springsource.server.ide.bundlor.core.template.properties.files=template.properties\:build.properties
+eclipse.preferences.version=1
diff --git a/org.eclipse.gemini.web.core/.settings/org.eclipse.wst.common.project.facet.core.xml b/org.eclipse.gemini.web.core/.settings/org.eclipse.wst.common.project.facet.core.xml
new file mode 100644
index 0000000..801f856
--- /dev/null
+++ b/org.eclipse.gemini.web.core/.settings/org.eclipse.wst.common.project.facet.core.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<faceted-project>
+ <installed facet="com.springsource.server.bundle" version="1.0"/>
+</faceted-project>
diff --git a/org.eclipse.gemini.web.core/.settings/org.springframework.ide.eclipse.core.prefs b/org.eclipse.gemini.web.core/.settings/org.springframework.ide.eclipse.core.prefs
new file mode 100644
index 0000000..a45cf89
--- /dev/null
+++ b/org.eclipse.gemini.web.core/.settings/org.springframework.ide.eclipse.core.prefs
@@ -0,0 +1,67 @@
+#Wed Apr 15 15:01:45 BST 2009
+eclipse.preferences.version=1
+org.springframework.ide.eclipse.core.builders.enable.aopreferencemodelbuilder=true
+org.springframework.ide.eclipse.core.builders.enable.beanmetadatabuilder=false
+org.springframework.ide.eclipse.core.builders.enable.osgibundleupdater=true
+org.springframework.ide.eclipse.core.enable.project.preferences=true
+org.springframework.ide.eclipse.core.validator.enable.com.springsource.server.ide.manifest.core.manifestvalidator=false
+org.springframework.ide.eclipse.core.validator.enable.com.springsource.sts.bestpractices.beansvalidator=true
+org.springframework.ide.eclipse.core.validator.enable.com.springsource.sts.server.quickfix.manifestvalidator=false
+org.springframework.ide.eclipse.core.validator.enable.org.springframework.ide.eclipse.beans.core.beansvalidator=true
+org.springframework.ide.eclipse.core.validator.enable.org.springframework.ide.eclipse.core.springvalidator=false
+org.springframework.ide.eclipse.core.validator.enable.org.springframework.ide.eclipse.webflow.core.validator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.com.springsource.server.ide.manifest.core.applicationSymbolicNameRule-com.springsource.server.ide.manifest.core.manifestvalidator=false
+org.springframework.ide.eclipse.core.validator.rule.enable.com.springsource.server.ide.manifest.core.applicationVersionRule-com.springsource.server.ide.manifest.core.manifestvalidator=false
+org.springframework.ide.eclipse.core.validator.rule.enable.com.springsource.server.ide.manifest.core.bundleActivationPolicyRule-com.springsource.server.ide.manifest.core.manifestvalidator=false
+org.springframework.ide.eclipse.core.validator.rule.enable.com.springsource.server.ide.manifest.core.bundleActivatorRule-com.springsource.server.ide.manifest.core.manifestvalidator=false
+org.springframework.ide.eclipse.core.validator.rule.enable.com.springsource.server.ide.manifest.core.bundleManifestVersionRule-com.springsource.server.ide.manifest.core.manifestvalidator=false
+org.springframework.ide.eclipse.core.validator.rule.enable.com.springsource.server.ide.manifest.core.bundleNameRule-com.springsource.server.ide.manifest.core.manifestvalidator=false
+org.springframework.ide.eclipse.core.validator.rule.enable.com.springsource.server.ide.manifest.core.bundleSymbolicNameRule-com.springsource.server.ide.manifest.core.manifestvalidator=false
+org.springframework.ide.eclipse.core.validator.rule.enable.com.springsource.server.ide.manifest.core.bundleVersionRule-com.springsource.server.ide.manifest.core.manifestvalidator=false
+org.springframework.ide.eclipse.core.validator.rule.enable.com.springsource.server.ide.manifest.core.exportPackageRule-com.springsource.server.ide.manifest.core.manifestvalidator=false
+org.springframework.ide.eclipse.core.validator.rule.enable.com.springsource.server.ide.manifest.core.importRule-com.springsource.server.ide.manifest.core.manifestvalidator=false
+org.springframework.ide.eclipse.core.validator.rule.enable.com.springsource.server.ide.manifest.core.parsingProblemsRule-com.springsource.server.ide.manifest.core.manifestvalidator=false
+org.springframework.ide.eclipse.core.validator.rule.enable.com.springsource.server.ide.manifest.core.requireBundleRule-com.springsource.server.ide.manifest.core.manifestvalidator=false
+org.springframework.ide.eclipse.core.validator.rule.enable.com.springsource.sts.bestpractices.com.springsource.sts.bestpractices.AvoidDriverManagerDataSource-com.springsource.sts.bestpractices.beansvalidator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.com.springsource.sts.bestpractices.com.springsource.sts.bestpractices.ImportElementsAtTopRulee-com.springsource.sts.bestpractices.beansvalidator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.com.springsource.sts.bestpractices.com.springsource.sts.bestpractices.ParentBeanSpecifiesAbstractClassRule-com.springsource.sts.bestpractices.beansvalidator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.com.springsource.sts.bestpractices.com.springsource.sts.bestpractices.RefElementRule-com.springsource.sts.bestpractices.beansvalidator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.com.springsource.sts.bestpractices.com.springsource.sts.bestpractices.TooManyBeansInFileRule-com.springsource.sts.bestpractices.beansvalidator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.com.springsource.sts.bestpractices.com.springsource.sts.bestpractices.UnnecessaryValueElementRule-com.springsource.sts.bestpractices.beansvalidator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.com.springsource.sts.bestpractices.com.springsource.sts.bestpractices.UseBeanInheritance-com.springsource.sts.bestpractices.beansvalidator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.com.springsource.sts.bestpractices.legacyxmlusage.jndiobjectfactory-com.springsource.sts.bestpractices.beansvalidator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.com.springsource.sts.server.quickfix.importBundleVersionRule-com.springsource.sts.server.quickfix.manifestvalidator=false
+org.springframework.ide.eclipse.core.validator.rule.enable.com.springsource.sts.server.quickfix.importLibraryVersionRule-com.springsource.sts.server.quickfix.manifestvalidator=false
+org.springframework.ide.eclipse.core.validator.rule.enable.com.springsource.sts.server.quickfix.importPackageVersionRule-com.springsource.sts.server.quickfix.manifestvalidator=false
+org.springframework.ide.eclipse.core.validator.rule.enable.com.springsource.sts.server.quickfix.requireBundleVersionRule-com.springsource.sts.server.quickfix.manifestvalidator=false
+org.springframework.ide.eclipse.core.validator.rule.enable.org.springframework.ide.eclipse.beans.core.beanAlias-org.springframework.ide.eclipse.beans.core.beansvalidator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.org.springframework.ide.eclipse.beans.core.beanClass-org.springframework.ide.eclipse.beans.core.beansvalidator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.org.springframework.ide.eclipse.beans.core.beanConstructorArgument-org.springframework.ide.eclipse.beans.core.beansvalidator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.org.springframework.ide.eclipse.beans.core.beanDefinition-org.springframework.ide.eclipse.beans.core.beansvalidator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.org.springframework.ide.eclipse.beans.core.beanDefinitionHolder-org.springframework.ide.eclipse.beans.core.beansvalidator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.org.springframework.ide.eclipse.beans.core.beanFactory-org.springframework.ide.eclipse.beans.core.beansvalidator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.org.springframework.ide.eclipse.beans.core.beanInitDestroyMethod-org.springframework.ide.eclipse.beans.core.beansvalidator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.org.springframework.ide.eclipse.beans.core.beanProperty-org.springframework.ide.eclipse.beans.core.beansvalidator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.org.springframework.ide.eclipse.beans.core.beanReference-org.springframework.ide.eclipse.beans.core.beansvalidator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.org.springframework.ide.eclipse.beans.core.methodOverride-org.springframework.ide.eclipse.beans.core.beansvalidator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.org.springframework.ide.eclipse.beans.core.parsingProblems-org.springframework.ide.eclipse.beans.core.beansvalidator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.org.springframework.ide.eclipse.beans.core.requiredProperty-org.springframework.ide.eclipse.beans.core.beansvalidator=false
+org.springframework.ide.eclipse.core.validator.rule.enable.org.springframework.ide.eclipse.core.springClasspath-org.springframework.ide.eclipse.core.springvalidator=false
+org.springframework.ide.eclipse.core.validator.rule.enable.org.springframework.ide.eclipse.webflow.core.validation.action-org.springframework.ide.eclipse.webflow.core.validator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.org.springframework.ide.eclipse.webflow.core.validation.actionstate-org.springframework.ide.eclipse.webflow.core.validator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.org.springframework.ide.eclipse.webflow.core.validation.attribute-org.springframework.ide.eclipse.webflow.core.validator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.org.springframework.ide.eclipse.webflow.core.validation.attributemapper-org.springframework.ide.eclipse.webflow.core.validator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.org.springframework.ide.eclipse.webflow.core.validation.beanaction-org.springframework.ide.eclipse.webflow.core.validator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.org.springframework.ide.eclipse.webflow.core.validation.evaluationaction-org.springframework.ide.eclipse.webflow.core.validator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.org.springframework.ide.eclipse.webflow.core.validation.evaluationresult-org.springframework.ide.eclipse.webflow.core.validator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.org.springframework.ide.eclipse.webflow.core.validation.exceptionhandler-org.springframework.ide.eclipse.webflow.core.validator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.org.springframework.ide.eclipse.webflow.core.validation.import-org.springframework.ide.eclipse.webflow.core.validator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.org.springframework.ide.eclipse.webflow.core.validation.inputattribute-org.springframework.ide.eclipse.webflow.core.validator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.org.springframework.ide.eclipse.webflow.core.validation.mapping-org.springframework.ide.eclipse.webflow.core.validator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.org.springframework.ide.eclipse.webflow.core.validation.outputattribute-org.springframework.ide.eclipse.webflow.core.validator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.org.springframework.ide.eclipse.webflow.core.validation.set-org.springframework.ide.eclipse.webflow.core.validator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.org.springframework.ide.eclipse.webflow.core.validation.state-org.springframework.ide.eclipse.webflow.core.validator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.org.springframework.ide.eclipse.webflow.core.validation.subflowstate-org.springframework.ide.eclipse.webflow.core.validator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.org.springframework.ide.eclipse.webflow.core.validation.transition-org.springframework.ide.eclipse.webflow.core.validator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.org.springframework.ide.eclipse.webflow.core.validation.variable-org.springframework.ide.eclipse.webflow.core.validator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.org.springframework.ide.eclipse.webflow.core.validation.webflowstate-org.springframework.ide.eclipse.webflow.core.validator=true
diff --git a/org.eclipse.gemini.web.core/.springBeans b/org.eclipse.gemini.web.core/.springBeans
new file mode 100644
index 0000000..2257068
--- /dev/null
+++ b/org.eclipse.gemini.web.core/.springBeans
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<beansProjectDescription>
+ <version>1</version>
+ <pluginVersion><![CDATA[2.2.2.RELEASE]]></pluginVersion>
+ <configSuffixes>
+ <configSuffix><![CDATA[xml]]></configSuffix>
+ </configSuffixes>
+ <enableImports><![CDATA[false]]></enableImports>
+ <configs>
+ </configs>
+ <configSets>
+ </configSets>
+</beansProjectDescription>
diff --git a/org.eclipse.gemini.web.core/build.xml b/org.eclipse.gemini.web.core/build.xml
new file mode 100644
index 0000000..700982f
--- /dev/null
+++ b/org.eclipse.gemini.web.core/build.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project name="org.eclipse.gemini.web.core">
+
+ <property name="findbugs.exclude.file" value="${basedir}/findbugs-exclude.xml"/>
+ <property file="${basedir}/../build.properties"/>
+ <property file="${basedir}/../build.versions"/>
+ <import file="${basedir}/../virgo-build/standard/default.xml"/>
+
+ <target name="collect">
+ <copy file="${jar.output.file}" tofile="${collect.output.dir}/${ant.project.name}.jar"/>
+ </target>
+
+</project>
diff --git a/org.eclipse.gemini.web.core/findbugs-exclude.xml b/org.eclipse.gemini.web.core/findbugs-exclude.xml
new file mode 100644
index 0000000..b98ad71
--- /dev/null
+++ b/org.eclipse.gemini.web.core/findbugs-exclude.xml
@@ -0,0 +1,23 @@
+<FindBugsFilter>
+ <!-- Exclusions -->
+ <Match>
+ <Bug pattern="EI_EXPOSE_REP"/>
+ <Or>
+ <Class name="org.eclipse.virgo.kernel.config.internal.validate.KernelConfigPointSchemaWalker"/>
+ <Class name="org.eclipse.virgo.kernel.config.internal.validate.KernelConfigPointSchemaParser"/>
+ </Or>
+ <Method name="getTokenNames"/>
+ </Match>
+ <Match>
+ <Bug pattern="SIC_INNER_SHOULD_BE_STATIC"/>
+ <Class name="org.eclipse.virgo.kernel.config.internal.validate.KernelConfigPointSchemaParser$DFA15"/>
+ </Match>
+ <Match>
+ <Bug pattern="MS_PKGPROTECT"/>
+ <Or>
+ <Class name="org.eclipse.virgo.kernel.config.internal.validate.KernelConfigPointSchemaParser"/>
+ <Class name="org.eclipse.virgo.kernel.config.internal.validate.KernelConfigPointSchemaWalker"/>
+ </Or>
+ <Field name="tokenNames"/>
+ </Match>
+</FindBugsFilter>
diff --git a/org.eclipse.gemini.web.core/ivy.xml b/org.eclipse.gemini.web.core/ivy.xml
new file mode 100644
index 0000000..1fc68b7
--- /dev/null
+++ b/org.eclipse.gemini.web.core/ivy.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?xml-stylesheet type="text/xsl" href="http://ivyrep.jayasoft.org/ivy-doc.xsl"?>
+<ivy-module
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:noNamespaceSchemaLocation="http://incubator.apache.org/ivy/schemas/ivy.xsd"
+ version="1.3">
+
+ <info organisation="org.eclipse.gemini.web" module="${ant.project.name}"/>
+
+ <configurations>
+ <include file="${virgo.build.dir}/common/default-ivy-configurations.xml"/>
+ </configurations>
+
+ <publications>
+ <artifact name="${ant.project.name}"/>
+ <artifact name="${ant.project.name}-sources" type="src" ext="jar"/>
+ </publications>
+
+ <dependencies>
+ <dependency org="org.eclipse.virgo.util" name="org.eclipse.virgo.util.osgi" rev="${org.eclipse.virgo.util}" conf="compile->runtime"/>
+ <dependency org="org.eclipse.virgo.util" name="org.eclipse.virgo.util.io" rev="${org.eclipse.virgo.util}" conf="compile->runtime"/>
+ <dependency org="javax.servlet" name="com.springsource.javax.servlet" rev="${javax.servlet}" conf="compile->runtime"/>
+ <dependency org="org.apache.felix" name="org.apache.felix.eventadmin" rev="${org.apache.felix.eventadmin}" conf="compile->runtime" />
+ <dependency org="org.eclipse.osgi" name="org.eclipse.osgi" rev="${org.eclipse.osgi}" conf="compile->compile"/>
+ <dependency org="org.slf4j" name="com.springsource.slf4j.api" rev="${org.slf4j}" conf="compile->runtime"/>
+
+ <dependency org="org.eclipse.virgo.teststubs" name="org.eclipse.virgo.teststubs.osgi" rev="${org.eclipse.virgo.teststubs}" conf="test->runtime"/>
+ <dependency org="org.junit" name="com.springsource.org.junit" rev="${org.junit}" conf="test->runtime"/>
+ <dependency org="org.slf4j" name="com.springsource.slf4j.nop" rev="${org.slf4j}" conf="test,runtime->runtime"/>
+ <dependency org="org.easymock" name="com.springsource.org.easymock" rev="${org.easymock}" conf="test->runtime"/>
+
+ </dependencies>
+
+</ivy-module>
diff --git a/org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/core/ConnectorDescriptor.java b/org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/core/ConnectorDescriptor.java
new file mode 100644
index 0000000..12cdd82
--- /dev/null
+++ b/org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/core/ConnectorDescriptor.java
@@ -0,0 +1,51 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 VMware Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Apache License v2.0 which accompanies this distribution.
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Apache License v2.0 is available at
+ * http://www.opensource.org/licenses/apache2.0.php.
+ * You may elect to redistribute this code under either of these licenses.
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.gemini.web.core;
+
+/**
+ * <p>
+ * ConnectorDescriptor describes a configured connector to the web layer
+ * </p>
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * ConnectorDescriptor is thread-safe
+ *
+ */
+public interface ConnectorDescriptor {
+
+ /**
+ * @return The protocol being used, eg. HTTP-1.1
+ */
+ String getProtocol();
+
+ /**
+ * @return The scheme of the connector, eg. http.
+ */
+ String getScheme();
+
+ /**
+ * @return The port the connector is listening on, eg. 8080
+ */
+ int getPort();
+
+ /**
+ * @return true iff ssl is enabled
+ */
+ boolean sslEnabled();
+
+}
diff --git a/org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/core/InstallationOptions.java b/org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/core/InstallationOptions.java
new file mode 100644
index 0000000..b0b5ff0
--- /dev/null
+++ b/org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/core/InstallationOptions.java
@@ -0,0 +1,202 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 VMware Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Apache License v2.0 which accompanies this distribution.
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Apache License v2.0 is available at
+ * http://www.opensource.org/licenses/apache2.0.php.
+ * You may elect to redistribute this code under either of these licenses.
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.gemini.web.core;
+
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+
+import org.eclipse.gemini.web.internal.WebContainerUtils;
+import org.osgi.framework.Constants;
+
+
+/**
+ * Simple utility class that parses the user-supplied installation options from a {@link Map}.
+ */
+public final class InstallationOptions {
+
+ private static final class CaseInsensitiveMap extends HashMap<String, String> {
+
+ private static final long serialVersionUID = -514044030419642872L;
+
+ @Override
+ public String get(Object key) {
+ if (key instanceof String) {
+ return super.get(normaliseOption((String) key));
+ }
+ return super.get(key);
+ }
+
+ @Override
+ public String put(String key, String value) {
+ return super.put(normaliseOption(key), value);
+ }
+
+ private String normaliseOption(String key) {
+ return key.toLowerCase(Locale.ENGLISH);
+ }
+
+ }
+
+ private final String bundleSymbolicName;
+
+ private final String bundleVersion;
+
+ private final String bundleManifestVersion;
+
+ private final String bundleClassPath;
+
+ private final String importPackageDeclaration;
+
+ private final String exportPackageDeclaration;
+
+ private final String webContextPath;
+
+ private final String webJSPExtractLocation;
+
+ private volatile boolean defaultWABHeaders;
+
+ /**
+ * Creates a new <code>InstallationOptions</code> from the supplied <code>options</code> {@link Map}.
+ * <p/>
+ * Changes to the <code>options</code> <code>Map</code> are not reflected in the new instance.
+ *
+ * @param options the options
+ */
+ public InstallationOptions(Map<String, String> options) {
+ Map<String, String> normalisedOptions = normalise(options);
+
+ this.bundleSymbolicName = normalisedOptions.get(Constants.BUNDLE_SYMBOLICNAME);
+ this.bundleVersion = normalisedOptions.get(Constants.BUNDLE_VERSION);
+ this.bundleManifestVersion = normalisedOptions.get(Constants.BUNDLE_MANIFESTVERSION);
+ this.bundleClassPath = normalisedOptions.get(Constants.BUNDLE_CLASSPATH);
+
+ this.importPackageDeclaration = normalisedOptions.get(Constants.IMPORT_PACKAGE);
+ this.exportPackageDeclaration = normalisedOptions.get(Constants.EXPORT_PACKAGE);
+
+ this.webContextPath = normalisedOptions.get(WebContainerUtils.HEADER_WEB_CONTEXT_PATH);
+ this.webJSPExtractLocation = normalisedOptions.get(WebContainerUtils.HEADER_WEB_JSP_EXTRACT_LOCATION);
+
+ this.defaultWABHeaders = options.get(WebContainerUtils.HEADER_SPRINGSOURCE_DEFAULT_WAB_HEADERS) != null;
+ }
+
+ private Map<String, String> normalise(Map<String, String> options) {
+ Set<String> keys = options.keySet();
+ Map<String, String> normalisedOptions = new CaseInsensitiveMap();
+ for (String key : keys) {
+ normalisedOptions.put(key, options.get(key));
+ }
+ return normalisedOptions;
+ }
+
+ /**
+ * Gets the symbolic name installation option. This option overrides the <code>Bundle-SymbolicName</code> set in the
+ * web bundle manifest.
+ *
+ * @return the symbolic name installation option.
+ */
+ public String getBundleSymbolicName() {
+ return this.bundleSymbolicName;
+ }
+
+ /**
+ * Get the bundle version installation option. This option overrides the <code>Bundle-Version</code> set in the web
+ * bundle manifest.
+ *
+ * @return the bundle version installation option.
+ */
+ public String getBundleVersion() {
+ return this.bundleVersion;
+ }
+
+ /**
+ * Gets the unvalidated bundle manifest version installation option. This option overrides the
+ * <code>Bundle-ManifestVersion</code> set in the web bundle manifest.
+ * <p/>
+ * The bundle manifest version must not be validated early otherwise the IllegalArgumentException will not be turned
+ * into a BundleException by the OSGi framework.
+ *
+ * @return the unvalidated bundle manifest version installation option.
+ */
+ public String getBundleManifestVersion() {
+ return this.bundleManifestVersion;
+ }
+
+ /**
+ * Gets the bundle class path installation option. This option overrides the <code>Bundle-ClassPath</code> set in
+ * the web bundle manifest.
+ *
+ * @return the bundle class path installation option.
+ */
+ public String getBundleClassPath() {
+ return this.bundleClassPath;
+ }
+
+ /**
+ * Gets the import package installation option.
+ *
+ * @return the import package installation option.
+ */
+ public String getImportPackageDeclaration() {
+ return this.importPackageDeclaration;
+ }
+
+ /**
+ * Gets the export package installation option.
+ *
+ * @return the export package installation option.
+ */
+ public String getExportPackageDeclaration() {
+ return this.exportPackageDeclaration;
+ }
+
+ /**
+ * Gets the web context path installation option. This option overrides the context path specified in the manifest.
+ *
+ * @return the web context path installation option.
+ */
+ public String getWebContextPath() {
+ return this.webContextPath;
+ }
+
+ /**
+ * Gets the JSP extract location installation option. This option controls where the servlet container will extract
+ * application JSPs.
+ *
+ * @return the JSP extract location installation option.
+ */
+ public String getWebJSPExtractLocation() {
+ return this.webJSPExtractLocation;
+ }
+
+ /**
+ * Returns whether Web Application Bundle (WAB) manifest headers should be defaulted or not. This should be
+ * <code>false</code> for strict compliance with the OSGi Web Container specification and <code>true</code> for
+ * compatibility with the behaviour shipped with dm Server 2.0.0.RELEASE.
+ *
+ * @return the default WAB headers installation option.
+ */
+ public boolean getDefaultWABHeaders() {
+ return this.defaultWABHeaders;
+ }
+
+ public void setDefaultWABHeaders(boolean defaultWABHeaders) {
+ this.defaultWABHeaders = defaultWABHeaders;
+ }
+
+}
diff --git a/org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/core/WebApplication.java b/org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/core/WebApplication.java
new file mode 100644
index 0000000..542cf17
--- /dev/null
+++ b/org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/core/WebApplication.java
@@ -0,0 +1,62 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 VMware Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Apache License v2.0 which accompanies this distribution.
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Apache License v2.0 is available at
+ * http://www.opensource.org/licenses/apache2.0.php.
+ * You may elect to redistribute this code under either of these licenses.
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.gemini.web.core;
+
+import javax.servlet.ServletContext;
+
+import org.osgi.framework.Bundle;
+import org.osgi.service.event.EventAdmin;
+
+/**
+ * Represents a web application managed by a {@link WebContainer}.
+ *
+ * <p/>
+ *
+ * Web applications are created from valid web bundles using {@link WebContainer#createWebApplication(Bundle)}.
+ *
+ *
+ */
+public interface WebApplication {
+
+ /**
+ * Gets the {@link ServletContext} associated with this web application.
+ *
+ * @return the <code>ServletContext</code>, never <code>null</code>.
+ */
+ ServletContext getServletContext();
+
+ /**
+ * Gets the {@link ClassLoader} of this web application.
+ *
+ * @return the web application's <code>ClassLoader</code>.
+ */
+ ClassLoader getClassLoader();
+
+ /**
+ * Starts this web application under the {@link ServletContext#getContextPath() configured context path}.
+ * <p/>
+ * If the application fails to start an {@link EventAdmin} event is emitted to the
+ * <code>org/osgi/services/web/FAILED</code> topic and a {@link WebApplicationStartFailedException} is thrown.
+ * @throws WebApplicationStartFailedException
+ */
+ void start() throws WebApplicationStartFailedException;
+
+ /**
+ * Stops this web application. After stop the web application is no longer available to serve content.
+ */
+ void stop();
+}
diff --git a/org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/core/WebApplicationStartFailedException.java b/org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/core/WebApplicationStartFailedException.java
new file mode 100644
index 0000000..2b2cd23
--- /dev/null
+++ b/org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/core/WebApplicationStartFailedException.java
@@ -0,0 +1,42 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 VMware Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Apache License v2.0 which accompanies this distribution.
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Apache License v2.0 is available at
+ * http://www.opensource.org/licenses/apache2.0.php.
+ * You may elect to redistribute this code under either of these licenses.
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.gemini.web.core;
+
+
+/**
+ * Thrown to signal that a {@link WebApplication} has failed to {@link WebApplication#start()}.
+
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * Thread-safe.
+ *
+ */
+public final class WebApplicationStartFailedException extends RuntimeException {
+
+ private static final long serialVersionUID = -1722479683094175136L;
+
+ /**
+ * Creates a new WebApplicationStartFailedException with the supplied cause
+ * @param cause The cause of the failure
+ */
+ public WebApplicationStartFailedException(Throwable cause) {
+ super(cause);
+ }
+
+}
diff --git a/org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/core/WebBundleManifestTransformer.java b/org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/core/WebBundleManifestTransformer.java
new file mode 100644
index 0000000..bab96b4
--- /dev/null
+++ b/org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/core/WebBundleManifestTransformer.java
@@ -0,0 +1,48 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 VMware Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Apache License v2.0 which accompanies this distribution.
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Apache License v2.0 is available at
+ * http://www.opensource.org/licenses/apache2.0.php.
+ * You may elect to redistribute this code under either of these licenses.
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.gemini.web.core;
+
+import java.io.IOException;
+import java.net.URL;
+
+import org.eclipse.virgo.util.osgi.manifest.BundleManifest;
+
+/**
+ * Strategy for applying transformations to a web bundle manifest.
+ * <p/>
+ * The exact set of transformations performed is implementation-dependent, but it is expected that implementations will
+ * at least support the transformations mandated by the RFC66 specification.
+ * <p/>
+ * Transformations that are not defined by the specification should be disabled by default and enabled using an
+ * {@link InstallationOptions installation option}.
+ *
+ *
+ */
+public interface WebBundleManifestTransformer {
+
+ /**
+ * Transforms the supplied {@link BundleManifest} in place.
+ *
+ * @param manifest the <code>BundleManifest</code> to transform.
+ * @param sourceURL the {@link URL} the bundle was installed from.
+ * @param options the {@link InstallationOptions}. May be <code>null</code>.
+ * @param webBundle whether or not the bundle is deemed to be a bundle as determined by the
+ * {@link org.eclipse.gemini.web.internal.WebContainerUtils#isWebApplicationBundle isWebApplicationBundle} specification.
+ * @throws IOException if transformation fails.
+ */
+ void transform(BundleManifest manifest, URL sourceURL, InstallationOptions options, boolean webBundle) throws IOException;
+}
diff --git a/org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/core/WebContainer.java b/org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/core/WebContainer.java
new file mode 100644
index 0000000..1dd67f7
--- /dev/null
+++ b/org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/core/WebContainer.java
@@ -0,0 +1,143 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 VMware Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Apache License v2.0 which accompanies this distribution.
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Apache License v2.0 is available at
+ * http://www.opensource.org/licenses/apache2.0.php.
+ * You may elect to redistribute this code under either of these licenses.
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.gemini.web.core;
+
+import javax.servlet.ServletContext;
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleException;
+import org.osgi.service.event.EventAdmin;
+
+/**
+ * A <code>WebContainer</code> provides a mechanism for creating {@link WebApplication WebApplications} from
+ * user-supplied web bundles.
+ * <p/>
+ * The <code>WebContainer</code> provides programmatic access to the web bundle deployment functionality that is
+ * typically accessed via the extender.
+ *
+ */
+public interface WebContainer {
+
+ /**
+ * The {@link ServletContext} attribute under which the {@link BundleContext} is available.
+ */
+ public static final String ATTRIBUTE_BUNDLE_CONTEXT = "osgi-bundlecontext";
+
+ static final String EVENT_NAME_PREFIX = "org/osgi/service/web/";
+
+ /**
+ * The {@link EventAdmin} topic for web bundle <code>DEPLOYING</code> events.
+ */
+ static final String EVENT_DEPLOYING = EVENT_NAME_PREFIX + "DEPLOYING";
+
+ /**
+ * The {@link EventAdmin} topic for web bundle <code>DEPLOYED</code> events.
+ */
+ static final String EVENT_DEPLOYED = EVENT_NAME_PREFIX + "DEPLOYED";
+
+ /**
+ * The {@link EventAdmin} topic for web bundle <code>UNDEPLOYING</code> events.
+ */
+ static final String EVENT_UNDEPLOYING = EVENT_NAME_PREFIX + "UNDEPLOYING";
+
+ /**
+ * The {@link EventAdmin} topic for web bundle <code>UNDEPLOYED</code> events.
+ */
+ static final String EVENT_UNDEPLOYED = EVENT_NAME_PREFIX + "UNDEPLOYED";
+
+ /**
+ * The {@link EventAdmin} topic for web bundle <code>FAILED</code> events.
+ */
+ static final String EVENT_FAILED = EVENT_NAME_PREFIX + "FAILED";
+
+ /**
+ * The {@link org.osgi.service.event.Event Event} property for the web application bundle's context path.
+ */
+ static final String EVENT_PROPERTY_CONTEXT_PATH = "context.path";
+
+ /**
+ * The {@link org.osgi.service.event.Event Event} property for the web application bundle's version.
+ */
+ static final String EVENT_PROPERTY_BUNDLE_VERSION = "bundle.version";
+
+ /**
+ * The {@link org.osgi.service.event.Event Event} property for the web container extender bundle.
+ */
+ static final String EVENT_PROPERTY_EXTENDER_BUNDLE = "extender.bundle";
+
+ /**
+ * The {@link org.osgi.service.event.Event Event} property for the web container extender bundle's id.
+ */
+ static final String EVENT_PROPERTY_EXTENDER_BUNDLE_ID = "extender.bundle.id";
+
+ /**
+ * The {@link org.osgi.service.event.Event Event} property for the web container extender bundle's symbolic name.
+ */
+ static final String EVENT_PROPERTY_EXTENDER_BUNDLE_SYMBOLICNAME = "extender.bundle.symbolicName";
+
+ /**
+ * The {@link org.osgi.service.event.Event Event} property for the web container extender bundle's version.
+ */
+ static final String EVENT_PROPERTY_EXTENDER_BUNDLE_VERSION = "extender.bundle.version";
+
+ /**
+ * The {@link org.osgi.service.event.Event Event} property containing a Web-ContextPath which is shared by more than one bundle.
+ */
+ static final String EVENT_PROPERTY_COLLISION = "collision";
+
+ /**
+ * The {@link org.osgi.service.event.Event Event} property listing, in the case of a collsion, the bundle ids, as a
+ * <code>Collection&lt;Long&gt;</code>, which share the same Web-ContextPath.
+ */
+ static final String EVENT_PROPERTY_COLLISION_BUNDLES = "collision.bundles";
+
+ /**
+ * Creates a {@link WebApplication} for the supplied web bundle. Equivalent to calling
+ * {@link #createWebApplication(Bundle, Bundle) createWebApplication(bundle, null)}.
+ *
+ * @param bundle the web bundle
+ *
+ * @return the newly created <code>WebApplication</code>.
+ * @throws BundleException if the <code>WebApplication</code> cannot be created.
+ */
+ WebApplication createWebApplication(Bundle bundle) throws BundleException;
+
+ /**
+ * Creates a {@link WebApplication} for the supplied web bundle.
+ *
+ * @param bundle the web bundle
+ * @param extender the extender bundle that has trigger the creation of the web application, or <code>null</code> if
+ * an extender is not involved.
+ * @return the newly created <code>WebApplication</code>.
+ * @throws BundleException if the <code>WebApplication</code> cannot be created.
+ */
+ WebApplication createWebApplication(Bundle bundle, Bundle extender) throws BundleException;
+
+ /**
+ * Checks to see if the supplied {@link Bundle} is a valid web bundle.
+ *
+ * @param bundle the bundle to check.
+ * @return <code>true</code> if the supplied bundle is a valid web bundle; otherwise <code>false</code>.
+ */
+ boolean isWebBundle(Bundle bundle);
+
+ /**
+ * Stops the web container.
+ */
+ public void halt();
+}
diff --git a/org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/core/WebContainerProperties.java b/org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/core/WebContainerProperties.java
new file mode 100644
index 0000000..a2a099c
--- /dev/null
+++ b/org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/core/WebContainerProperties.java
@@ -0,0 +1,41 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 VMware Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Apache License v2.0 which accompanies this distribution.
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Apache License v2.0 is available at
+ * http://www.opensource.org/licenses/apache2.0.php.
+ * You may elect to redistribute this code under either of these licenses.
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.gemini.web.core;
+
+import java.util.Set;
+
+/**
+ * <p>
+ * WebContainerProperties allows applications running on this RFC66
+ * implementation to obtain the properties used to configure it.
+ * </p>
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * thread-safe
+ *
+ */
+public interface WebContainerProperties {
+
+ /**
+ * The port that this webcontainer is listening on.
+ *
+ * @return the port number
+ */
+ Set<ConnectorDescriptor> getConnectorDescriptors();
+
+}
diff --git a/org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/core/spi/ContextPathExistsException.java b/org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/core/spi/ContextPathExistsException.java
new file mode 100644
index 0000000..b089f13
--- /dev/null
+++ b/org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/core/spi/ContextPathExistsException.java
@@ -0,0 +1,38 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 VMware Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Apache License v2.0 which accompanies this distribution.
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Apache License v2.0 is available at
+ * http://www.opensource.org/licenses/apache2.0.php.
+ * You may elect to redistribute this code under either of these licenses.
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.gemini.web.core.spi;
+
+/**
+ * Exception signalling that a context path referred to by a web application is already in use by another web
+ * application.
+ *
+ */
+public class ContextPathExistsException extends ServletContainerException {
+
+ private static final long serialVersionUID = 1846326281773843365L;
+
+ private final String contextPath;
+
+ public ContextPathExistsException(String contextPath) {
+ super("Context path '" + contextPath + "' already exists");
+ this.contextPath = contextPath;
+ }
+
+ public String getContextPath() {
+ return contextPath;
+ }
+}
diff --git a/org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/core/spi/ServletContainer.java b/org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/core/spi/ServletContainer.java
new file mode 100644
index 0000000..4656c5a
--- /dev/null
+++ b/org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/core/spi/ServletContainer.java
@@ -0,0 +1,50 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 VMware Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Apache License v2.0 which accompanies this distribution.
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Apache License v2.0 is available at
+ * http://www.opensource.org/licenses/apache2.0.php.
+ * You may elect to redistribute this code under either of these licenses.
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.gemini.web.core.spi;
+
+import org.osgi.framework.Bundle;
+
+public interface ServletContainer {
+
+ /**
+ * Creates a web application for the supplied {@link Bundle}.
+ *
+ * @param contextPath the context path the web application should run under.
+ * @param bundle the <code>Bundle</code> containing the web application content.
+ * @return a handle to the web application that can be used to drive lifecycle events.
+ * @throws ServletContainerException if the web application cannot be created.
+ */
+ WebApplicationHandle createWebApplication(String contextPath, Bundle bundle);
+
+ /**
+ * Starts the web application referred to by the supplied {@link WebApplicationHandle}.
+ *
+ * @param handle the handle to the web application to start.
+ * @throws ContextPathExistsException if the context path is already in use.
+ * @throws ServletContainerException if the web application fails to start.
+ */
+ void startWebApplication(WebApplicationHandle handle);
+
+ /**
+ * Stops the web application referred to by the supplied {@link WebApplicationHandle}.
+ *
+ * @param handle the handle to the web application to stop.
+ * @throws ServletContainerException if the web application fails to stop.
+ */
+ void stopWebApplication(WebApplicationHandle handle);
+
+}
diff --git a/org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/core/spi/ServletContainerException.java b/org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/core/spi/ServletContainerException.java
new file mode 100644
index 0000000..5cf885e
--- /dev/null
+++ b/org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/core/spi/ServletContainerException.java
@@ -0,0 +1,40 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 VMware Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Apache License v2.0 which accompanies this distribution.
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Apache License v2.0 is available at
+ * http://www.opensource.org/licenses/apache2.0.php.
+ * You may elect to redistribute this code under either of these licenses.
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.gemini.web.core.spi;
+
+
+public class ServletContainerException extends RuntimeException {
+
+ private static final long serialVersionUID = -4955082179218908312L;
+
+ public ServletContainerException() {
+ }
+
+ public ServletContainerException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public ServletContainerException(String message) {
+ super(message);
+ }
+
+ public ServletContainerException(Throwable cause) {
+ super(cause);
+ }
+
+
+}
diff --git a/org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/core/spi/WebApplicationHandle.java b/org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/core/spi/WebApplicationHandle.java
new file mode 100644
index 0000000..ffd89ec
--- /dev/null
+++ b/org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/core/spi/WebApplicationHandle.java
@@ -0,0 +1,49 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 VMware Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Apache License v2.0 which accompanies this distribution.
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Apache License v2.0 is available at
+ * http://www.opensource.org/licenses/apache2.0.php.
+ * You may elect to redistribute this code under either of these licenses.
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.gemini.web.core.spi;
+
+import javax.servlet.ServletContext;
+
+/**
+ * Handle to a web application deployed in a {@link ServletContainer}. <code>ServletContainer</code> implementations
+ * will create custom subclasses of this interface and return them from {@link ServletContainer#createWebApplication}.
+ * The <code>ServletContainer</code> can store any state need during {@link ServletContainer#startWebApplication(WebApplicationHandle) start} and
+ * {@link ServletContainer#stopWebApplication(WebApplicationHandle) stop} in this custom implementation.
+ * <p/>
+ * Client code <strong>must</strong> return the correct handle to the <code>ServletContainer</code> when starting or
+ * stopping an application.
+ *
+ */
+public interface WebApplicationHandle {
+
+ /**
+ * Gets the {@link ServletContext} of the deployed web application.
+ *
+ * @return the <code>ServletContext</code>.
+ */
+ ServletContext getServletContext();
+
+ /**
+ * Gets the {@link ClassLoader} of the deployed web application. May be <code>null</code> if the
+ * web application has not yet been {@link ServletContainer#startWebApplication(WebApplicationHandle)
+ * started}.
+ *
+ * @return the <code>ClassLoader</code>.
+ */
+ ClassLoader getClassLoader();
+
+}
diff --git a/org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/internal/EventManager.java b/org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/internal/EventManager.java
new file mode 100644
index 0000000..96928fe
--- /dev/null
+++ b/org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/internal/EventManager.java
@@ -0,0 +1,156 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 VMware Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Apache License v2.0 which accompanies this distribution.
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Apache License v2.0 is available at
+ * http://www.opensource.org/licenses/apache2.0.php.
+ * You may elect to redistribute this code under either of these licenses.
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.gemini.web.internal;
+
+import static org.eclipse.gemini.web.core.WebContainer.EVENT_DEPLOYED;
+import static org.eclipse.gemini.web.core.WebContainer.EVENT_DEPLOYING;
+import static org.eclipse.gemini.web.core.WebContainer.EVENT_FAILED;
+import static org.eclipse.gemini.web.core.WebContainer.EVENT_PROPERTY_BUNDLE_VERSION;
+import static org.eclipse.gemini.web.core.WebContainer.EVENT_PROPERTY_COLLISION;
+import static org.eclipse.gemini.web.core.WebContainer.EVENT_PROPERTY_COLLISION_BUNDLES;
+import static org.eclipse.gemini.web.core.WebContainer.EVENT_PROPERTY_CONTEXT_PATH;
+import static org.eclipse.gemini.web.core.WebContainer.EVENT_PROPERTY_EXTENDER_BUNDLE;
+import static org.eclipse.gemini.web.core.WebContainer.EVENT_PROPERTY_EXTENDER_BUNDLE_ID;
+import static org.eclipse.gemini.web.core.WebContainer.EVENT_PROPERTY_EXTENDER_BUNDLE_SYMBOLICNAME;
+import static org.eclipse.gemini.web.core.WebContainer.EVENT_PROPERTY_EXTENDER_BUNDLE_VERSION;
+import static org.eclipse.gemini.web.core.WebContainer.EVENT_UNDEPLOYED;
+import static org.eclipse.gemini.web.core.WebContainer.EVENT_UNDEPLOYING;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.Set;
+
+import org.eclipse.gemini.web.internal.template.ServiceCallback;
+import org.eclipse.gemini.web.internal.template.ServiceTemplate;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.event.Event;
+import org.osgi.service.event.EventAdmin;
+import org.osgi.service.event.EventConstants;
+
+
+/**
+ * TODO: Need a better implementation of this - abstraction is poor :)
+ *
+ */
+final class EventManager {
+
+ private final ServiceTemplate<EventAdmin> template;
+
+ public EventManager(BundleContext context) {
+ if (isEventAdminAvailable()) {
+ this.template = new ServiceTemplate<EventAdmin>(context, EventAdmin.class);
+ } else {
+ this.template = null;
+ }
+ }
+
+ public void start() {
+ if (this.template != null) {
+ this.template.start();
+ }
+ }
+
+ public void stop() {
+ if (this.template != null) {
+ this.template.stop();
+ }
+ }
+
+ public void sendDeploying(Bundle applicationBundle, Bundle extenderBundle, String contextPath) {
+ sendEvent(EVENT_DEPLOYING, applicationBundle, extenderBundle, contextPath, null, null, null);
+ }
+
+ public void sendDeployed(Bundle applicationBundle, Bundle extenderBundle, String contextPath) {
+ sendEvent(EVENT_DEPLOYED, applicationBundle, extenderBundle, contextPath, null, null, null);
+ }
+
+ public void sendUndeploying(Bundle applicationBundle, Bundle extenderBundle, String contextPath) {
+ sendEvent(EVENT_UNDEPLOYING, applicationBundle, extenderBundle, contextPath, null, null, null);
+ }
+
+ public void sendUndeployed(Bundle applicationBundle, Bundle extenderBundle, String contextPath) {
+ sendEvent(EVENT_UNDEPLOYED, applicationBundle, extenderBundle, contextPath, null, null, null);
+ }
+
+ public void sendFailed(Bundle applicationBundle, Bundle extenderBundle, String contextPath, Exception ex, String collidingWebContextPath,
+ Set<Long> collisionBundles) {
+ sendEvent(EVENT_FAILED, applicationBundle, extenderBundle, contextPath, ex, collidingWebContextPath, collisionBundles);
+ }
+
+ private void sendEvent(final String eventName, final Bundle applicationBundle, final Bundle extenderBundle, final String contextPath,
+ final Throwable ex, final String collidingWebContextPath, final Set<Long> collisionBundles) {
+ if (this.template != null) {
+ this.template.executeWithService(new ServiceCallback<EventAdmin, Void>() {
+
+ public Void doWithService(EventAdmin eventAdmin) {
+ Dictionary<String, Object> props = new Hashtable<String, Object>();
+ if (applicationBundle.getSymbolicName() != null) {
+ props.put(EventConstants.BUNDLE_SYMBOLICNAME, applicationBundle.getSymbolicName());
+ }
+ props.put(EventConstants.BUNDLE_ID, applicationBundle.getBundleId());
+ props.put(EventConstants.BUNDLE, applicationBundle);
+ props.put(EVENT_PROPERTY_BUNDLE_VERSION, applicationBundle.getVersion());
+ props.put(EventConstants.TIMESTAMP, System.currentTimeMillis());
+ props.put(EVENT_PROPERTY_CONTEXT_PATH, contextPath);
+
+ if (extenderBundle != null) {
+ props.put(EVENT_PROPERTY_EXTENDER_BUNDLE, extenderBundle);
+ props.put(EVENT_PROPERTY_EXTENDER_BUNDLE_ID, extenderBundle.getBundleId());
+ if (extenderBundle.getSymbolicName() != null) {
+ props.put(EVENT_PROPERTY_EXTENDER_BUNDLE_SYMBOLICNAME, extenderBundle.getSymbolicName());
+ }
+ props.put(EVENT_PROPERTY_EXTENDER_BUNDLE_VERSION, extenderBundle.getVersion());
+ }
+
+ if (ex != null) {
+ props.put(EventConstants.EXCEPTION, ex);
+ }
+
+ if (collidingWebContextPath != null) {
+ props.put(EVENT_PROPERTY_COLLISION, collidingWebContextPath);
+
+ /*
+ * Prevent event handlers modifying the set of collision bundles.
+ *
+ * Note: OSGi specs prefer Collection to Set even when there cannot be duplicates.
+ */
+ Collection<Long> immutableCollisionBundles = Collections.unmodifiableCollection(collisionBundles);
+ props.put(EVENT_PROPERTY_COLLISION_BUNDLES, immutableCollisionBundles);
+ }
+
+ eventAdmin.sendEvent(new Event(eventName, props));
+ return null;
+ }
+
+ });
+ }
+ }
+
+ private boolean isEventAdminAvailable() {
+ try {
+ getClass().getClassLoader().loadClass(EventAdmin.class.getName());
+ return true;
+ } catch (NoClassDefFoundError ex) {
+ return false;
+ } catch (ClassNotFoundException ex) {
+ return false;
+ }
+ }
+}
diff --git a/org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/internal/StandardWebApplication.java b/org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/internal/StandardWebApplication.java
new file mode 100644
index 0000000..9f88dd1
--- /dev/null
+++ b/org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/internal/StandardWebApplication.java
@@ -0,0 +1,160 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 VMware Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Apache License v2.0 which accompanies this distribution.
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Apache License v2.0 is available at
+ * http://www.opensource.org/licenses/apache2.0.php.
+ * You may elect to redistribute this code under either of these licenses.
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.gemini.web.internal;
+
+import java.util.HashSet;
+import java.util.Properties;
+import java.util.Set;
+
+import javax.servlet.ServletContext;
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+import org.eclipse.gemini.web.core.WebApplication;
+import org.eclipse.gemini.web.core.WebApplicationStartFailedException;
+import org.eclipse.gemini.web.core.spi.ServletContainer;
+import org.eclipse.gemini.web.core.spi.ServletContainerException;
+import org.eclipse.gemini.web.core.spi.WebApplicationHandle;
+import org.eclipse.virgo.util.osgi.ServiceRegistrationTracker;
+
+final class StandardWebApplication implements WebApplication {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(StandardWebApplication.class);
+
+ private final BundleContext bundleContext;
+
+ private final Bundle extender;
+
+ private final WebApplicationHandle handle;
+
+ private final ServletContainer container;
+
+ private final ServiceRegistrationTracker tracker = new ServiceRegistrationTracker();
+
+ private final EventManager eventManager;
+
+ private boolean started = false;
+
+ private final Object monitor = new Object();
+
+ private final WebApplicationStartFailureRetryController retryController;
+
+ public StandardWebApplication(BundleContext bundleContext, Bundle extender, WebApplicationHandle handle, ServletContainer container,
+ EventManager eventManager, WebApplicationStartFailureRetryController retryController) {
+ this.bundleContext = bundleContext;
+ this.extender = extender;
+ this.handle = handle;
+ this.container = container;
+ this.eventManager = eventManager;
+ this.retryController = retryController;
+ }
+
+ public ServletContext getServletContext() {
+ return this.handle.getServletContext();
+ }
+
+ public ClassLoader getClassLoader() {
+ return this.handle.getClassLoader();
+ }
+
+ public void start() {
+ boolean localStarted;
+
+ synchronized (this.monitor) {
+ localStarted = this.started;
+ }
+
+ if (!localStarted) {
+ String webContextPath = getContextPath();
+ this.eventManager.sendDeploying(getBundle(), this.extender, webContextPath);
+
+ try {
+ this.container.startWebApplication(this.handle);
+ publishServletContext();
+
+ synchronized (this.monitor) {
+ this.started = true;
+ }
+
+ this.eventManager.sendDeployed(getBundle(), this.extender, webContextPath);
+ } catch (ServletContainerException ex) {
+ if (LOGGER.isErrorEnabled()) {
+ LOGGER.error("Failed to start web application at bundleContext path '" + this.handle.getServletContext().getContextPath() + "'", ex);
+ }
+ this.retryController.recordFailure(this);
+ Set<Long> webContextPathBundleIds = getWebContextPathBundleIds(webContextPath);
+ boolean collision = webContextPathBundleIds.size() > 1;
+ this.eventManager.sendFailed(getBundle(), this.extender, webContextPath, ex, collision ? webContextPath : null,
+ collision ? webContextPathBundleIds : null);
+ throw new WebApplicationStartFailedException(ex);
+ }
+ }
+ }
+
+ private Set<Long> getWebContextPathBundleIds(String webContextPath) {
+ Set<Long> bundleIds = new HashSet<Long>();
+ for (Bundle bundle : this.bundleContext.getBundles()) {
+ if (webContextPath.equals(WebContainerUtils.getContextPath(bundle))) {
+ bundleIds.add(bundle.getBundleId());
+ }
+ }
+ return bundleIds;
+ }
+
+ public void stop() {
+ boolean localStarted;
+
+ synchronized (this.monitor) {
+ localStarted = this.started;
+ this.started = false;
+ }
+
+ if (localStarted) {
+ this.eventManager.sendUndeploying(getBundle(), this.extender, getContextPath());
+ this.container.stopWebApplication(this.handle);
+ this.tracker.unregisterAll();
+ this.eventManager.sendUndeployed(getBundle(), this.extender, getContextPath());
+ }
+ this.retryController.retryFailures(this);
+ }
+
+ private void publishServletContext() {
+ Properties properties = constructServletContextProperties();
+ this.tracker.track(this.bundleContext.registerService(ServletContext.class.getName(), getServletContext(), properties));
+ }
+
+ String getContextPath() {
+ return this.handle.getServletContext().getContextPath();
+ }
+
+ Bundle getBundle() {
+ return this.bundleContext.getBundle();
+ }
+
+ private Properties constructServletContextProperties() {
+ Properties properties = new Properties();
+ Bundle bundle = getBundle();
+ WebContainerUtils.setServletContextBundleProperties(properties, bundle);
+ properties.setProperty("osgi.web.contextpath", getServletContext().getContextPath());
+ return properties;
+ }
+
+}
diff --git a/org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/internal/StandardWebContainer.java b/org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/internal/StandardWebContainer.java
new file mode 100644
index 0000000..9662423
--- /dev/null
+++ b/org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/internal/StandardWebContainer.java
@@ -0,0 +1,79 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 VMware Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Apache License v2.0 which accompanies this distribution.
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Apache License v2.0 is available at
+ * http://www.opensource.org/licenses/apache2.0.php.
+ * You may elect to redistribute this code under either of these licenses.
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.gemini.web.internal;
+
+import org.eclipse.gemini.web.core.WebApplication;
+import org.eclipse.gemini.web.core.WebContainer;
+import org.eclipse.gemini.web.core.spi.ServletContainer;
+import org.eclipse.gemini.web.core.spi.ServletContainerException;
+import org.eclipse.gemini.web.core.spi.WebApplicationHandle;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * Standard implementation of {@link WebContainer}.
+ */
+final class StandardWebContainer implements WebContainer {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(StandardWebContainer.class);
+
+ private final EventManager eventManager;
+
+ private final ServletContainer servletContainer;
+
+ private final WebApplicationStartFailureRetryController retryController = new WebApplicationStartFailureRetryController();
+
+ public StandardWebContainer(ServletContainer servletContainer, EventManager eventManager) {
+ this.servletContainer = servletContainer;
+ this.eventManager = eventManager;
+ }
+
+ public WebApplication createWebApplication(Bundle bundle) throws BundleException {
+ return this.createWebApplication(bundle, null);
+ }
+
+ public WebApplication createWebApplication(Bundle bundle, Bundle extender) throws BundleException {
+ if (!isWebBundle(bundle)) {
+ throw new BundleException("Bundle '" + bundle + "' is not a valid web bundle.");
+ }
+ try {
+ WebApplicationHandle handle = this.servletContainer.createWebApplication(WebContainerUtils.getContextPath(bundle), bundle);
+ handle.getServletContext().setAttribute(ATTRIBUTE_BUNDLE_CONTEXT, bundle.getBundleContext());
+ return new StandardWebApplication(bundle.getBundleContext(), extender, handle, this.servletContainer, this.eventManager, this.retryController);
+ } catch (ServletContainerException ex) {
+ if (LOGGER.isErrorEnabled()) {
+ LOGGER.error("Failed to create web application for bundle '" + bundle + "'", ex);
+ }
+ throw new BundleException("Failed to create web application for bundle '" + bundle + "'", ex);
+ }
+ }
+
+ public boolean isWebBundle(Bundle bundle) {
+ return WebContainerUtils.isWebBundle(bundle);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void halt() {
+ this.retryController.clear();
+ }
+
+}
diff --git a/org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/internal/SystemBundleExportsResolver.java b/org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/internal/SystemBundleExportsResolver.java
new file mode 100644
index 0000000..9c0ddf1
--- /dev/null
+++ b/org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/internal/SystemBundleExportsResolver.java
@@ -0,0 +1,79 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 VMware Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Apache License v2.0 which accompanies this distribution.
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Apache License v2.0 is available at
+ * http://www.opensource.org/licenses/apache2.0.php.
+ * You may elect to redistribute this code under either of these licenses.
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.gemini.web.internal;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Version;
+import org.osgi.service.packageadmin.ExportedPackage;
+import org.osgi.service.packageadmin.PackageAdmin;
+
+import org.eclipse.gemini.web.internal.template.ServiceCallback;
+import org.eclipse.gemini.web.internal.template.ServiceTemplate;
+import org.eclipse.virgo.util.osgi.VersionRange;
+
+final class SystemBundleExportsResolver {
+
+ private final BundleContext bundleContext;
+
+ SystemBundleExportsResolver(BundleContext bundleContext) {
+ this.bundleContext = bundleContext;
+ }
+
+ public Map<String, VersionRange> getSystemBundleExports() {
+ final Bundle systemBundle = bundleContext.getBundle(0);
+ ServiceTemplate<PackageAdmin> packageAdminTemplate = new ServiceTemplate<PackageAdmin>(bundleContext, PackageAdmin.class);
+ packageAdminTemplate.start();
+ ExportedPackage[] systemBundleExports = packageAdminTemplate.executeWithService(new ServiceCallback<PackageAdmin, ExportedPackage[]>() {
+ public ExportedPackage[] doWithService(PackageAdmin packageAdmin) {
+ return packageAdmin.getExportedPackages(systemBundle);
+ }
+ });
+ packageAdminTemplate.stop();
+ return combineDuplicateExports(systemBundleExports);
+ }
+
+ static Map<String, VersionRange> combineDuplicateExports(ExportedPackage[] allExportedPackages) {
+ Map<String, VersionRange> exportedPackages = new HashMap<String, VersionRange>();
+ for (ExportedPackage exportedPackage : allExportedPackages) {
+ VersionRange versionRange = exportedPackages.get(exportedPackage.getName());
+ if (versionRange == null) {
+ versionRange = VersionRange.createExactRange(exportedPackage.getVersion());
+ } else {
+ Version version = exportedPackage.getVersion();
+ if (!versionRange.includes(version)) {
+ versionRange = expandVersionRange(version, versionRange);
+ }
+ }
+ exportedPackages.put(exportedPackage.getName(), versionRange);
+ }
+
+ return exportedPackages;
+ }
+
+ private static VersionRange expandVersionRange(Version version, VersionRange versionRange) {
+ Version ceiling = versionRange.getCeiling();
+ if (version.compareTo(ceiling) > 0) {
+ return new VersionRange("[" + versionRange.getFloor() + "," + version + "]");
+ } else {
+ return new VersionRange("[" + version + "," + versionRange.getCeiling() + "]");
+ }
+ }
+}
diff --git a/org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/internal/WebApplicationStartFailureRetryController.java b/org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/internal/WebApplicationStartFailureRetryController.java
new file mode 100644
index 0000000..ae9fb33
--- /dev/null
+++ b/org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/internal/WebApplicationStartFailureRetryController.java
@@ -0,0 +1,104 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 VMware Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Apache License v2.0 which accompanies this distribution.
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Apache License v2.0 is available at
+ * http://www.opensource.org/licenses/apache2.0.php.
+ * You may elect to redistribute this code under either of these licenses.
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.gemini.web.internal;
+
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import org.eclipse.gemini.web.core.WebApplication;
+import org.eclipse.gemini.web.core.WebApplicationStartFailedException;
+
+
+
+final class WebApplicationStartFailureRetryController {
+
+ private final Object monitor = new Object();
+
+ private final ConcurrentMap<String, Set<StandardWebApplication>> failures = new ConcurrentHashMap<String, Set<StandardWebApplication>>();
+
+ void recordFailure(StandardWebApplication failedWebApplication) {
+ String contextPath = failedWebApplication.getContextPath();
+ if (contextPath != null) {
+ addFailureForWebContextPath(contextPath, failedWebApplication);
+ }
+ }
+
+ private void addFailureForWebContextPath(String contextPath, StandardWebApplication failedWebApplication) {
+ Set<StandardWebApplication> contextFailures = this.failures.get(contextPath);
+ if (contextFailures == null) {
+ contextFailures = new HashSet<StandardWebApplication>();
+ Set<StandardWebApplication> previousContextFailures = this.failures.putIfAbsent(contextPath, contextFailures);
+ if (previousContextFailures != null) {
+ contextFailures = previousContextFailures;
+ }
+ }
+ synchronized (this.monitor) {
+ contextFailures.add(failedWebApplication);
+ }
+ }
+
+ void retryFailures(StandardWebApplication stoppedWebApplication) {
+ String contextPath = stoppedWebApplication.getContextPath();
+ if (contextPath != null) {
+ Set<StandardWebApplication> contextFailures = removeFailuresForWebContextPath(contextPath);
+ contextFailures.remove(stoppedWebApplication);
+ for (WebApplication failedWebApplication : contextFailures) {
+ try {
+ failedWebApplication.start();
+ } catch (WebApplicationStartFailedException _) {
+ // ignore as the web application will have been added to the new contextFailures set
+ }
+ }
+ }
+ }
+
+ private Set<StandardWebApplication> removeFailuresForWebContextPath(String contextPath) {
+ Set<StandardWebApplication> sortedContextFailures = createSetSortedByBundleId();
+ Set<StandardWebApplication> contextFailures = this.failures.remove(contextPath);
+ if (contextFailures != null) {
+ sortedContextFailures.addAll(contextFailures);
+ }
+ return sortedContextFailures;
+
+ }
+
+ private Set<StandardWebApplication> createSetSortedByBundleId() {
+ return new TreeSet<StandardWebApplication>(new Comparator<StandardWebApplication>() {
+
+ public int compare(StandardWebApplication wa1, StandardWebApplication wa2) {
+ long id1 = wa1.getBundle().getBundleId();
+ long id2 = wa2.getBundle().getBundleId();
+ if (id1 < id2) {
+ return -1;
+ } else if (id1 > id2) {
+ return 1;
+ } else {
+ return 0;
+ }
+ }
+ });
+ }
+
+ void clear() {
+ this.failures.clear();
+ }
+
+}
diff --git a/org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/internal/WebContainerActivator.java b/org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/internal/WebContainerActivator.java
new file mode 100644
index 0000000..f53e524
--- /dev/null
+++ b/org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/internal/WebContainerActivator.java
@@ -0,0 +1,130 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 VMware Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Apache License v2.0 which accompanies this distribution.
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Apache License v2.0 is available at
+ * http://www.opensource.org/licenses/apache2.0.php.
+ * You may elect to redistribute this code under either of these licenses.
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.gemini.web.internal;
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.Map;
+
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.service.url.URLConstants;
+import org.osgi.service.url.URLStreamHandlerService;
+import org.osgi.util.tracker.ServiceTracker;
+import org.osgi.util.tracker.ServiceTrackerCustomizer;
+
+
+import org.eclipse.gemini.web.core.WebBundleManifestTransformer;
+import org.eclipse.gemini.web.core.WebContainer;
+import org.eclipse.gemini.web.core.spi.ServletContainer;
+import org.eclipse.gemini.web.internal.url.ChainingWebBundleManifestTransformer;
+import org.eclipse.gemini.web.internal.url.DefaultsWebBundleManifestTransformer;
+import org.eclipse.gemini.web.internal.url.SpecificationWebBundleManifestTransformer;
+import org.eclipse.gemini.web.internal.url.SystemBundleExportsImportingWebBundleManifestTransformer;
+import org.eclipse.gemini.web.internal.url.WebBundleUrl;
+import org.eclipse.gemini.web.internal.url.WebBundleUrlStreamHandlerService;
+import org.eclipse.virgo.util.osgi.ServiceRegistrationTracker;
+import org.eclipse.virgo.util.osgi.VersionRange;
+
+public class WebContainerActivator implements BundleActivator {
+
+ private final ServiceRegistrationTracker regTracker = new ServiceRegistrationTracker();
+
+ private volatile EventManager eventManager;
+
+ private ServiceTracker serviceTracker;
+
+ public void start(BundleContext context) throws Exception {
+ WebBundleManifestTransformer transformer = registerWebBundleManifestTransformer(context);
+
+ registerUrlStreamHandler(context, transformer);
+
+ this.eventManager = new EventManager(context);
+ this.eventManager.start();
+
+ this.serviceTracker = new ServiceTracker(context, ServletContainer.class.getName(), new ServletContainerTracker(context, this.eventManager));
+ this.serviceTracker.open();
+ }
+
+ public void stop(BundleContext context) throws Exception {
+ this.serviceTracker.close();
+ this.regTracker.unregisterAll();
+ this.eventManager.stop();
+ }
+
+ private WebBundleManifestTransformer registerWebBundleManifestTransformer(BundleContext context) {
+
+ WebBundleManifestTransformer specTransformer = new SpecificationWebBundleManifestTransformer();
+ WebBundleManifestTransformer defaultsTransformer = new DefaultsWebBundleManifestTransformer();
+ Map<String, VersionRange> systemBundleExports = new SystemBundleExportsResolver(context).getSystemBundleExports();
+ WebBundleManifestTransformer systemBundleExportImportingWebBundleManifestTransformer = new SystemBundleExportsImportingWebBundleManifestTransformer(systemBundleExports);
+
+ WebBundleManifestTransformer chainingTransformer = new ChainingWebBundleManifestTransformer(specTransformer, defaultsTransformer, systemBundleExportImportingWebBundleManifestTransformer);
+
+ ServiceRegistration reg = context.registerService(WebBundleManifestTransformer.class.getName(), chainingTransformer, null);
+ this.regTracker.track(reg);
+
+ return chainingTransformer;
+ }
+
+
+ private void registerUrlStreamHandler(BundleContext context, WebBundleManifestTransformer transformer) {
+ Dictionary<String, Object> props = new Hashtable<String, Object>();
+ props.put(URLConstants.URL_HANDLER_PROTOCOL, WebBundleUrl.SCHEME);
+
+ ServiceRegistration reg = context.registerService(URLStreamHandlerService.class.getName(), new WebBundleUrlStreamHandlerService(transformer), props);
+ this.regTracker.track(reg);
+ }
+
+ private static final class ServletContainerTracker implements ServiceTrackerCustomizer {
+
+ private final ServiceRegistrationTracker regTracker = new ServiceRegistrationTracker();
+
+ private final BundleContext context;
+
+ private final EventManager eventManager;
+
+ public ServletContainerTracker(BundleContext context, EventManager eventManager) {
+ this.context = context;
+ this.eventManager = eventManager;
+ }
+
+ public Object addingService(ServiceReference reference) {
+ ServletContainer container = (ServletContainer) this.context.getService(reference);
+
+ WebContainer webContainer = new StandardWebContainer(container, this.eventManager);
+
+ ServiceRegistration reg = context.registerService(WebContainer.class.getName(), webContainer, null);
+ this.regTracker.track(reg);
+
+ return webContainer;
+ }
+
+ public void modifiedService(ServiceReference reference, Object service) {
+ }
+
+ public void removedService(ServiceReference reference, Object service) {
+ this.regTracker.unregisterAll();
+ if (service instanceof WebContainer) {
+ ((WebContainer)service).halt();
+ }
+ }
+
+ }
+}
diff --git a/org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/internal/WebContainerUtils.java b/org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/internal/WebContainerUtils.java
new file mode 100644
index 0000000..f114467
--- /dev/null
+++ b/org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/internal/WebContainerUtils.java
@@ -0,0 +1,207 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 VMware Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Apache License v2.0 which accompanies this distribution.
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Apache License v2.0 is available at
+ * http://www.opensource.org/licenses/apache2.0.php.
+ * You may elect to redistribute this code under either of these licenses.
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.gemini.web.internal;
+
+import java.net.URL;
+import java.util.Locale;
+import java.util.Properties;
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.Constants;
+
+import org.eclipse.virgo.util.osgi.manifest.BundleManifest;
+
+public final class WebContainerUtils {
+
+ public static final String WEB_BUNDLE_SCHEME = "webbundle";
+
+ /**
+ * Constant for the <code>Web-ContextPath</code> manifest header.
+ */
+ public static final String HEADER_WEB_CONTEXT_PATH = "Web-ContextPath";
+
+ /**
+ * Constant for the <code>SpringSource-SystemPackages</code> manifest header.
+ */
+ public static final String HEADER_SPRINGSOURCE_DEFAULT_WAB_HEADERS = "SpringSource-DefaultWABHeaders";
+
+ /**
+ * Constant for the <code>Web-JSPExtractLocation</code> manifest header.
+ */
+ public static final String HEADER_WEB_JSP_EXTRACT_LOCATION = "Web-JSPExtractLocation";
+
+ static final String ENTRY_WEB_XML = "/WEB-INF/web.xml";
+
+ static final String WAR_EXTENSION = ".war";
+
+ static final String OSGI_WEB_VERSION = "osgi.web.version";
+
+ static final String OSGI_WEB_SYMBOLICNAME = "osgi.web.symbolicname";
+
+ static final String BUNDLE_VERSION_HEADER = "bundle-version";
+
+ private WebContainerUtils() {
+ }
+
+ public static boolean isWebBundle(Bundle bundle) {
+ return hasWarExtension(bundle) || hasWarScheme(bundle) || hasWebContextPath(bundle) || hasWebXml(bundle);
+ }
+
+ private static boolean hasWarExtension(Bundle bundle) {
+ String lowerCaseLocation = bundle.getLocation().toLowerCase(Locale.ENGLISH);
+ while (lowerCaseLocation.endsWith("/")) {
+ lowerCaseLocation = lowerCaseLocation.substring(0, lowerCaseLocation.length() - 1);
+ }
+ return lowerCaseLocation.endsWith(WAR_EXTENSION);
+ }
+
+ private static boolean hasWarScheme(Bundle bundle) {
+ return bundle.getLocation().startsWith(WEB_BUNDLE_SCHEME);
+ }
+
+ private static boolean hasWebContextPath(Bundle bundle) {
+ return getWebContextPathHeader(bundle) != null;
+ }
+
+ private static String getWebContextPathHeader(Bundle bundle) {
+ return (String) bundle.getHeaders().get(HEADER_WEB_CONTEXT_PATH);
+ }
+
+ private static boolean hasWebXml(Bundle bundle) {
+ return bundle.getEntry(ENTRY_WEB_XML) != null;
+ }
+
+ public static String getContextPath(Bundle bundle) {
+ String contextPath = getWebContextPathHeader(bundle);
+
+ if (contextPath == null) {
+ contextPath = getBaseName(bundle.getLocation());
+ }
+
+ return contextPath;
+ }
+
+ public static String createDefaultBundleSymbolicName(URL source) {
+ return getBaseName(source.getPath());
+ }
+
+ static String getBaseName(String path) {
+ String base = path;
+ base = unifySeparators(base);
+ if (base.endsWith("/")) {
+ base = base.substring(0, base.length() - 1);
+ }
+
+ base = stripQuery(base);
+ base = stripSchemeAndDrive(base);
+ base = stripLeadingPathElements(base);
+ base = stripExtension(base);
+ return base;
+ }
+
+ private static String unifySeparators(String base) {
+ return base.replaceAll("\\\\", "/");
+ }
+
+ private static String stripExtension(String base) {
+ int index;
+ index = base.lastIndexOf(".");
+ if (index > -1) {
+ base = base.substring(0, index);
+ }
+ return base;
+ }
+
+ private static String stripLeadingPathElements(String base) {
+ int index = base.lastIndexOf("/");
+ if (index > -1) {
+ base = base.substring(index + 1);
+ }
+ return base;
+ }
+
+ private static String stripQuery(String path) {
+ String result = path;
+ int index = result.lastIndexOf("?");
+ if (index > -1) {
+ result = result.substring(0, index);
+ }
+ return result;
+ }
+
+ private static String stripSchemeAndDrive(String path) {
+ String result = path;
+ int index = result.indexOf(":");
+ while (index > -1 && index < result.length()) {
+ result = result.substring(index + 1);
+ index = result.indexOf(":");
+ }
+ return result;
+ }
+
+ public static void setServletContextBundleProperties(Properties properties, Bundle bundle) {
+ setServletContextOsgiWebSymbolicNameProperty(properties, bundle);
+ setServletContextOsgiWebVersionProperty(properties, bundle);
+ }
+
+ private static void setServletContextOsgiWebVersionProperty(Properties properties, Bundle bundle) {
+ if (bundle.getHeaders().get(BUNDLE_VERSION_HEADER) != null) {
+ properties.setProperty(OSGI_WEB_VERSION, bundle.getVersion().toString());
+ }
+ }
+
+ private static void setServletContextOsgiWebSymbolicNameProperty(Properties properties, Bundle bundle) {
+ String symbolicName = bundle.getSymbolicName();
+ if (symbolicName != null) {
+ properties.setProperty(OSGI_WEB_SYMBOLICNAME, symbolicName);
+ }
+ }
+
+ /**
+ * Determines whether the given manifest represents a web application bundle. According to the R4.2 Enterprise
+ * Specification, this is true if and only if the manifest contains any of the headers in Table 128.3:
+ * Bundle-SymbolicName, Bundle-Version, Bundle-ManifestVersion, Import-Package, Web-ContextPath. Note: there is no
+ * need to validate the manifest as if it is invalid it will cause an error later.
+ *
+ * @param manifest the bundle manifest
+ * @return <code>true</code> if and only if the given manifest represents a web application bundle
+ */
+ public static boolean isWebApplicationBundle(BundleManifest manifest) {
+ return specifiesBundleSymbolicName(manifest) || specifiesBundleVersion(manifest) || specifiesBundleManifestVersion(manifest)
+ || specifiesImportPackage(manifest) || specifiesWebContextPath(manifest);
+ }
+
+ private static boolean specifiesBundleSymbolicName(BundleManifest manifest) {
+ return manifest.getBundleSymbolicName().getSymbolicName() != null;
+ }
+
+ private static boolean specifiesBundleVersion(BundleManifest manifest) {
+ return manifest.getHeader(Constants.BUNDLE_VERSION) != null;
+ }
+
+ private static boolean specifiesBundleManifestVersion(BundleManifest manifest) {
+ return manifest.getBundleManifestVersion() != 1;
+ }
+
+ private static boolean specifiesImportPackage(BundleManifest manifest) {
+ return !manifest.getImportPackage().getImportedPackages().isEmpty();
+ }
+
+ private static boolean specifiesWebContextPath(BundleManifest manifest) {
+ return manifest.getHeader(WebContainerUtils.HEADER_WEB_CONTEXT_PATH) != null;
+ }
+}
diff --git a/org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/internal/template/ServiceCallback.java b/org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/internal/template/ServiceCallback.java
new file mode 100644
index 0000000..97ff729
--- /dev/null
+++ b/org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/internal/template/ServiceCallback.java
@@ -0,0 +1,21 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 VMware Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Apache License v2.0 which accompanies this distribution.
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Apache License v2.0 is available at
+ * http://www.opensource.org/licenses/apache2.0.php.
+ * You may elect to redistribute this code under either of these licenses.
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.gemini.web.internal.template;
+
+public interface ServiceCallback<S, T> {
+ T doWithService(S service);
+}
diff --git a/org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/internal/template/ServiceTemplate.java b/org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/internal/template/ServiceTemplate.java
new file mode 100644
index 0000000..831ae29
--- /dev/null
+++ b/org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/internal/template/ServiceTemplate.java
@@ -0,0 +1,70 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 VMware Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Apache License v2.0 which accompanies this distribution.
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Apache License v2.0 is available at
+ * http://www.opensource.org/licenses/apache2.0.php.
+ * You may elect to redistribute this code under either of these licenses.
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.gemini.web.internal.template;
+
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.util.tracker.ServiceTracker;
+import org.osgi.util.tracker.ServiceTrackerCustomizer;
+
+
+public class ServiceTemplate<S> {
+
+ private final ServiceTracker tracker;
+
+ public ServiceTemplate(BundleContext context, Class<S> clazz) {
+ this.tracker = new ServiceTracker(context, clazz.getName(), new ServiceTemplateCustomizer(context));
+ }
+
+ public void start() {
+ this.tracker.open();
+ }
+
+ public void stop() {
+ this.tracker.close();
+ }
+
+ @SuppressWarnings("unchecked")
+ public <T> T executeWithService(ServiceCallback<S, T> callback) {
+ Object service = this.tracker.getService();
+ if(service != null) {
+ return callback.doWithService((S)service);
+ }
+ return null;
+ }
+
+ private static final class ServiceTemplateCustomizer implements ServiceTrackerCustomizer {
+
+ private final BundleContext context;
+
+ public ServiceTemplateCustomizer(BundleContext context) {
+ this.context = context;
+ }
+
+ public Object addingService(ServiceReference reference) {
+ return this.context.getService(reference);
+ }
+
+ public void modifiedService(ServiceReference reference, Object service) {
+ }
+
+ public void removedService(ServiceReference reference, Object service) {
+ this.context.ungetService(reference);
+ }
+
+ }
+}
diff --git a/org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/internal/template/ServiceUnavailableException.java b/org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/internal/template/ServiceUnavailableException.java
new file mode 100644
index 0000000..394c7b9
--- /dev/null
+++ b/org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/internal/template/ServiceUnavailableException.java
@@ -0,0 +1,42 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 VMware Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Apache License v2.0 which accompanies this distribution.
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Apache License v2.0 is available at
+ * http://www.opensource.org/licenses/apache2.0.php.
+ * You may elect to redistribute this code under either of these licenses.
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.gemini.web.internal.template;
+
+public class ServiceUnavailableException extends RuntimeException {
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = -4800943191504596869L;
+
+ public ServiceUnavailableException() {
+ super();
+ }
+
+ public ServiceUnavailableException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public ServiceUnavailableException(String message) {
+ super(message);
+ }
+
+ public ServiceUnavailableException(Throwable cause) {
+ super(cause);
+ }
+
+}
diff --git a/org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/internal/url/ChainingWebBundleManifestTransformer.java b/org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/internal/url/ChainingWebBundleManifestTransformer.java
new file mode 100644
index 0000000..6d0bc9b
--- /dev/null
+++ b/org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/internal/url/ChainingWebBundleManifestTransformer.java
@@ -0,0 +1,44 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 VMware Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Apache License v2.0 which accompanies this distribution.
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Apache License v2.0 is available at
+ * http://www.opensource.org/licenses/apache2.0.php.
+ * You may elect to redistribute this code under either of these licenses.
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.gemini.web.internal.url;
+
+import java.io.IOException;
+import java.net.URL;
+import java.util.Collections;
+
+import org.eclipse.gemini.web.core.InstallationOptions;
+import org.eclipse.gemini.web.core.WebBundleManifestTransformer;
+import org.eclipse.virgo.util.osgi.manifest.BundleManifest;
+
+
+public class ChainingWebBundleManifestTransformer implements WebBundleManifestTransformer {
+
+ private final WebBundleManifestTransformer[] manifestTransformers;
+
+ public ChainingWebBundleManifestTransformer(WebBundleManifestTransformer... transformers) {
+ this.manifestTransformers = transformers;
+ }
+
+ public void transform(BundleManifest manifest, URL sourceURL, InstallationOptions options, boolean webBundle) throws IOException {
+ if (options == null) {
+ options = new InstallationOptions(Collections.<String, String> emptyMap());
+ }
+ for (WebBundleManifestTransformer manifestTransformer : this.manifestTransformers) {
+ manifestTransformer.transform(manifest, sourceURL, options, webBundle);
+ }
+ }
+}
diff --git a/org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/internal/url/DefaultsWebBundleManifestTransformer.java b/org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/internal/url/DefaultsWebBundleManifestTransformer.java
new file mode 100644
index 0000000..edf06ac
--- /dev/null
+++ b/org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/internal/url/DefaultsWebBundleManifestTransformer.java
@@ -0,0 +1,108 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 VMware Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Apache License v2.0 which accompanies this distribution.
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Apache License v2.0 is available at
+ * http://www.opensource.org/licenses/apache2.0.php.
+ * You may elect to redistribute this code under either of these licenses.
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.gemini.web.internal.url;
+
+import java.io.IOException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.osgi.framework.Constants;
+import org.osgi.framework.Version;
+
+
+import org.eclipse.gemini.web.core.InstallationOptions;
+import org.eclipse.gemini.web.core.WebBundleManifestTransformer;
+import org.eclipse.gemini.web.internal.WebContainerUtils;
+import org.eclipse.virgo.util.osgi.manifest.BundleManifest;
+import org.eclipse.virgo.util.osgi.manifest.BundleSymbolicName;
+import org.eclipse.virgo.util.osgi.manifest.ImportedPackage;
+
+public final class DefaultsWebBundleManifestTransformer implements WebBundleManifestTransformer {
+
+ private static final int MINIMUM_BUNDLE_MANIFEST_VERSION = 2;
+
+ private static final String WEB_INF_CLASSES = "WEB-INF/classes";
+
+ public void transform(BundleManifest manifest, URL sourceURL, InstallationOptions options, boolean webBundle) throws IOException {
+ if (!webBundle || options.getDefaultWABHeaders()) {
+ applyDefaultBundleSymbolicName(sourceURL, manifest);
+ applyDefaultBundleManifestVersion(manifest);
+ applyBundleClassPath(sourceURL, manifest);
+ applyImportPackage(manifest);
+ }
+ }
+
+ private void applyImportPackage(BundleManifest manifest) {
+ addImportInNecessary("javax.servlet", new Version("2.5"), manifest);
+ addImportInNecessary("javax.servlet.http", new Version("2.5"), manifest);
+ addImportInNecessary("javax.servlet.jsp", new Version("2.1"), manifest);
+ addImportInNecessary("javax.servlet.jsp.el", new Version("2.1"), manifest);
+ addImportInNecessary("javax.servlet.jsp.tagext", new Version("2.1"), manifest);
+ addImportInNecessary("javax.el", new Version("1.0"), manifest);
+ }
+
+ private void addImportInNecessary(String packageName, Version version, BundleManifest manifest) {
+ List<ImportedPackage> pkgs = manifest.getImportPackage().getImportedPackages();
+ for (ImportedPackage pkg : pkgs) {
+ if (pkg.getPackageName().equals(packageName)) {
+ return;
+ }
+ }
+ ImportedPackage packageImport = manifest.getImportPackage().addImportedPackage(packageName);
+ packageImport.getAttributes().put(Constants.VERSION_ATTRIBUTE, version.toString());
+ }
+
+ private void applyBundleClassPath(URL source, BundleManifest manifest) throws IOException {
+ List<String> bundleClassPath = manifest.getBundleClasspath();
+
+ if (!bundleClassPath.contains(WEB_INF_CLASSES)) {
+ bundleClassPath.add(0, WEB_INF_CLASSES);
+ }
+
+ final List<String> entries = new ArrayList<String>();
+ WebBundleScanner scanner = new WebBundleScanner(source, new WebBundleScannerCallback() {
+
+ public void classFound(String entry) {
+ }
+
+ public void jarFound(String entry) {
+ entries.add(entry);
+ }
+ });
+ scanner.scanWar();
+
+ for (String entry : entries) {
+ if (!bundleClassPath.contains(entry)) {
+ bundleClassPath.add(entry);
+ }
+ }
+ }
+
+ private void applyDefaultBundleManifestVersion(BundleManifest manifest) {
+ if (manifest.getBundleManifestVersion() < MINIMUM_BUNDLE_MANIFEST_VERSION) {
+ manifest.setBundleManifestVersion(MINIMUM_BUNDLE_MANIFEST_VERSION);
+ }
+ }
+
+ private void applyDefaultBundleSymbolicName(URL source, BundleManifest manifest) {
+ BundleSymbolicName bsn = manifest.getBundleSymbolicName();
+ if (bsn.getSymbolicName() == null) {
+ bsn.setSymbolicName(WebContainerUtils.createDefaultBundleSymbolicName(source));
+ }
+ }
+}
diff --git a/org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/internal/url/PackageMergeUtils.java b/org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/internal/url/PackageMergeUtils.java
new file mode 100644
index 0000000..6093150
--- /dev/null
+++ b/org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/internal/url/PackageMergeUtils.java
@@ -0,0 +1,81 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 VMware Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Apache License v2.0 which accompanies this distribution.
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Apache License v2.0 is available at
+ * http://www.opensource.org/licenses/apache2.0.php.
+ * You may elect to redistribute this code under either of these licenses.
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.gemini.web.internal.url;
+
+import java.util.List;
+import java.util.Map;
+
+import org.osgi.framework.Constants;
+import org.osgi.framework.Version;
+
+import org.eclipse.virgo.util.osgi.manifest.BundleManifest;
+import org.eclipse.virgo.util.osgi.manifest.ExportedPackage;
+import org.eclipse.virgo.util.osgi.manifest.ImportedPackage;
+
+public class PackageMergeUtils {
+
+ public static void mergeImportPackage(BundleManifest manifest, String name, Map<String, String> attributes, Map<String, String> directives) {
+ ImportedPackage packageImport = findImportedPackage(manifest, name);
+
+ if (packageImport == null) {
+ packageImport = manifest.getImportPackage().addImportedPackage(name);
+ }
+
+ packageImport.getAttributes().clear();
+ packageImport.getAttributes().putAll(attributes);
+
+ packageImport.getDirectives().clear();
+ packageImport.getDirectives().putAll(directives);
+ }
+
+ public static void mergeExportPackage(BundleManifest manifest, String name, Map<String, String> attributes, Map<String, String> directives) {
+ String versionAttribute = attributes.get(Constants.VERSION_ATTRIBUTE);
+ Version version = (versionAttribute == null ? Version.emptyVersion : new Version(versionAttribute));
+
+ ExportedPackage packageExport = findExportedPackage(manifest, name, version);
+
+ if (packageExport == null) {
+ packageExport = manifest.getExportPackage().addExportedPackage(name);
+ }
+
+ packageExport.getAttributes().clear();
+ packageExport.getAttributes().putAll(attributes);
+
+ packageExport.getDirectives().clear();
+ packageExport.getDirectives().putAll(directives);
+ }
+
+ private static final ExportedPackage findExportedPackage(BundleManifest manifest, String packageName, Version version) {
+ List<ExportedPackage> exportedPackages = manifest.getExportPackage().getExportedPackages();
+ for (ExportedPackage exportedPackage : exportedPackages) {
+ if (packageName.equals(exportedPackage.getPackageName()) && version.equals(exportedPackage.getVersion())) {
+ return exportedPackage;
+ }
+ }
+ return null;
+ }
+
+ static final ImportedPackage findImportedPackage(BundleManifest manifest, String packageName) {
+ List<ImportedPackage> importedPackages = manifest.getImportPackage().getImportedPackages();
+ for (ImportedPackage importedPackage : importedPackages) {
+ if (packageName.equals(importedPackage.getPackageName())) {
+ return importedPackage;
+ }
+ }
+ return null;
+ }
+}
diff --git a/org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/internal/url/PackagesInWarScanner.java b/org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/internal/url/PackagesInWarScanner.java
new file mode 100644
index 0000000..9bcd17a
--- /dev/null
+++ b/org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/internal/url/PackagesInWarScanner.java
@@ -0,0 +1,46 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 VMware Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Apache License v2.0 which accompanies this distribution.
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Apache License v2.0 is available at
+ * http://www.opensource.org/licenses/apache2.0.php.
+ * You may elect to redistribute this code under either of these licenses.
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.gemini.web.internal.url;
+
+import java.io.IOException;
+import java.net.URL;
+import java.util.HashSet;
+import java.util.Set;
+
+class PackagesInWarScanner {
+
+ Set<String> getPackagesContainedInWar(URL warURL) throws IOException {
+ final Set<String> packagesInWar = new HashSet<String>();
+ if (warURL != null) {
+ WebBundleScanner scanner = new WebBundleScanner(warURL, new WebBundleScannerCallback() {
+
+ public void classFound(String entry) {
+ int lastSlashIndex = entry.lastIndexOf('/');
+ if (lastSlashIndex >= 0) {
+ packagesInWar.add(entry.substring(0, lastSlashIndex).replace('/', '.'));
+ }
+ }
+
+ public void jarFound(String entry) {
+ }
+ }, true);
+ scanner.scanWar();
+ }
+
+ return packagesInWar;
+ }
+}
diff --git a/org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/internal/url/Path.java b/org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/internal/url/Path.java
new file mode 100644
index 0000000..32f753c
--- /dev/null
+++ b/org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/internal/url/Path.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright SpringSource Inc 2010
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.eclipse.gemini.web.internal.url;
+
+import java.util.Arrays;
+
+final class Path {
+
+ private static final String BACK_SLASH = "\\";
+
+ private static final String PATH_SEPARATOR = "/";
+
+ private static final String DOUBLE_PATH_SEPARATOR = PATH_SEPARATOR + PATH_SEPARATOR;
+
+ private static final String PATH_HERE = ".";
+
+ private static final String PATH_UP = "..";
+
+ private final String[] baseComponents;
+
+ Path(String basePath) {
+ validatePath(basePath);
+ String[] comps = basePath.split(PATH_SEPARATOR);
+ while (comps.length > 0 && PATH_HERE.equals(comps[0])) {
+ String[] c2 = new String[comps.length - 1];
+ System.arraycopy(comps, 1, c2, 0, c2.length);
+ comps = c2;
+ }
+ this.baseComponents = comps;
+ }
+
+ private Path(String[] comps) {
+ this.baseComponents = comps;
+ }
+
+ private String head() {
+ if (!isEmpty()) {
+ return baseComponents[0];
+ } else {
+ throw new IllegalStateException("head not applicable to an empty path");
+ }
+ }
+
+ private Path tail() {
+ if (!isEmpty()) {
+ String[] c = new String[this.baseComponents.length - 1];
+ System.arraycopy(this.baseComponents, 1, c, 0, c.length);
+ return new Path(c);
+ } else {
+ throw new IllegalStateException("tail not applicable to an empty path");
+ }
+ }
+
+ private Path front() {
+ if (!isEmpty()) {
+ String[] c = new String[this.baseComponents.length - 1];
+ System.arraycopy(this.baseComponents, 0, c, 0, c.length);
+ return new Path(c);
+ } else {
+ throw new IllegalStateException("front not applicable to an empty path");
+ }
+ }
+
+ private static void validatePath(String basePath) {
+ if (basePath == null) {
+ throw new IllegalArgumentException("path must not be null");
+ }
+ if (basePath.contains(BACK_SLASH)) {
+ throw new IllegalArgumentException("path must not contain '" + BACK_SLASH + "'");
+ }
+ if (basePath.endsWith(PATH_SEPARATOR)) {
+ throw new IllegalArgumentException("path must not end in '" + PATH_SEPARATOR + "'");
+ }
+ if (basePath.contains(DOUBLE_PATH_SEPARATOR)) {
+ throw new IllegalArgumentException("path must not contain '" + DOUBLE_PATH_SEPARATOR + "'");
+ }
+ }
+
+ public Path() {
+ this.baseComponents = new String[0];
+ }
+
+ public Path applyRelativePath(Path relativePath) {
+ try {
+ Path b = this;
+ Path r = relativePath;
+ while (r.isUp()) {
+ r = r.tail();
+ b = b.front();
+ }
+ return b.append(r);
+ } catch (IllegalStateException s) {
+ throw new IllegalArgumentException("relative path cannot be applied", s);
+ }
+ }
+
+ private Path append(Path r) {
+ if (isEmpty()) {
+ return r;
+ } else if (r.isEmpty()) {
+ return this;
+ }
+ return new Path(toString() + PATH_SEPARATOR + r.toString());
+ }
+
+ private boolean isUp() {
+ return !isEmpty() && PATH_UP.equals(head());
+ }
+
+ private boolean isEmpty() {
+ return this.baseComponents.length == 0;
+ }
+
+ @Override
+ public String toString() {
+ StringBuffer s = new StringBuffer();
+ boolean first = true;
+ for (String c : baseComponents) {
+ if (!first) {
+ s.append(PATH_SEPARATOR);
+ }
+ first = false;
+ s.append(c);
+ }
+
+ return s.toString();
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + Arrays.hashCode(baseComponents);
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ Path other = (Path) obj;
+ if (!Arrays.equals(baseComponents, other.baseComponents))
+ return false;
+ return true;
+ }
+
+}
diff --git a/org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/internal/url/SpecificationWebBundleManifestTransformer.java b/org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/internal/url/SpecificationWebBundleManifestTransformer.java
new file mode 100644
index 0000000..e0729fc
--- /dev/null
+++ b/org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/internal/url/SpecificationWebBundleManifestTransformer.java
@@ -0,0 +1,218 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 VMware Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Apache License v2.0 which accompanies this distribution.
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Apache License v2.0 is available at
+ * http://www.opensource.org/licenses/apache2.0.php.
+ * You may elect to redistribute this code under either of these licenses.
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.gemini.web.internal.url;
+
+import java.io.IOException;
+import java.net.URL;
+import java.util.Collections;
+import java.util.List;
+
+import org.osgi.framework.Constants;
+import org.osgi.framework.Version;
+
+
+import org.eclipse.gemini.web.core.InstallationOptions;
+import org.eclipse.gemini.web.core.WebBundleManifestTransformer;
+import org.eclipse.gemini.web.internal.WebContainerUtils;
+import org.eclipse.virgo.util.osgi.manifest.BundleManifest;
+import org.eclipse.virgo.util.osgi.manifest.parse.HeaderDeclaration;
+import org.eclipse.virgo.util.osgi.manifest.parse.HeaderParser;
+import org.eclipse.virgo.util.osgi.manifest.parse.HeaderParserFactory;
+import org.eclipse.virgo.util.osgi.manifest.parse.ParserLogger;
+
+/**
+ * Applies user installation options onto a {@link BundleManifest}.
+ *
+ *
+ */
+public final class SpecificationWebBundleManifestTransformer implements WebBundleManifestTransformer {
+
+ private static final int MINIMUM_VALID_BUNDLE_MANIFEST_VERSION = 2;
+
+ public void transform(BundleManifest manifest, URL sourceURL, InstallationOptions options, boolean webBundle) throws IOException {
+ if (options == null) {
+ options = new InstallationOptions(Collections.<String, String> emptyMap());
+ }
+
+ transformBundleSymbolicName(manifest, options, webBundle);
+ transformBundleVersion(manifest, options, webBundle);
+ transformBundleManifestVersion(manifest, options, webBundle);
+ transformBundleClassPath(manifest, options, webBundle);
+ transformImportPackage(manifest, options, webBundle);
+ transformExportPackage(manifest, options, webBundle);
+ transformWebContextPath(manifest, options, webBundle);
+ }
+
+ private void transformExportPackage(BundleManifest manifest, InstallationOptions options, boolean isWebApplicationBundle) {
+ String epd = options.getExportPackageDeclaration();
+ if (epd != null) {
+ if (isWebApplicationBundle) {
+ throw new IllegalArgumentException("Export-Package URL parameter cannot modify a Web Application Bundle");
+ }
+ HeaderParser parser = HeaderParserFactory.newHeaderParser(new TransformerParserLogger());
+ List<HeaderDeclaration> packageHeader = parser.parsePackageHeader(epd, Constants.EXPORT_PACKAGE);
+ for (HeaderDeclaration headerDeclaration : packageHeader) {
+ for (String name : headerDeclaration.getNames()) {
+ PackageMergeUtils.mergeExportPackage(manifest, name, headerDeclaration.getAttributes(), headerDeclaration.getDirectives());
+ }
+ }
+ }
+ }
+
+ private void transformImportPackage(BundleManifest manifest, InstallationOptions options, boolean isWebApplicationBundle) {
+ String ipd = options.getImportPackageDeclaration();
+ if (ipd != null) {
+ if (isWebApplicationBundle) {
+ throw new IllegalArgumentException("Import-Package URL parameter cannot modify a Web Application Bundle");
+ }
+ HeaderParser parser = HeaderParserFactory.newHeaderParser(new TransformerParserLogger());
+ List<HeaderDeclaration> packageHeader = parser.parsePackageHeader(ipd, Constants.IMPORT_PACKAGE);
+ for (HeaderDeclaration headerDeclaration : packageHeader) {
+ for (String name : headerDeclaration.getNames()) {
+ PackageMergeUtils.mergeImportPackage(manifest, name, headerDeclaration.getAttributes(), headerDeclaration.getDirectives());
+ }
+ }
+ }
+ }
+
+ private void transformBundleSymbolicName(BundleManifest manifest, InstallationOptions options, boolean isWebApplicationBundle) {
+ if (options.getBundleSymbolicName() != null) {
+ if (isWebApplicationBundle) {
+ throw new IllegalArgumentException("Bundle-SymbolicName URL parameter cannot modify a Web Application Bundle");
+ }
+ manifest.getBundleSymbolicName().setSymbolicName(options.getBundleSymbolicName());
+ }
+ }
+
+ private void transformBundleManifestVersion(BundleManifest manifest, InstallationOptions options, boolean isWebApplicationBundle) {
+ if (options.getBundleManifestVersion() != null) {
+ if (isWebApplicationBundle) {
+ throw new IllegalArgumentException("Bundle-ManifestVersion URL parameter cannot modify a Web Application Bundle");
+ }
+ manifest.setBundleManifestVersion(parseBundleManifestVersion(options.getBundleManifestVersion()));
+ }
+ }
+
+ private int parseBundleManifestVersion(String bundleManifestVersion) {
+ int result = MINIMUM_VALID_BUNDLE_MANIFEST_VERSION;
+ if (bundleManifestVersion != null) {
+ try {
+ result = Integer.parseInt(bundleManifestVersion);
+ if (result < MINIMUM_VALID_BUNDLE_MANIFEST_VERSION) {
+ throw new IllegalArgumentException(Constants.BUNDLE_MANIFESTVERSION + " " + result + " is less than the smallest valid value of "
+ + MINIMUM_VALID_BUNDLE_MANIFEST_VERSION);
+ }
+ } catch (NumberFormatException ex) {
+ throw new IllegalArgumentException(Constants.BUNDLE_MANIFESTVERSION + " is not a valid integer.", ex);
+ }
+ }
+ return result;
+ }
+
+ private void transformBundleVersion(BundleManifest manifest, InstallationOptions options, boolean isWebApplicationBundle) {
+ if (options.getBundleVersion() != null) {
+ if (isWebApplicationBundle) {
+ throw new IllegalArgumentException("Bundle-Version URL parameter cannot modify a Web Application Bundle");
+ }
+ manifest.setBundleVersion(new Version(options.getBundleVersion()));
+ }
+ }
+
+ private void transformBundleClassPath(BundleManifest manifest, InstallationOptions options, boolean isWebApplicationBundle) {
+ List<String> bundleClassPath = manifest.getBundleClasspath();
+ String bundleClassPathOption = options.getBundleClassPath();
+ if (bundleClassPathOption != null) {
+ if (isWebApplicationBundle) {
+ throw new IllegalArgumentException("Bundle-ClassPath URL parameter cannot modify a Web Application Bundle");
+ }
+ for (String entry : parseBundleClassPath(bundleClassPathOption)) {
+ if (!bundleClassPath.contains(entry)) {
+ bundleClassPath.add(entry);
+ }
+ }
+ }
+ }
+
+ private static String[] parseBundleClassPath(String bundleClassPath) {
+ String[] bundleClassPathEntries = bundleClassPath.split(",");
+ minimallyValidateBundleClassPathEntries(bundleClassPathEntries, bundleClassPath);
+ return bundleClassPathEntries;
+ }
+
+ /**
+ * Validates the given bundle class path entries.
+ * <p>
+ * Trailing slashes are tolerated and removed so the resultant class path entries are more likely to conform
+ * strictly to the OSGi specification.
+ */
+ private static void minimallyValidateBundleClassPathEntries(String[] bundleClassPathEntries, String bundleClassPath) {
+ for (int i = 0; i < bundleClassPathEntries.length; i++) {
+ String entry = bundleClassPathEntries[i];
+ if (entry.length() == 0) {
+ diagnoseInvalidEntry(entry, bundleClassPath);
+ }
+ if (entry.endsWith("/")) {
+ if (entry.length() == 1) {
+ diagnoseInvalidEntry(entry, bundleClassPath);
+ }
+ bundleClassPathEntries[i] = entry.substring(0, entry.length() - 1);
+ }
+ }
+ }
+
+ private static void diagnoseInvalidEntry(String entry, String bundleClassPath) {
+ throw new IllegalArgumentException(Constants.BUNDLE_CLASSPATH + "'" + bundleClassPath + "' contains an invalid entry '" + entry + "'");
+ }
+
+ /**
+ * @param isWebApplicationBundle
+ */
+ private void transformWebContextPath(BundleManifest manifest, InstallationOptions options, boolean isWebApplicationBundle) {
+ String webContextPathOption = options.getWebContextPath();
+ if (webContextPathOption != null) {
+ manifest.setHeader(WebContainerUtils.HEADER_WEB_CONTEXT_PATH, validateWebContextPath(webContextPathOption));
+ } else if (!options.getDefaultWABHeaders()) {
+ String webContextPathHeader = manifest.getHeader(WebContainerUtils.HEADER_WEB_CONTEXT_PATH);
+ if (webContextPathHeader == null || webContextPathHeader.trim().length() == 0) {
+ throw new IllegalArgumentException(WebContainerUtils.HEADER_WEB_CONTEXT_PATH + " is missing");
+ }
+ }
+ }
+
+ private String validateWebContextPath(String webContextPathOption) {
+ String trimmedWebContextPathOption = webContextPathOption.trim();
+ if (trimmedWebContextPathOption.length() == 0) {
+ throw new IllegalArgumentException(WebContainerUtils.HEADER_WEB_CONTEXT_PATH + " URL parameter value is missing");
+ }
+ if (trimmedWebContextPathOption.startsWith("/")) {
+ return trimmedWebContextPathOption;
+ } else {
+ return "/" + trimmedWebContextPathOption;
+ }
+ }
+
+ private static class TransformerParserLogger implements ParserLogger {
+
+ public String[] errorReports() {
+ return null;
+ }
+
+ public void outputErrorMsg(Exception re, String item) {
+
+ }
+ }
+}
diff --git a/org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/internal/url/SystemBundleExportsImportingWebBundleManifestTransformer.java b/org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/internal/url/SystemBundleExportsImportingWebBundleManifestTransformer.java
new file mode 100644
index 0000000..6a56bfb
--- /dev/null
+++ b/org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/internal/url/SystemBundleExportsImportingWebBundleManifestTransformer.java
@@ -0,0 +1,55 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 VMware Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Apache License v2.0 which accompanies this distribution.
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Apache License v2.0 is available at
+ * http://www.opensource.org/licenses/apache2.0.php.
+ * You may elect to redistribute this code under either of these licenses.
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.gemini.web.internal.url;
+
+import java.io.IOException;
+import java.net.URL;
+import java.util.Map;
+import java.util.Set;
+import java.util.Map.Entry;
+
+import org.eclipse.gemini.web.core.InstallationOptions;
+import org.eclipse.gemini.web.core.WebBundleManifestTransformer;
+import org.eclipse.virgo.util.osgi.VersionRange;
+import org.eclipse.virgo.util.osgi.manifest.BundleManifest;
+
+
+public final class SystemBundleExportsImportingWebBundleManifestTransformer implements WebBundleManifestTransformer {
+
+ private final Map<String, VersionRange> systemBundleExports;
+
+ private final PackagesInWarScanner warPackagesScanner = new PackagesInWarScanner();
+
+ public SystemBundleExportsImportingWebBundleManifestTransformer(Map<String, VersionRange> systemBundleExports) {
+ this.systemBundleExports = systemBundleExports;
+ }
+
+ public void transform(BundleManifest manifest, URL sourceURL, InstallationOptions options, boolean webBundle) throws IOException {
+ if (!webBundle || options.getDefaultWABHeaders()) {
+ addImportsForSystemBundleExports(manifest, this.warPackagesScanner.getPackagesContainedInWar(sourceURL));
+ }
+ }
+
+ protected void addImportsForSystemBundleExports(BundleManifest bundleManifest, Set<String> packagesInWar) {
+ for (Entry<String, VersionRange> exportedPackage : this.systemBundleExports.entrySet()) {
+ String packageName = exportedPackage.getKey();
+ if (!packagesInWar.contains(packageName) && PackageMergeUtils.findImportedPackage(bundleManifest, packageName) == null) {
+ bundleManifest.getImportPackage().addImportedPackage(packageName).setVersion(exportedPackage.getValue());
+ }
+ }
+ }
+}
diff --git a/org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/internal/url/WebBundleScanner.java b/org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/internal/url/WebBundleScanner.java
new file mode 100644
index 0000000..bbdb962
--- /dev/null
+++ b/org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/internal/url/WebBundleScanner.java
@@ -0,0 +1,298 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 VMware Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Apache License v2.0 which accompanies this distribution.
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Apache License v2.0 is available at
+ * http://www.opensource.org/licenses/apache2.0.php.
+ * You may elect to redistribute this code under either of these licenses.
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.gemini.web.internal.url;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.jar.Attributes;
+import java.util.jar.JarEntry;
+import java.util.jar.JarInputStream;
+import java.util.jar.Manifest;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.eclipse.virgo.util.io.IOUtils;
+
+final class WebBundleScanner {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(WebBundleScanner.class);
+
+ private static final String LIB_DIR_SUFFIX = File.separator + "WEB-INF" + File.separator + "lib";
+
+ private static final String CLASSES_DIR_SUFFIX = File.separator + "WEB-INF" + File.separator + "classes";
+
+ private static final String FILE_SCHEME = "file";
+
+ private static final String JAR_SUFFIX = ".jar";
+
+ private static final String CLASS_SUFFIX = ".class";
+
+ private static final String LIB_ENTRY_PREFIX = "WEB-INF/lib/";
+
+ private static final String CLASSES_ENTRY_PREFIX = "WEB-INF/classes/";
+
+ private static final String CLASS_PATH_ATTRIBUTE_NAME = "Class-Path";
+
+ private static final String CLASS_PATH_SEPARATOR = " ";
+
+ private final Object monitor = new Object();
+
+ private final URL source;
+
+ private final WebBundleScannerCallback callBack;
+
+ private final boolean findClassesInNestedJars;
+
+ private final Set<String> scannedJars = new HashSet<String>();
+
+ WebBundleScanner(URL source, WebBundleScannerCallback callBack) {
+ this(source, callBack, false);
+ }
+
+ /**
+ * Creates a WebBundleScanner for a given WAR with a given callBack.
+ *
+ * @param source the WAR content
+ * @param callBack The callBack to notify of entries in the WAR
+ * @param findClassesInNestedJars
+ */
+ WebBundleScanner(URL source, WebBundleScannerCallback callBack, boolean findClassesInNestedJars) {
+ this.source = source;
+ this.callBack = callBack;
+ this.findClassesInNestedJars = findClassesInNestedJars;
+ }
+
+ /**
+ * Scans the WAR content from <code>source</code>, notifying the callBack of .class entries in WEB-INF/classes and
+ * its sub-directories, and .jar entries in WEB-INF/lib.
+ *
+ * @throws IOException if the WAR cannot be scanned
+ */
+ void scanWar() throws IOException {
+ synchronized (this.monitor) {
+ this.scannedJars.clear();
+ if (isDirectory()) {
+ scanWarDirectory();
+ } else {
+ scanWarFile();
+ }
+ }
+ }
+
+ private void scanWarDirectory() throws IOException {
+ try {
+ File bundleDir = sourceAsFile();
+ File libDir = new File(bundleDir, LIB_DIR_SUFFIX);
+ if (libDir.isDirectory()) {
+ doScanLibDirectory(libDir);
+ }
+ File classesDir = new File(bundleDir, CLASSES_DIR_SUFFIX);
+ if (classesDir.isDirectory()) {
+ doScanClassesDirectory(classesDir);
+ }
+ } catch (URISyntaxException e) {
+ throw new IllegalStateException("Unexpected URISyntaxException.", e);
+ }
+ }
+
+ private void doScanLibDirectory(File libDir) throws IOException {
+ File[] files = libDir.listFiles();
+ if (files != null) {
+ for (File file : files) {
+ if (file.isFile() && file.getName().endsWith(JAR_SUFFIX)) {
+ String pathToJar = LIB_ENTRY_PREFIX + file.getName();
+ if (driveCallBackIfNewJarFound(pathToJar)) {
+ doScanNestedJar(file);
+ }
+ }
+ }
+ }
+ }
+
+ private boolean driveCallBackIfNewJarFound(String pathToJar) {
+ // Prevent infinite recursion.
+ if (this.scannedJars.contains(pathToJar)) {
+ return false;
+ }
+ this.scannedJars.add(pathToJar);
+ this.callBack.jarFound(pathToJar);
+ return true;
+ }
+
+ private void doScanNestedJar(File file) throws IOException {
+ JarInputStream jis = null;
+
+ try {
+ jis = new JarInputStream(new FileInputStream(file));
+ doScanNestedJar(file.getAbsolutePath(), jis);
+ } finally {
+ if (jis != null) {
+ IOUtils.closeQuietly(jis);
+ }
+ }
+ }
+
+ private void doScanClassesDirectory(File classesDir) {
+ File[] files = classesDir.listFiles();
+ if (files != null) {
+ for (File file : files) {
+ if (file.isDirectory()) {
+ doScanClassesDirectory(file);
+ } else if (file.isFile() && file.getName().endsWith(CLASS_SUFFIX)) {
+ String path = normalizePath(file.getPath());
+ this.callBack.classFound(path.substring(path.lastIndexOf(CLASSES_ENTRY_PREFIX) + CLASSES_ENTRY_PREFIX.length()));
+ }
+ }
+ }
+ }
+
+ private void scanWarFile() throws IOException {
+ JarInputStream jis = new JarInputStream(this.source.openStream());
+ try {
+ JarEntry entry;
+ while ((entry = jis.getNextJarEntry()) != null) {
+ String entryName = entry.getName();
+ if (entryName.startsWith(LIB_ENTRY_PREFIX) && entryName.endsWith(JAR_SUFFIX)) {
+ if (driveCallBackIfNewJarFound(entryName)) {
+ JarInputStream nestedJis = new JarInputStream(jis);
+ doScanNestedJar(entryName, nestedJis);
+ }
+ } else if (entryName.startsWith(CLASSES_ENTRY_PREFIX) && entryName.endsWith(CLASS_SUFFIX)) {
+ this.callBack.classFound(entry.getName().substring(CLASSES_ENTRY_PREFIX.length()));
+ }
+ }
+ } finally {
+ IOUtils.closeQuietly(jis);
+ }
+ }
+
+ private void doScanNestedJar(String jarEntryName, JarInputStream jis) throws IOException {
+ Manifest manifest = jis.getManifest();
+ if (manifest != null) {
+ Attributes mainAttributes = manifest.getMainAttributes();
+ if (mainAttributes != null) {
+ String classPath = mainAttributes.getValue(CLASS_PATH_ATTRIBUTE_NAME);
+ if (classPath != null) {
+ Path jarPathx = getNormalisedDirectoryPath(jarEntryName);
+
+ String[] classPathItems = classPath.split(CLASS_PATH_SEPARATOR);
+ for (String classPathItem : classPathItems) {
+ try {
+ Path entryPath = jarPathx.applyRelativePath(new Path(classPathItem));
+ scanNestedJarInWar(entryPath.toString());
+ } catch (IllegalArgumentException _) {
+ // skip invalid relative paths which try to escape the WAR
+ }
+ }
+ }
+ }
+ }
+
+ JarEntry entry;
+ while ((entry = jis.getNextJarEntry()) != null) {
+ String entryName = entry.getName();
+ if (entryName.endsWith(CLASS_SUFFIX)) {
+ notifyClassFound(entryName);
+ }
+ }
+ }
+
+ private static Path getNormalisedDirectoryPath(String jarEntryName) {
+ String jarPath = normalizePath(jarEntryName);
+ int lastDirectoryIndex = jarPath.lastIndexOf("/");
+ return lastDirectoryIndex == -1 ? new Path() : new Path(jarPath.substring(0, lastDirectoryIndex));
+ }
+
+ private void scanNestedJarInWar(String jarPath) throws IOException {
+ if (isDirectory()) {
+ scanNestedJarInWarDirectory(jarPath);
+ } else {
+ scanNestedJarInWarFile(jarPath);
+ }
+ }
+
+ private void scanNestedJarInWarDirectory(String jarPath) throws IOException {
+ try {
+ File bundleDir = sourceAsFile();
+ File nestedJar = new File(bundleDir, "/" + jarPath);
+ if (nestedJar.isFile()) {
+ String pathToJar = "/" + nestedJar.getName();
+ if (driveCallBackIfNewJarFound(pathToJar)) {
+ doScanNestedJar(nestedJar);
+ }
+ }
+ } catch (URISyntaxException e) {
+ throw new IllegalStateException("Unexpected URISyntaxException.", e);
+ }
+ }
+
+ private void scanNestedJarInWarFile(String jarPath) throws IOException {
+ JarInputStream jis = new JarInputStream(this.source.openStream());
+ try {
+ JarEntry entry;
+ while ((entry = jis.getNextJarEntry()) != null) {
+ String entryName = entry.getName();
+ if (jarPath.endsWith(entryName)) {
+ if (driveCallBackIfNewJarFound(entryName)) {
+ JarInputStream nestedJis = new JarInputStream(jis);
+ doScanNestedJar(entryName, nestedJis);
+ }
+ }
+ }
+ } finally {
+ IOUtils.closeQuietly(jis);
+ }
+
+ }
+
+ private void notifyClassFound(String entryName) {
+ if (this.findClassesInNestedJars) {
+ this.callBack.classFound(entryName);
+ }
+ }
+
+ private boolean isDirectory() {
+ if (FILE_SCHEME.equals(this.source.getProtocol())) {
+ try {
+ return sourceAsFile().isDirectory();
+ } catch (URISyntaxException e) {
+ LOGGER.warn("Unable to determine if bundle '" + this.source + "'is a directory.", e);
+ }
+ }
+ return false;
+ }
+
+ private File sourceAsFile() throws URISyntaxException {
+ URI uri = this.source.toURI();
+ if (uri.isOpaque()) {
+ return new File(uri.getSchemeSpecificPart());
+ } else {
+ return new File(uri);
+ }
+ }
+
+ private static String normalizePath(String path) {
+ return path.replace('\\', '/');
+ }
+}
diff --git a/org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/internal/url/WebBundleScannerCallback.java b/org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/internal/url/WebBundleScannerCallback.java
new file mode 100644
index 0000000..d0eb225
--- /dev/null
+++ b/org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/internal/url/WebBundleScannerCallback.java
@@ -0,0 +1,24 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 VMware Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Apache License v2.0 which accompanies this distribution.
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Apache License v2.0 is available at
+ * http://www.opensource.org/licenses/apache2.0.php.
+ * You may elect to redistribute this code under either of these licenses.
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.gemini.web.internal.url;
+
+interface WebBundleScannerCallback {
+
+ void jarFound(String pathToJar);
+
+ void classFound(String className);
+}
diff --git a/org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/internal/url/WebBundleUrl.java b/org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/internal/url/WebBundleUrl.java
new file mode 100644
index 0000000..3145c61
--- /dev/null
+++ b/org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/internal/url/WebBundleUrl.java
@@ -0,0 +1,130 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 VMware Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Apache License v2.0 which accompanies this distribution.
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Apache License v2.0 is available at
+ * http://www.opensource.org/licenses/apache2.0.php.
+ * You may elect to redistribute this code under either of these licenses.
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.gemini.web.internal.url;
+
+import static java.util.Collections.unmodifiableMap;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLStreamHandler;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.gemini.web.internal.WebContainerUtils;
+
+
+/**
+ * Encapsulates the state of a <code>war:</code> URL.
+ */
+public class WebBundleUrl {
+
+ public static final String SCHEME = WebContainerUtils.WEB_BUNDLE_SCHEME;
+
+ public final Object monitor = new Object();
+
+ private final URL url;
+
+ private final String location;
+
+ private Map<String, String> options;
+
+ public WebBundleUrl(String location, Map<String, String> options) throws MalformedURLException {
+ this.url = createURL(location, options);
+ this.location = location;
+ this.options = (options == null ? Collections.<String, String> emptyMap() : unmodifiableMap(new HashMap<String, String>(options)));
+ }
+
+ public WebBundleUrl(URL url) {
+ String protocol = url.getProtocol();
+ if (!SCHEME.equals(protocol)) {
+ throw new IllegalArgumentException("URL '" + url + "' is not a valid WAR URL");
+ }
+ this.url = url;
+ this.location = url.getPath();
+ }
+
+ public final String getLocation() {
+ return this.location;
+ }
+
+ /**
+ * Gets the query options from the URL. Note that validation is deferred until this point as doing it earlier does
+ * not produce the necessary BundleException when driven under {@link org.osgi.framework.BundleContext#installBundle(String) installBundle(String)}.
+ * @return the options in a String->String map
+ */
+ public final Map<String, String> getOptions() {
+ synchronized (this.monitor) {
+ if (this.options == null) {
+ this.options = parseQueryString(url.getQuery());
+ }
+ }
+ return this.options;
+ }
+
+ private String toPathString(String location, Map<?, ?> options) {
+ StringBuilder sb = new StringBuilder();
+ sb.append(location);
+ appendQueryStringIfNecessary(options, sb);
+ return sb.toString();
+ }
+
+ public final String toString() {
+ return this.url.toString();
+ }
+
+ public final URL toURL() {
+ return this.url;
+ }
+
+ /**
+ * Hook method to use in unit testing. Allows for a specific {@link URLStreamHandler} to be added to {@link URL}
+ * instances created internally.
+ */
+ protected URLStreamHandler createURLStreamHandler() {
+ return null;
+ }
+
+ private URL createURL(String location, Map<?, ?> options) throws MalformedURLException {
+ return new URL(SCHEME, null, -1, toPathString(location, options), createURLStreamHandler());
+ }
+
+ private static Map<String, String> parseQueryString(String query) {
+ Map<String, String> options = new HashMap<String, String>();
+ if (query != null) {
+ String[] parms = query.split("&");
+ for (String parm : parms) {
+ int equals = parm.indexOf("=");
+ if (equals == -1) {
+ throw new IllegalArgumentException("Missing '=' in URL parameter '" + parm + "'");
+ }
+ options.put(parm.substring(0, equals), parm.substring(equals + 1));
+ }
+ }
+ return unmodifiableMap(options);
+ }
+
+ private static void appendQueryStringIfNecessary(Map<?, ?> options, StringBuilder sb) {
+ if (options != null && !options.isEmpty()) {
+ sb.append("?");
+ for (Map.Entry<?, ?> entry : options.entrySet()) {
+ sb.append(entry.getKey()).append("=").append(entry.getValue()).append("&");
+ }
+ sb.deleteCharAt(sb.length() - 1);
+ }
+ }
+}
diff --git a/org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/internal/url/WebBundleUrlStreamHandlerService.java b/org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/internal/url/WebBundleUrlStreamHandlerService.java
new file mode 100644
index 0000000..4743c7e
--- /dev/null
+++ b/org.eclipse.gemini.web.core/src/main/java/org/eclipse/gemini/web/internal/url/WebBundleUrlStreamHandlerService.java
@@ -0,0 +1,134 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 VMware Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Apache License v2.0 which accompanies this distribution.
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Apache License v2.0 is available at
+ * http://www.opensource.org/licenses/apache2.0.php.
+ * You may elect to redistribute this code under either of these licenses.
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.gemini.web.internal.url;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.jar.Attributes;
+import java.util.jar.JarFile;
+import java.util.jar.JarOutputStream;
+import java.util.jar.Manifest;
+import java.util.zip.ZipEntry;
+
+import org.osgi.service.url.AbstractURLStreamHandlerService;
+import org.osgi.service.url.URLStreamHandlerService;
+
+
+import org.eclipse.gemini.web.core.InstallationOptions;
+import org.eclipse.gemini.web.core.WebBundleManifestTransformer;
+import org.eclipse.gemini.web.internal.WebContainerUtils;
+import org.eclipse.virgo.util.io.JarTransformer;
+import org.eclipse.virgo.util.io.JarTransformingURLConnection;
+import org.eclipse.virgo.util.io.JarTransformer.JarTransformerCallback;
+import org.eclipse.virgo.util.osgi.manifest.BundleManifest;
+import org.eclipse.virgo.util.osgi.manifest.BundleManifestFactory;
+
+/**
+ * {@link URLStreamHandlerService} that transforms bundles installed with the <code>war:</code> protocol.
+ * <p/>
+ * Transformations are applied using the {@link WebBundleManifestTransformer}.
+ *
+ * @see WebBundleManifestTransformer
+ */
+public final class WebBundleUrlStreamHandlerService extends AbstractURLStreamHandlerService {
+
+ private final WebBundleManifestTransformer transformer;
+
+ public WebBundleUrlStreamHandlerService(WebBundleManifestTransformer transformer) {
+ this.transformer = transformer;
+ }
+
+ @Override
+ public URLConnection openConnection(URL u) throws IOException {
+ WebBundleUrl url = new WebBundleUrl(u);
+ URL actualUrl = new URL(url.getLocation());
+
+ JarTransformer jarTransformer = new JarTransformer(new Callback(actualUrl, url, this.transformer));
+ return new JarTransformingURLConnection(actualUrl, jarTransformer, true);
+ }
+
+ private static final class Callback implements JarTransformerCallback {
+
+ private final WebBundleManifestTransformer transformer;
+
+ private final URL sourceURL;
+
+ private final WebBundleUrl webBundleUrl;
+
+ public Callback(URL sourceURL, WebBundleUrl url, WebBundleManifestTransformer transformer) {
+ this.sourceURL = sourceURL;
+ this.webBundleUrl = url;
+ this.transformer = transformer;
+ }
+
+ public boolean transformEntry(String entryName, InputStream is, JarOutputStream jos) throws IOException {
+ if (JarFile.MANIFEST_NAME.equals(entryName)) {
+ jos.putNextEntry(new ZipEntry(entryName));
+ InputStreamReader reader = new InputStreamReader(is);
+ BundleManifest manifest = BundleManifestFactory.createBundleManifest(reader);
+ InstallationOptions options = new InstallationOptions(this.webBundleUrl.getOptions());
+ if (manifest.getHeader(WebContainerUtils.HEADER_SPRINGSOURCE_DEFAULT_WAB_HEADERS) != null) {
+ options.setDefaultWABHeaders(true);
+ }
+
+ boolean webBundle = WebContainerUtils.isWebApplicationBundle(manifest);
+ this.transformer.transform(manifest, sourceURL, options, webBundle);
+
+ toManifest(manifest.toDictionary()).write(jos);
+ jos.closeEntry();
+ return true;
+ }
+
+ // Delete signature files. Should be generalised into another transformer type.
+ return isSignatureFile(entryName);
+ }
+
+ private boolean isSignatureFile(String entryName) {
+ String[] entryNameComponents = entryName.split("/");
+ if (entryNameComponents.length == 2) {
+ if ("META-INF".equals(entryNameComponents[0])) {
+ String entryFileName = entryNameComponents[1];
+ if (entryFileName.endsWith(".SF") || entryFileName.endsWith(".DSA") || entryFileName.endsWith(".RSA")) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ private static Manifest toManifest(Dictionary<String, String> headers) {
+ Manifest manifest = new Manifest();
+ Attributes attributes = manifest.getMainAttributes();
+ Enumeration<String> names = headers.keys();
+
+ while (names.hasMoreElements()) {
+ String name = names.nextElement();
+ String value = headers.get(name);
+
+ attributes.putValue(name, value);
+ }
+ return manifest;
+ }
+
+ }
+
+}
diff --git a/org.eclipse.gemini.web.core/src/main/resources/META-INF/MANIFEST.MF b/org.eclipse.gemini.web.core/src/main/resources/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..fb73801
--- /dev/null
+++ b/org.eclipse.gemini.web.core/src/main/resources/META-INF/MANIFEST.MF
@@ -0,0 +1,25 @@
+Manifest-Version: 1
+Export-Package: org.eclipse.gemini.web.core;version="1.0";uses:="javax
+ .servlet,org.eclipse.virgo.util.osgi.manifest,org.osgi.framework",org
+ .eclipse.gemini.web.core.spi;version="1.0";uses:="javax.servlet,org.o
+ sgi.framework",org.eclipse.gemini.web.internal.template;version="1.0"
+ ;uses:="org.osgi.framework,org.osgi.util.tracker",org.eclipse.gemini.
+ web.internal.url;version="1.0";uses:="org.eclipse.gemini.web.core,org
+ .eclipse.virgo.util.io,org.eclipse.virgo.util.osgi.manifest,org.eclip
+ se.virgo.util.osgi.manifest.parse,org.osgi.framework,org.osgi.service
+ .url"
+Bundle-Version: 1.0
+Tool: Bundlor 1.0.0.M6
+Bundle-Name: SpringSource OSGi Web Container
+Bundle-ManifestVersion: 2
+Bundle-Activator: org.eclipse.gemini.web.internal.WebContainerActivato
+ r
+Bundle-SymbolicName: org.eclipse.gemini.web.core
+Import-Package: javax.servlet;version="2.5",org.eclipse.virgo.util.io;
+ version="2.0",org.eclipse.virgo.util.osgi;version="2.0",org.eclipse.v
+ irgo.util.osgi.manifest;version="2.0",org.eclipse.virgo.util.osgi.man
+ ifest.parse;version="2.0",org.osgi.framework;version="0",org.osgi.ser
+ vice.event;version="1.1";resolution:="optional",org.osgi.service.pack
+ ageadmin;version="1.2",org.osgi.service.url;version="1.0",org.osgi.ut
+ il.tracker;version="1.4.2",org.slf4j;version="1.5.0"
+
diff --git a/org.eclipse.gemini.web.core/src/test/java/org/eclipse/gemini/web/internal/EventManagerTests.java b/org.eclipse.gemini.web.core/src/test/java/org/eclipse/gemini/web/internal/EventManagerTests.java
new file mode 100644
index 0000000..eb6478d
--- /dev/null
+++ b/org.eclipse.gemini.web.core/src/test/java/org/eclipse/gemini/web/internal/EventManagerTests.java
@@ -0,0 +1,122 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 VMware Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Apache License v2.0 which accompanies this distribution.
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Apache License v2.0 is available at
+ * http://www.opensource.org/licenses/apache2.0.php.
+ * You may elect to redistribute this code under either of these licenses.
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.gemini.web.internal;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.osgi.service.event.Event;
+import org.osgi.service.event.EventAdmin;
+
+import org.eclipse.gemini.web.internal.EventManager;
+import org.eclipse.virgo.teststubs.osgi.framework.StubBundle;
+import org.eclipse.virgo.teststubs.osgi.framework.StubBundleContext;
+import org.eclipse.virgo.teststubs.osgi.service.event.StubEventAdmin;
+import org.eclipse.virgo.teststubs.osgi.support.ObjectClassFilter;
+
+
+public class EventManagerTests {
+
+ private final StubBundleContext bundleContext = new StubBundleContext();
+
+ private EventManager eventManager;
+
+ private final StubEventAdmin eventAdmin = new StubEventAdmin();
+
+ private final StubBundle bundle = new StubBundle();
+
+ private final String contextPath = "/myWebApp";
+
+ @Before
+ public void initialise() {
+ this.bundleContext.registerService(EventAdmin.class.getName(), this.eventAdmin, null);
+ this.bundleContext.addFilter(new ObjectClassFilter(EventAdmin.class));
+
+ this.eventManager = new EventManager(bundleContext);
+ this.eventManager.start();
+ }
+
+ @After
+ public void shutdown() {
+ this.eventManager.stop();
+ }
+
+ @Test
+ public void deploying() {
+ this.eventManager.sendDeploying(this.bundle, null, this.contextPath);
+ Event event = this.eventAdmin.awaitSendingOfEvent("org/osgi/service/web/DEPLOYING", 10);
+ assertNotNull(event);
+ assertProperties(event);
+ }
+
+ @Test
+ public void deployed() {
+ this.eventManager.sendDeployed(this.bundle, null, this.contextPath);
+ Event event = this.eventAdmin.awaitSendingOfEvent("org/osgi/service/web/DEPLOYED", 10);
+ assertNotNull(event);
+ assertProperties(event);
+ }
+
+ @Test
+ public void undeploying() {
+ this.eventManager.sendUndeploying(this.bundle, null, this.contextPath);
+ Event event = this.eventAdmin.awaitSendingOfEvent("org/osgi/service/web/UNDEPLOYING", 10);
+ assertNotNull(event);
+ assertProperties(event);
+ }
+
+ @Test
+ public void undeployed() {
+ this.eventManager.sendUndeployed(this.bundle, null, this.contextPath);
+ Event event = this.eventAdmin.awaitSendingOfEvent("org/osgi/service/web/UNDEPLOYED", 10);
+ assertNotNull(event);
+ assertProperties(event);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Test
+ public void failed() {
+ Exception failure = new Exception();
+ Set<Long> collidingBundles = new HashSet<Long>();
+ collidingBundles.add(2L);
+ collidingBundles.add(3L);
+ this.eventManager.sendFailed(this.bundle, null, this.contextPath, failure, "/path", collidingBundles);
+ Event event = this.eventAdmin.awaitSendingOfEvent("org/osgi/service/web/FAILED", 10);
+ assertNotNull(event);
+ assertProperties(event);
+
+ assertEquals("/path", (String)event.getProperty("collision"));
+
+ Collection<Long> cb = (Collection<Long>)event.getProperty("collision.bundles");
+ assertEquals(2, cb.size());
+ assertTrue(cb.contains(2L));
+ assertTrue(cb.contains(3L));
+ }
+
+ private void assertProperties(Event event) {
+ String contextPath = (String)event.getProperty("context.path");
+ assertEquals("/myWebApp", contextPath);
+ }
+}
diff --git a/org.eclipse.gemini.web.core/src/test/java/org/eclipse/gemini/web/internal/ManifestAsserts.java b/org.eclipse.gemini.web.core/src/test/java/org/eclipse/gemini/web/internal/ManifestAsserts.java
new file mode 100644
index 0000000..429e8eb
--- /dev/null
+++ b/org.eclipse.gemini.web.core/src/test/java/org/eclipse/gemini/web/internal/ManifestAsserts.java
@@ -0,0 +1,71 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 VMware Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Apache License v2.0 which accompanies this distribution.
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Apache License v2.0 is available at
+ * http://www.opensource.org/licenses/apache2.0.php.
+ * You may elect to redistribute this code under either of these licenses.
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.gemini.web.internal;
+
+import static org.junit.Assert.fail;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.osgi.framework.Constants;
+import org.osgi.framework.Version;
+
+import org.eclipse.virgo.util.osgi.VersionRange;
+import org.eclipse.virgo.util.osgi.manifest.BundleManifest;
+import org.eclipse.virgo.util.osgi.manifest.ExportedPackage;
+import org.eclipse.virgo.util.osgi.manifest.ImportedPackage;
+
+public final class ManifestAsserts {
+
+ public static void assertIncludesImport(String packageName, Version version, BundleManifest manifest) {
+ List<ImportedPackage> importedPackages = manifest.getImportPackage().getImportedPackages();
+ for (ImportedPackage packageImport : importedPackages) {
+ String range = packageImport.getAttributes().get(Constants.VERSION_ATTRIBUTE);
+ VersionRange vr = (range == null ? VersionRange.NATURAL_NUMBER_RANGE : new VersionRange(range));
+ String importName = packageImport.getPackageName();
+ if (importName.equals(packageName)) {
+ if (vr.includes(version)) {
+ return;
+ } else {
+ fail("Package '" + packageName + "' not found at version '" + version + "'. Found range :" + vr);
+ }
+ }
+ }
+ fail("Import-Package '" + packageName + "' not found at any version");
+ }
+
+ public static void assertIncludesExport(String packageName, Version version, BundleManifest manifest) {
+ List<ExportedPackage> exportedPackages = manifest.getExportPackage().getExportedPackages();
+ List<Version> nearMatches = new ArrayList<Version>();
+ for (ExportedPackage packageExport : exportedPackages) {
+ String v = packageExport.getAttributes().get(Constants.VERSION_ATTRIBUTE);
+ Version pv = (v == null ? Version.emptyVersion : new Version(v));
+ if (packageExport.getPackageName().equals(packageName)) {
+ if (pv.equals(version)) {
+ return;
+ } else {
+ nearMatches.add(pv);
+ }
+ }
+ }
+ if (nearMatches.isEmpty()) {
+ fail("Export-Package for '" + packageName + "' not found at any version");
+ } else {
+ fail("Export-Package for '" + packageName + "' not found at '" + version + "'. Found '" + nearMatches + "'");
+ }
+ }
+}
diff --git a/org.eclipse.gemini.web.core/src/test/java/org/eclipse/gemini/web/internal/SystemBundleExportsResolverTests.java b/org.eclipse.gemini.web.core/src/test/java/org/eclipse/gemini/web/internal/SystemBundleExportsResolverTests.java
new file mode 100644
index 0000000..a07eb72
--- /dev/null
+++ b/org.eclipse.gemini.web.core/src/test/java/org/eclipse/gemini/web/internal/SystemBundleExportsResolverTests.java
@@ -0,0 +1,114 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 VMware Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Apache License v2.0 which accompanies this distribution.
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Apache License v2.0 is available at
+ * http://www.opensource.org/licenses/apache2.0.php.
+ * You may elect to redistribute this code under either of these licenses.
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.gemini.web.internal;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.Map;
+
+import org.junit.Test;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.Version;
+import org.osgi.service.packageadmin.ExportedPackage;
+
+import org.eclipse.gemini.web.internal.SystemBundleExportsResolver;
+import org.eclipse.virgo.util.osgi.VersionRange;
+
+public class SystemBundleExportsResolverTests {
+
+ @Test
+ public void basic() {
+ ExportedPackage[] input = new ExportedPackage[] { new ExportedPackageImpl("a", new Version(1, 2, 3)),
+ new ExportedPackageImpl("b", new Version(2, 3, 4)) };
+
+ Map<String, VersionRange> output = SystemBundleExportsResolver.combineDuplicateExports(input);
+
+ assertEquals(2, output.size());
+ assertEquals(new VersionRange("[1.2.3,1.2.3]"), output.get("a"));
+ assertEquals(new VersionRange("[2.3.4,2.3.4]"), output.get("b"));
+ }
+
+ @Test
+ public void downwardExpansion() {
+ ExportedPackage[] input = new ExportedPackage[] { new ExportedPackageImpl("a", new Version(2, 0, 0)),
+ new ExportedPackageImpl("a", new Version(1, 0, 0)) };
+
+ Map<String, VersionRange> output = SystemBundleExportsResolver.combineDuplicateExports(input);
+
+ assertEquals(1, output.size());
+ assertEquals(new VersionRange("[1.0.0,2.0.0]"), output.get("a"));
+ }
+
+ @Test
+ public void upwardExpansion() {
+ ExportedPackage[] input = new ExportedPackage[] { new ExportedPackageImpl("a", new Version(1, 0, 0)),
+ new ExportedPackageImpl("a", new Version(2, 0, 0)) };
+
+ Map<String, VersionRange> output = SystemBundleExportsResolver.combineDuplicateExports(input);
+
+ assertEquals(1, output.size());
+ assertEquals(new VersionRange("[1.0.0,2.0.0]"), output.get("a"));
+ }
+
+ @Test
+ public void expansionInBothDirections() {
+ ExportedPackage[] input = new ExportedPackage[] { new ExportedPackageImpl("a", new Version(2, 0, 0)),
+ new ExportedPackageImpl("a", new Version(3, 0, 0)), new ExportedPackageImpl("a", new Version(1, 0, 0)) };
+
+ Map<String, VersionRange> output = SystemBundleExportsResolver.combineDuplicateExports(input);
+
+ assertEquals(1, output.size());
+ assertEquals(new VersionRange("[1.0.0,3.0.0]"), output.get("a"));
+ }
+
+ private static final class ExportedPackageImpl implements ExportedPackage {
+
+ private final String name;
+
+ private final Version version;
+
+ private ExportedPackageImpl(String name, Version version) {
+ this.name = name;
+ this.version = version;
+ }
+
+ public Bundle getExportingBundle() {
+ throw new UnsupportedOperationException();
+ }
+
+ public Bundle[] getImportingBundles() {
+ throw new UnsupportedOperationException();
+ }
+
+ public String getName() {
+ return this.name;
+ }
+
+ public String getSpecificationVersion() {
+ throw new UnsupportedOperationException();
+ }
+
+ public Version getVersion() {
+ return this.version;
+ }
+
+ public boolean isRemovalPending() {
+ throw new UnsupportedOperationException();
+ }
+
+ }
+}
diff --git a/org.eclipse.gemini.web.core/src/test/java/org/eclipse/gemini/web/internal/WebContainerUtilsTests.java b/org.eclipse.gemini.web.core/src/test/java/org/eclipse/gemini/web/internal/WebContainerUtilsTests.java
new file mode 100644
index 0000000..65019dd
--- /dev/null
+++ b/org.eclipse.gemini.web.core/src/test/java/org/eclipse/gemini/web/internal/WebContainerUtilsTests.java
@@ -0,0 +1,251 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 VMware Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Apache License v2.0 which accompanies this distribution.
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Apache License v2.0 is available at
+ * http://www.opensource.org/licenses/apache2.0.php.
+ * You may elect to redistribute this code under either of these licenses.
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.gemini.web.internal;
+
+import static org.easymock.EasyMock.createNiceMock;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.replay;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Properties;
+
+import org.eclipse.gemini.web.internal.WebContainerUtils;
+import org.junit.Test;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.Version;
+
+public class WebContainerUtilsTests {
+
+ private static final Version TEST_BUNDLE_VERSION = new Version("467");
+ private static final String TEST_SYMBOLIC_NAME = "a";
+ private static final Properties EMPTY_PROPERTIES = new Properties();
+
+ @Test
+ public void testGetBaseNameFilePath() {
+ String name = WebContainerUtils.getBaseName("/path/to/app.war");
+ assertEquals("app", name);
+ }
+
+ @Test
+ public void testGetBaseNameDirPath() {
+ String name = WebContainerUtils.getBaseName("/path/to/app.war/");
+ assertEquals("app", name);
+ }
+
+ @Test
+ public void testIsWebBundleWithWarExtension() {
+ Bundle bundle = createNiceMock(Bundle.class);
+ expect(bundle.getLocation()).andReturn("file:foo.war").anyTimes();
+ replay(bundle);
+ assertTrue(WebContainerUtils.isWebBundle(bundle));
+ }
+
+ @Test
+ public void testIsWebBundleWithUpperCaseWarExtension() {
+ Bundle bundle = createNiceMock(Bundle.class);
+ expect(bundle.getLocation()).andReturn("file:foo.WAR").anyTimes();
+ replay(bundle);
+ assertTrue(WebContainerUtils.isWebBundle(bundle));
+ }
+
+ @Test
+ public void testIsWebBundleWithWarExtensionAndTrailingSlash() {
+ Bundle bundle = createNiceMock(Bundle.class);
+ expect(bundle.getLocation()).andReturn("file:foo.war/").anyTimes();
+ replay(bundle);
+ assertTrue(WebContainerUtils.isWebBundle(bundle));
+ }
+
+ @Test
+ public void testIsWebBundleWithWarExtensionAndTrailingSlashes() {
+ Bundle bundle = createNiceMock(Bundle.class);
+ expect(bundle.getLocation()).andReturn("file:foo.war//").anyTimes();
+ replay(bundle);
+ assertTrue(WebContainerUtils.isWebBundle(bundle));
+ }
+
+ @Test
+ public void testIsWebBundleWithUpperCaseWarExtensionAndTrailingSlash() {
+ Bundle bundle = createNiceMock(Bundle.class);
+ expect(bundle.getLocation()).andReturn("file:foo.WAR/").anyTimes();
+ replay(bundle);
+ assertTrue(WebContainerUtils.isWebBundle(bundle));
+ }
+
+ @Test
+ public void testIsWebBundleWithWebBundleScheme() {
+ Bundle bundle = createNiceMock(Bundle.class);
+ expect(bundle.getLocation()).andReturn("webbundle:foo.jar").anyTimes();
+ replay(bundle);
+ assertTrue(WebContainerUtils.isWebBundle(bundle));
+ }
+
+ @Test
+ public void testIsWebBundleWithWebContextPath()
+ throws Exception {
+ Properties p = new Properties();
+ p.setProperty(WebContainerUtils.HEADER_WEB_CONTEXT_PATH, "/foo");
+
+ Bundle bundle = createNiceMock(Bundle.class);
+ expect(bundle.getLocation()).andReturn("file:foo.jar").anyTimes();
+ expect(bundle.getHeaders()).andReturn(p).anyTimes();
+ replay(bundle);
+ assertTrue(WebContainerUtils.isWebBundle(bundle));
+ }
+
+ @Test
+ public void testIsWebBundleWithWebXml() throws MalformedURLException {
+ Bundle bundle = createNiceMock(Bundle.class);
+ expect(bundle.getLocation()).andReturn("file:foo.jar").anyTimes();
+ expect(bundle.getHeaders()).andReturn(new Properties()).anyTimes();
+ expect(bundle.getEntry(WebContainerUtils.ENTRY_WEB_XML)).andReturn(
+ new URL("file:foo.txt")).anyTimes();
+ replay(bundle);
+ assertTrue(WebContainerUtils.isWebBundle(bundle));
+ }
+
+ @Test
+ public void testNotWebBundle() throws Exception {
+ Bundle bundle = createNiceMock(Bundle.class);
+ expect(bundle.getLocation()).andReturn("file:foo.jar").anyTimes();
+ expect(bundle.getHeaders()).andReturn(new Properties()).anyTimes();
+ expect(bundle.getEntry(WebContainerUtils.ENTRY_WEB_XML))
+ .andReturn(null).anyTimes();
+ replay(bundle);
+ assertFalse(WebContainerUtils.isWebBundle(bundle));
+ }
+
+ @Test
+ public void testContextPathSupplied() throws Exception {
+ Properties p = new Properties();
+ p.setProperty(WebContainerUtils.HEADER_WEB_CONTEXT_PATH, "/foo");
+
+ Bundle bundle = createNiceMock(Bundle.class);
+ expect(bundle.getHeaders()).andReturn(p).anyTimes();
+ replay(bundle);
+
+ assertEquals("/foo", WebContainerUtils.getContextPath(bundle));
+ }
+
+ @Test
+ public void testContextPathDefaulted() throws Exception {
+ Properties p = new Properties();
+
+ Bundle bundle = createNiceMock(Bundle.class);
+ expect(bundle.getLocation()).andReturn("file:bar.war").anyTimes();
+ expect(bundle.getHeaders()).andReturn(p).anyTimes();
+ replay(bundle);
+
+ assertEquals("bar", WebContainerUtils.getContextPath(bundle));
+ }
+
+ @Test
+ public void testContextPathDefaultedWindowsPath() throws Exception {
+ Properties p = new Properties();
+
+ Bundle bundle = createNiceMock(Bundle.class);
+ expect(bundle.getLocation()).andReturn("file:/C:\\bar\\app.war")
+ .anyTimes();
+ expect(bundle.getHeaders()).andReturn(p).anyTimes();
+ replay(bundle);
+
+ assertEquals("app", WebContainerUtils.getContextPath(bundle));
+ }
+
+ @Test
+ public void testContextPathDefaultedComplexPath() throws Exception {
+ Properties p = new Properties();
+
+ Bundle bundle = createNiceMock(Bundle.class);
+ expect(bundle.getLocation()).andReturn(
+ "file:../formtags.war?Import-Package:org.foo.bar").anyTimes();
+ expect(bundle.getHeaders()).andReturn(p).anyTimes();
+ replay(bundle);
+
+ assertEquals("formtags", WebContainerUtils.getContextPath(bundle));
+ }
+
+ @Test
+ public void testServletContextOsgiWebSymbolicNamePropertyDefault() {
+ Properties p = new Properties();
+
+ Bundle bundle = createNiceMock(Bundle.class);
+ expect(bundle.getSymbolicName()).andReturn(null).anyTimes();
+ expect(bundle.getHeaders()).andReturn(EMPTY_PROPERTIES).anyTimes();
+ replay(bundle);
+
+ WebContainerUtils.setServletContextBundleProperties(p, bundle);
+
+ assertFalse(p.containsKey(WebContainerUtils.OSGI_WEB_SYMBOLICNAME));
+ }
+
+ @Test
+ public void testServletContextOsgiWebSymbolicNamePropertySupplied() {
+ Bundle bundle = createNiceMock(Bundle.class);
+ expect(bundle.getSymbolicName()).andReturn(TEST_SYMBOLIC_NAME)
+ .anyTimes();
+ expect(bundle.getHeaders()).andReturn(EMPTY_PROPERTIES).anyTimes();
+ replay(bundle);
+
+ Properties p = new Properties();
+
+ WebContainerUtils.setServletContextBundleProperties(p, bundle);
+
+ assertEquals(TEST_SYMBOLIC_NAME, p
+ .get(WebContainerUtils.OSGI_WEB_SYMBOLICNAME));
+ }
+
+ @Test
+ public void testServletContextOsgiWebVersionPropertyDefault() {
+ Bundle bundle = createNiceMock(Bundle.class);
+ expect(bundle.getVersion()).andReturn(null).anyTimes();
+ expect(bundle.getHeaders()).andReturn(EMPTY_PROPERTIES).anyTimes();
+ replay(bundle);
+
+ Properties p = new Properties();
+
+ WebContainerUtils.setServletContextBundleProperties(p, bundle);
+
+ assertFalse(p.containsKey(WebContainerUtils.OSGI_WEB_VERSION));
+ }
+
+ @Test
+ public void testServletContextOsgiWebVersionPropertySupplied() {
+ Bundle bundle = createNiceMock(Bundle.class);
+ expect(bundle.getVersion()).andReturn(TEST_BUNDLE_VERSION).anyTimes();
+
+ Properties headers = new Properties();
+ headers.put(WebContainerUtils.BUNDLE_VERSION_HEADER,
+ TEST_BUNDLE_VERSION.toString());
+ expect(bundle.getHeaders()).andReturn(headers).anyTimes();
+
+ replay(bundle);
+
+ Properties p = new Properties();
+
+ WebContainerUtils.setServletContextBundleProperties(p, bundle);
+
+ String stringVersion = (String) p
+ .get(WebContainerUtils.OSGI_WEB_VERSION);
+ assertEquals(TEST_BUNDLE_VERSION, new Version(stringVersion));
+ }
+
+}
diff --git a/org.eclipse.gemini.web.core/src/test/java/org/eclipse/gemini/web/internal/url/DefaultsWebBundleManifestTransformerTests.java b/org.eclipse.gemini.web.core/src/test/java/org/eclipse/gemini/web/internal/url/DefaultsWebBundleManifestTransformerTests.java
new file mode 100644
index 0000000..44308dd
--- /dev/null
+++ b/org.eclipse.gemini.web.core/src/test/java/org/eclipse/gemini/web/internal/url/DefaultsWebBundleManifestTransformerTests.java
@@ -0,0 +1,121 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 VMware Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Apache License v2.0 which accompanies this distribution.
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Apache License v2.0 is available at
+ * http://www.opensource.org/licenses/apache2.0.php.
+ * You may elect to redistribute this code under either of these licenses.
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.gemini.web.internal.url;
+
+import static org.eclipse.gemini.web.internal.ManifestAsserts.assertIncludesImport;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Collections;
+import java.util.List;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.osgi.framework.Version;
+
+
+import org.eclipse.gemini.web.core.InstallationOptions;
+import org.eclipse.gemini.web.internal.WebContainerUtils;
+import org.eclipse.gemini.web.internal.url.DefaultsWebBundleManifestTransformer;
+import org.eclipse.virgo.util.osgi.manifest.BundleManifest;
+import org.eclipse.virgo.util.osgi.manifest.BundleManifestFactory;
+
+
+public class DefaultsWebBundleManifestTransformerTests {
+
+ private DefaultsWebBundleManifestTransformer defaults = new DefaultsWebBundleManifestTransformer();
+
+ private URL source;
+
+ private InstallationOptions options = new InstallationOptions(Collections.<String, String>emptyMap());
+
+ @Before
+ public void before() throws MalformedURLException {
+ source = new URL("file:src/test/resources/simple-war.war");
+ }
+
+ @Test
+ public void testDefaultBundleManifestVersion() throws Exception{
+ BundleManifest manifest = BundleManifestFactory.createBundleManifest();
+ this.defaults.transform(manifest, source, options, WebContainerUtils.isWebApplicationBundle(manifest));
+ assertEquals(2, manifest.getBundleManifestVersion());
+
+ }
+
+ @Test
+ public void testBundleManifestVersionNotOverridden() throws Exception {
+ BundleManifest manifest = BundleManifestFactory.createBundleManifest();
+ manifest.setBundleManifestVersion(3);
+ this.defaults.transform(manifest, source, options, WebContainerUtils.isWebApplicationBundle(manifest));
+ assertEquals(3, manifest.getBundleManifestVersion());
+
+ }
+
+ @Test
+ public void testDefaultBundleSymbolicName() throws Exception{
+ BundleManifest manifest = BundleManifestFactory.createBundleManifest();
+ this.defaults.transform(manifest, source, options, WebContainerUtils.isWebApplicationBundle(manifest));
+ assertNotNull(manifest.getBundleSymbolicName());
+ assertNotNull(manifest.getBundleSymbolicName().getSymbolicName());
+ }
+
+ @Test
+ public void testBundleSymbolicNameNotOverridden() throws Exception{
+ BundleManifest manifest = BundleManifestFactory.createBundleManifest();
+ manifest.getBundleSymbolicName().setSymbolicName("bsn");
+ this.defaults.transform(manifest, source, options, WebContainerUtils.isWebApplicationBundle(manifest));
+ assertEquals("bsn", manifest.getBundleSymbolicName().getSymbolicName());
+ }
+
+ @Test
+ public void testDefaultBundleClassPath() throws Exception {
+
+ BundleManifest manifest = BundleManifestFactory.createBundleManifest();
+ this.defaults.transform(manifest, source, options, WebContainerUtils.isWebApplicationBundle(manifest));
+ List<String> bcp = manifest.getBundleClasspath();
+ assertNotNull(bcp);
+ assertEquals(4, bcp.size());
+ assertEquals("WEB-INF/classes", bcp.get(0));
+ }
+
+ @Test
+ public void testMergeBundleClassPath() throws Exception {
+ BundleManifest manifest = BundleManifestFactory.createBundleManifest();
+ manifest.getBundleClasspath().add("test");
+ this.defaults.transform(manifest, source, options, WebContainerUtils.isWebApplicationBundle(manifest));
+ List<String> bcp = manifest.getBundleClasspath();
+ assertNotNull(bcp);
+ assertEquals("WEB-INF/classes", bcp.get(0));
+ assertEquals("test", bcp.get(1));
+ }
+
+ @Test
+ public void testDefaultImportPackages() throws Exception {
+ BundleManifest manifest = BundleManifestFactory.createBundleManifest();
+ this.defaults.transform(manifest, source, options, WebContainerUtils.isWebApplicationBundle(manifest));
+
+ assertIncludesImport("javax.servlet", new Version("2.5"), manifest);
+ assertIncludesImport("javax.servlet.http", new Version("2.5"), manifest);
+ assertIncludesImport("javax.servlet.jsp", new Version("2.1"), manifest);
+ assertIncludesImport("javax.servlet.jsp.tagext", new Version("2.1"), manifest);
+ assertIncludesImport("javax.servlet.jsp.el", new Version("2.1"), manifest);
+ assertIncludesImport("javax.el", new Version("1.0"), manifest);
+ }
+
+}
diff --git a/org.eclipse.gemini.web.core/src/test/java/org/eclipse/gemini/web/internal/url/InstallationOptionsTests.java b/org.eclipse.gemini.web.core/src/test/java/org/eclipse/gemini/web/internal/url/InstallationOptionsTests.java
new file mode 100644
index 0000000..a4ed2e6
--- /dev/null
+++ b/org.eclipse.gemini.web.core/src/test/java/org/eclipse/gemini/web/internal/url/InstallationOptionsTests.java
@@ -0,0 +1,108 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 VMware Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Apache License v2.0 which accompanies this distribution.
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Apache License v2.0 is available at
+ * http://www.opensource.org/licenses/apache2.0.php.
+ * You may elect to redistribute this code under either of these licenses.
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.gemini.web.internal.url;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.gemini.web.core.InstallationOptions;
+import org.eclipse.gemini.web.internal.WebContainerUtils;
+import org.junit.Test;
+import org.osgi.framework.Constants;
+
+
+
+public class InstallationOptionsTests {
+
+ @Test
+ public void testBundleManifestVersion() {
+ Map<String, String> map = new HashMap<String, String>();
+ map.put(Constants.BUNDLE_MANIFESTVERSION, "2");
+ InstallationOptions options = new InstallationOptions(map);
+ assertEquals("2", options.getBundleManifestVersion());
+ }
+
+ @Test
+ public void testBundleClassPath() {
+ Map<String, String> map = new HashMap<String, String>();
+ map.put(Constants.BUNDLE_CLASSPATH, "foo,bar");
+ InstallationOptions options = new InstallationOptions(map);
+
+ assertNotNull(options.getBundleClassPath());
+ assertEquals("foo,bar", options.getBundleClassPath());
+ }
+
+ @Test
+ public void testEmptyMap() {
+ InstallationOptions options = new InstallationOptions(Collections.<String, String>emptyMap());
+
+ assertNull(options.getBundleSymbolicName());
+ assertNull(options.getBundleVersion());
+ assertNull(options.getBundleManifestVersion());
+ assertNull(options.getBundleClassPath());
+ assertNull(options.getImportPackageDeclaration());
+ assertNull(options.getExportPackageDeclaration());
+ assertNull(options.getWebContextPath());
+ assertNull(options.getWebJSPExtractLocation());
+ }
+
+ @Test
+ public void testPopulatedMap() {
+ String symbolicName = "sym.name";
+ String bundleManifestVersion = "2";
+ String bundleVersion = "1.0.0";
+
+ String importPackage = "p,q";
+ String exportPackage = "r";
+
+ String contextPath = "/test";
+ String extractLocation = "/tmp";
+
+ Map<String, String> map = new HashMap<String, String>();
+
+ map.put(Constants.BUNDLE_SYMBOLICNAME, symbolicName);
+ map.put(Constants.BUNDLE_MANIFESTVERSION, bundleManifestVersion);
+ map.put(Constants.BUNDLE_VERSION, bundleVersion);
+ map.put(Constants.IMPORT_PACKAGE, importPackage);
+ map.put(Constants.EXPORT_PACKAGE, exportPackage);
+
+ map.put(WebContainerUtils.HEADER_WEB_CONTEXT_PATH, contextPath);
+ map.put(WebContainerUtils.HEADER_WEB_JSP_EXTRACT_LOCATION, extractLocation);
+
+ InstallationOptions options = new InstallationOptions(map);
+ assertEquals(symbolicName, options.getBundleSymbolicName());
+ assertEquals(bundleManifestVersion, options.getBundleManifestVersion());
+ assertEquals(bundleVersion, options.getBundleVersion());
+ assertEquals(importPackage, options.getImportPackageDeclaration());
+ assertEquals(exportPackage, options.getExportPackageDeclaration());
+ assertEquals(contextPath, options.getWebContextPath());
+ assertEquals(extractLocation, options.getWebJSPExtractLocation());
+ }
+
+ @Test
+ public void testNonCamelCaseOption() {
+ Map<String, String> map = new HashMap<String, String>();
+ map.put("bundle-manifestversion", "3");
+ InstallationOptions options = new InstallationOptions(map);
+ assertEquals("3", options.getBundleManifestVersion());
+ }
+}
diff --git a/org.eclipse.gemini.web.core/src/test/java/org/eclipse/gemini/web/internal/url/PackagesInWarScannerTests.java b/org.eclipse.gemini.web.core/src/test/java/org/eclipse/gemini/web/internal/url/PackagesInWarScannerTests.java
new file mode 100644
index 0000000..6b17bf3
--- /dev/null
+++ b/org.eclipse.gemini.web.core/src/test/java/org/eclipse/gemini/web/internal/url/PackagesInWarScannerTests.java
@@ -0,0 +1,95 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 VMware Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Apache License v2.0 which accompanies this distribution.
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Apache License v2.0 is available at
+ * http://www.opensource.org/licenses/apache2.0.php.
+ * You may elect to redistribute this code under either of these licenses.
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.gemini.web.internal.url;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Set;
+
+import org.eclipse.gemini.web.internal.url.PackagesInWarScanner;
+import org.junit.Test;
+
+public class PackagesInWarScannerTests {
+
+ @Test
+ public void scanning() throws MalformedURLException, IOException {
+ PackagesInWarScanner scanner = new PackagesInWarScanner();
+ Set<String> packagesContainedInWar = scanner.getPackagesContainedInWar(new URL("file:src/test/resources/simple-war.war"));
+
+ assertEquals(47, packagesContainedInWar.size()); // was 6 (see below) bug?:
+
+ assertTrue(packagesContainedInWar.contains("ch.qos.logback.classic"));
+ assertTrue(packagesContainedInWar.contains("ch.qos.logback.classic.boolex"));
+ assertTrue(packagesContainedInWar.contains("ch.qos.logback.classic.db"));
+ assertTrue(packagesContainedInWar.contains("ch.qos.logback.classic.filter"));
+ assertTrue(packagesContainedInWar.contains("ch.qos.logback.classic.html"));
+ assertTrue(packagesContainedInWar.contains("ch.qos.logback.classic.jmx"));
+ assertTrue(packagesContainedInWar.contains("ch.qos.logback.classic.joran"));
+ assertTrue(packagesContainedInWar.contains("ch.qos.logback.classic.joran.action"));
+ assertTrue(packagesContainedInWar.contains("ch.qos.logback.classic.log4j"));
+ assertTrue(packagesContainedInWar.contains("ch.qos.logback.classic.net"));
+ assertTrue(packagesContainedInWar.contains("ch.qos.logback.classic.pattern"));
+ assertTrue(packagesContainedInWar.contains("ch.qos.logback.classic.selector"));
+ assertTrue(packagesContainedInWar.contains("ch.qos.logback.classic.selector.servlet"));
+ assertTrue(packagesContainedInWar.contains("ch.qos.logback.classic.sift"));
+ assertTrue(packagesContainedInWar.contains("ch.qos.logback.classic.spi"));
+ assertTrue(packagesContainedInWar.contains("ch.qos.logback.classic.turbo"));
+ assertTrue(packagesContainedInWar.contains("ch.qos.logback.classic.util"));
+ assertTrue(packagesContainedInWar.contains("ch.qos.logback.core"));
+ assertTrue(packagesContainedInWar.contains("ch.qos.logback.core.boolex"));
+ assertTrue(packagesContainedInWar.contains("ch.qos.logback.core.db"));
+ assertTrue(packagesContainedInWar.contains("ch.qos.logback.core.db.dialect"));
+ assertTrue(packagesContainedInWar.contains("ch.qos.logback.core.filter"));
+ assertTrue(packagesContainedInWar.contains("ch.qos.logback.core.helpers"));
+ assertTrue(packagesContainedInWar.contains("ch.qos.logback.core.html"));
+ assertTrue(packagesContainedInWar.contains("ch.qos.logback.core.joran"));
+ assertTrue(packagesContainedInWar.contains("ch.qos.logback.core.joran.action"));
+ assertTrue(packagesContainedInWar.contains("ch.qos.logback.core.joran.event"));
+ assertTrue(packagesContainedInWar.contains("ch.qos.logback.core.joran.spi"));
+ assertTrue(packagesContainedInWar.contains("ch.qos.logback.core.layout"));
+ assertTrue(packagesContainedInWar.contains("ch.qos.logback.core.net"));
+ assertTrue(packagesContainedInWar.contains("ch.qos.logback.core.pattern"));
+ assertTrue(packagesContainedInWar.contains("ch.qos.logback.core.pattern.parser"));
+ assertTrue(packagesContainedInWar.contains("ch.qos.logback.core.pattern.util"));
+ assertTrue(packagesContainedInWar.contains("ch.qos.logback.core.read"));
+ assertTrue(packagesContainedInWar.contains("ch.qos.logback.core.rolling"));
+ assertTrue(packagesContainedInWar.contains("ch.qos.logback.core.rolling.helper"));
+ assertTrue(packagesContainedInWar.contains("ch.qos.logback.core.sift"));
+ assertTrue(packagesContainedInWar.contains("ch.qos.logback.core.spi"));
+ assertTrue(packagesContainedInWar.contains("ch.qos.logback.core.status"));
+ assertTrue(packagesContainedInWar.contains("ch.qos.logback.core.util"));
+ assertTrue(packagesContainedInWar.contains("foo"));
+ assertTrue(packagesContainedInWar.contains("foo.bar"));
+ assertTrue(packagesContainedInWar.contains("goo"));
+ assertTrue(packagesContainedInWar.contains("org.slf4j"));
+ assertTrue(packagesContainedInWar.contains("org.slf4j.helpers"));
+ assertTrue(packagesContainedInWar.contains("org.slf4j.impl"));
+ assertTrue(packagesContainedInWar.contains("org.slf4j.spi"));
+
+
+// assertTrue(packagesContainedInWar.contains("foo"));
+// assertTrue(packagesContainedInWar.contains("foo.bar"));
+// assertTrue(packagesContainedInWar.contains("goo"));
+// assertTrue(packagesContainedInWar.contains("org.slf4j"));
+// assertTrue(packagesContainedInWar.contains("org.slf4j.helpers"));
+// assertTrue(packagesContainedInWar.contains("org.slf4j.spi"));
+ }
+}
diff --git a/org.eclipse.gemini.web.core/src/test/java/org/eclipse/gemini/web/internal/url/PathTests.java b/org.eclipse.gemini.web.core/src/test/java/org/eclipse/gemini/web/internal/url/PathTests.java
new file mode 100644
index 0000000..648049c
--- /dev/null
+++ b/org.eclipse.gemini.web.core/src/test/java/org/eclipse/gemini/web/internal/url/PathTests.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright SpringSource Inc 2010
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.eclipse.gemini.web.internal.url;
+
+import junit.framework.Assert;
+
+import org.eclipse.gemini.web.internal.url.Path;
+import org.junit.Test;
+
+public class PathTests {
+
+ @Test
+ public void testNoArgsConstructor() {
+ Path path = new Path();
+ Assert.assertEquals("", path.toString());
+ }
+
+ @Test
+ public void testPathStringConstructor() {
+ Assert.assertEquals("", new Path("").toString());
+ Assert.assertEquals("a", new Path("a").toString());
+ Assert.assertEquals("a", new Path("./a").toString());
+ Assert.assertEquals("a/b", new Path("a/b").toString());
+ Assert.assertEquals("a/b", new Path("./a/b").toString());
+
+ Assert.assertEquals("/a", new Path("/a").toString());
+ Assert.assertEquals("/a/b", new Path("/a/b").toString());
+ }
+
+ @Test(expected=IllegalArgumentException.class)
+ public void testNullPathFails() {
+ new Path(null);
+ }
+
+ @Test(expected=IllegalArgumentException.class)
+ public void testPathWithTrailingSlashFails() {
+ new Path("/");
+ }
+
+ @Test(expected=IllegalArgumentException.class)
+ public void testPathWithEmptyComponent() {
+ new Path("a//b");
+ }
+
+ @Test(expected=IllegalArgumentException.class)
+ public void testPathWithBackSlashFail() {
+ new Path("\\");
+ }
+
+ @Test
+ public void testSimpleRelativePath() {
+ Path p = new Path("a");
+ Path r = new Path("b");
+ Assert.assertEquals(new Path("a/b"), p.applyRelativePath(r));
+
+ }
+
+ @Test
+ public void testDotDotRelativePathAppliedToDirectory() {
+ Path p = new Path("a");
+ Path r = new Path("../b");
+ Assert.assertEquals(new Path("b"), p.applyRelativePath(r));
+ }
+
+ @Test
+ public void testDotDotRelativePathAppliedToSubdirectory() {
+ Path p = new Path("a/b");
+ Path r = new Path("../c");
+ Assert.assertEquals(new Path("a/c"), p.applyRelativePath(r));
+ }
+
+ @Test
+ public void testMultiDotDotRelativePathAppliedToSubdirectory() {
+ Path p = new Path("a/b/c");
+ Path r = new Path("../../d");
+ Assert.assertEquals(new Path("a/d"), p.applyRelativePath(r));
+ }
+
+ @Test
+ public void testDeepMultiDotDotRelativePathAppliedToSubdirectory() {
+ Path p = new Path("a/b/c");
+ Path r = new Path("../../d/e");
+ Assert.assertEquals(new Path("a/d/e"), p.applyRelativePath(r));
+ }
+
+ @Test
+ public void testDotDotRelativePathAppliedToDirectoryWithEmbeddedHere() {
+ Path p = new Path("a/./b");
+ Path r = new Path("../c");
+ // The implementation does not compress out embedded "heres".
+ Assert.assertEquals(new Path("a/./c"), p.applyRelativePath(r));
+ }
+
+ @Test(expected=IllegalArgumentException.class)
+ public void testCannotEscapeEmptyPath() {
+ Path p = new Path();
+ Path r = new Path("../b");
+ p.applyRelativePath(r);
+ }
+
+}
diff --git a/org.eclipse.gemini.web.core/src/test/java/org/eclipse/gemini/web/internal/url/SpecificationWebBundleManifestTransformerTests.java b/org.eclipse.gemini.web.core/src/test/java/org/eclipse/gemini/web/internal/url/SpecificationWebBundleManifestTransformerTests.java
new file mode 100644
index 0000000..c277a7d
--- /dev/null
+++ b/org.eclipse.gemini.web.core/src/test/java/org/eclipse/gemini/web/internal/url/SpecificationWebBundleManifestTransformerTests.java
@@ -0,0 +1,240 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 VMware Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Apache License v2.0 which accompanies this distribution.
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Apache License v2.0 is available at
+ * http://www.opensource.org/licenses/apache2.0.php.
+ * You may elect to redistribute this code under either of these licenses.
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.gemini.web.internal.url;
+
+import static org.eclipse.gemini.web.internal.ManifestAsserts.assertIncludesExport;
+import static org.eclipse.gemini.web.internal.ManifestAsserts.assertIncludesImport;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.osgi.framework.Constants;
+import org.osgi.framework.Version;
+
+
+import org.eclipse.gemini.web.core.InstallationOptions;
+import org.eclipse.gemini.web.internal.WebContainerUtils;
+import org.eclipse.gemini.web.internal.url.SpecificationWebBundleManifestTransformer;
+import org.eclipse.virgo.util.osgi.manifest.BundleManifest;
+import org.eclipse.virgo.util.osgi.manifest.BundleManifestFactory;
+
+public class SpecificationWebBundleManifestTransformerTests {
+
+ private URL sourceURL;
+
+ private SpecificationWebBundleManifestTransformer transformer;
+
+ @Before
+ public void before() throws MalformedURLException {
+ this.sourceURL = new URL("file:src/test/resources/simple-war.war");
+ this.transformer = new SpecificationWebBundleManifestTransformer();
+ }
+
+ @Test
+ public void transformFromNothing() throws Exception {
+ Map<String, String> options = new HashMap<String, String>();
+ options.put(WebContainerUtils.HEADER_WEB_CONTEXT_PATH, "/");
+
+ BundleManifest manifest = BundleManifestFactory.createBundleManifest();
+ transformer.transform(manifest, this.sourceURL, new InstallationOptions(options), WebContainerUtils.isWebApplicationBundle(manifest));
+
+ assertEquals(new Version("0"), manifest.getBundleVersion());
+ assertNotNull(manifest.getBundleSymbolicName());
+ }
+
+ @Test
+ public void testSpecifyBasicDetails() throws Exception {
+ String symbolicName = "my.bundle";
+ String version = "1.2.3";
+
+ Map<String, String> options = new HashMap<String, String>();
+ options.put(WebContainerUtils.HEADER_WEB_CONTEXT_PATH, "/");
+ options.put(Constants.BUNDLE_SYMBOLICNAME, symbolicName);
+ options.put(Constants.BUNDLE_VERSION, version);
+ options.put(Constants.BUNDLE_CLASSPATH, "foo,bar");
+ options.put(Constants.BUNDLE_MANIFESTVERSION, "3");
+
+ BundleManifest manifest = BundleManifestFactory.createBundleManifest();
+ transformer.transform(manifest, this.sourceURL, new InstallationOptions(options), WebContainerUtils.isWebApplicationBundle(manifest));
+
+ assertEquals(symbolicName, manifest.getBundleSymbolicName().getSymbolicName());
+ assertEquals(new Version(version), manifest.getBundleVersion());
+ assertEquals(3, manifest.getBundleManifestVersion());
+ assertTrue(manifest.getBundleClasspath().contains("foo"));
+ assertTrue(manifest.getBundleClasspath().contains("bar"));
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testSpecifyBundleSymbolicNameForWAB() throws Exception {
+ Map<String, String> options = new HashMap<String, String>();
+ options.put(WebContainerUtils.HEADER_WEB_CONTEXT_PATH, "/");
+ options.put(Constants.BUNDLE_SYMBOLICNAME, "my.bundle");
+
+ BundleManifest manifest = BundleManifestFactory.createBundleManifest();
+ manifest.setBundleVersion(new Version("0")); // implies WAB
+
+ transformer.transform(manifest, this.sourceURL, new InstallationOptions(options), WebContainerUtils.isWebApplicationBundle(manifest));
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testSpecifyBundleVersionForWAB() throws Exception {
+ Map<String, String> options = new HashMap<String, String>();
+ options.put(WebContainerUtils.HEADER_WEB_CONTEXT_PATH, "/");
+ options.put(Constants.BUNDLE_VERSION, "0");
+
+ BundleManifest manifest = BundleManifestFactory.createBundleManifest();
+ manifest.setBundleVersion(new Version("0")); // implies WAB
+
+ transformer.transform(manifest, this.sourceURL, new InstallationOptions(options), WebContainerUtils.isWebApplicationBundle(manifest));
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testSpecifyBundleManifestVersionForWAB() throws Exception {
+ Map<String, String> options = new HashMap<String, String>();
+ options.put(WebContainerUtils.HEADER_WEB_CONTEXT_PATH, "/");
+ options.put(Constants.BUNDLE_MANIFESTVERSION, "2");
+
+ BundleManifest manifest = BundleManifestFactory.createBundleManifest();
+ manifest.setBundleVersion(new Version("0")); // implies WAB
+
+ transformer.transform(manifest, this.sourceURL, new InstallationOptions(options), WebContainerUtils.isWebApplicationBundle(manifest));
+ }
+
+ @Test
+ public void testSpecifyImports() throws Exception {
+
+ Map<String, String> options = new HashMap<String, String>();
+ options.put(WebContainerUtils.HEADER_WEB_CONTEXT_PATH, "/");
+ options.put(Constants.IMPORT_PACKAGE, "p;version=\"1.2.3\",q;version=\"1.2.4\",r;version=\"1.2.3\"");
+
+ BundleManifest manifest = BundleManifestFactory.createBundleManifest();
+
+ transformer.transform(manifest, this.sourceURL, new InstallationOptions(options), WebContainerUtils.isWebApplicationBundle(manifest));
+
+ assertIncludesImport("p", new Version("1.2.3"), manifest);
+ assertIncludesImport("q", new Version("1.2.4"), manifest);
+ assertIncludesImport("r", new Version("1.2.3"), manifest);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testSpecifyImportsForWAB() throws Exception {
+
+ Map<String, String> options = new HashMap<String, String>();
+ options.put(WebContainerUtils.HEADER_WEB_CONTEXT_PATH, "/");
+ options.put(Constants.IMPORT_PACKAGE, "p;version=\"1.2.3\",q;version=\"1.2.4\",r;version=\"1.2.3\""); // implies WAB
+
+ BundleManifest manifest = BundleManifestFactory.createBundleManifest();
+ manifest.getImportPackage().addImportedPackage("r").getAttributes().put(Constants.VERSION_ATTRIBUTE, "1.2.0");
+
+ transformer.transform(manifest, this.sourceURL, new InstallationOptions(options), WebContainerUtils.isWebApplicationBundle(manifest));
+ }
+
+ @Test
+ public void testSpecifyExports() throws Exception {
+
+ Map<String, String> options = new HashMap<String, String>();
+ options.put(WebContainerUtils.HEADER_WEB_CONTEXT_PATH, "/");
+ options.put(Constants.EXPORT_PACKAGE, "p;version=\"1.2.3\",q;version=\"1.2.4\",r;version=\"1.2.3\"");
+
+ BundleManifest manifest = BundleManifestFactory.createBundleManifest();
+ manifest.getExportPackage().addExportedPackage("r").getAttributes().put(Constants.VERSION_ATTRIBUTE, "1.2.0");
+
+ transformer.transform(manifest, this.sourceURL, new InstallationOptions(options), WebContainerUtils.isWebApplicationBundle(manifest));
+
+ assertIncludesExport("p", new Version("1.2.3"), manifest);
+ assertIncludesExport("q", new Version("1.2.4"), manifest);
+ assertIncludesExport("r", new Version("1.2.3"), manifest);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testSpecifyExportsForWAB() throws Exception {
+
+ Map<String, String> options = new HashMap<String, String>();
+ options.put(WebContainerUtils.HEADER_WEB_CONTEXT_PATH, "/");
+ options.put(Constants.EXPORT_PACKAGE, "p;version=\"1.2.3\",q;version=\"1.2.4\",r;version=\"1.2.3\"");
+
+ BundleManifest manifest = BundleManifestFactory.createBundleManifest();
+ manifest.getExportPackage().addExportedPackage("r").getAttributes().put(Constants.VERSION_ATTRIBUTE, "1.2.0");
+ manifest.setBundleVersion(new Version("0")); // implies WAB
+
+ transformer.transform(manifest, this.sourceURL, new InstallationOptions(options), WebContainerUtils.isWebApplicationBundle(manifest));
+ }
+
+ @Test
+ public void testSpecifyWebContextPath() throws Exception {
+
+ Map<String, String> options = new HashMap<String, String>();
+ options.put(WebContainerUtils.HEADER_WEB_CONTEXT_PATH, "/foo");
+
+ BundleManifest manifest = BundleManifestFactory.createBundleManifest();
+ transformer.transform(manifest, this.sourceURL, new InstallationOptions(options), WebContainerUtils.isWebApplicationBundle(manifest));
+
+ assertEquals("/foo", manifest.getHeader(WebContainerUtils.HEADER_WEB_CONTEXT_PATH));
+ }
+
+ @Test
+ public void testSpecifyWebContextPathForWAB() throws Exception {
+
+ Map<String, String> options = new HashMap<String, String>();
+ options.put(WebContainerUtils.HEADER_WEB_CONTEXT_PATH, "/foo");
+
+ BundleManifest manifest = BundleManifestFactory.createBundleManifest();
+ manifest.setBundleVersion(new Version("0")); // implies WAB
+
+ transformer.transform(manifest, this.sourceURL, new InstallationOptions(options), WebContainerUtils.isWebApplicationBundle(manifest));
+
+ assertEquals("/foo", manifest.getHeader(WebContainerUtils.HEADER_WEB_CONTEXT_PATH));
+ }
+
+ @Test
+ public void testSpecifyWebContextPathWithMissingSlash() throws Exception {
+
+ Map<String, String> options = new HashMap<String, String>();
+ options.put(WebContainerUtils.HEADER_WEB_CONTEXT_PATH, "foo");
+
+ BundleManifest manifest = BundleManifestFactory.createBundleManifest();
+ transformer.transform(manifest, this.sourceURL, new InstallationOptions(options), WebContainerUtils.isWebApplicationBundle(manifest));
+
+ assertEquals("/foo", manifest.getHeader(WebContainerUtils.HEADER_WEB_CONTEXT_PATH));
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testSpecifyEmptyWebContextPath() throws Exception {
+
+ Map<String, String> options = new HashMap<String, String>();
+ options.put(WebContainerUtils.HEADER_WEB_CONTEXT_PATH, " ");
+
+ BundleManifest manifest = BundleManifestFactory.createBundleManifest();
+ transformer.transform(manifest, this.sourceURL, new InstallationOptions(options), WebContainerUtils.isWebApplicationBundle(manifest));
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testDoNotSpecifyWebContextPath() throws Exception {
+
+ Map<String, String> options = new HashMap<String, String>();
+
+ BundleManifest manifest = BundleManifestFactory.createBundleManifest();
+ transformer.transform(manifest, this.sourceURL, new InstallationOptions(options), WebContainerUtils.isWebApplicationBundle(manifest));
+ }
+}
diff --git a/org.eclipse.gemini.web.core/src/test/java/org/eclipse/gemini/web/internal/url/SystemBundleExportsImportingWebBundleManifestTransformerTests.java b/org.eclipse.gemini.web.core/src/test/java/org/eclipse/gemini/web/internal/url/SystemBundleExportsImportingWebBundleManifestTransformerTests.java
new file mode 100644
index 0000000..1caeabc
--- /dev/null
+++ b/org.eclipse.gemini.web.core/src/test/java/org/eclipse/gemini/web/internal/url/SystemBundleExportsImportingWebBundleManifestTransformerTests.java
@@ -0,0 +1,133 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 VMware Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Apache License v2.0 which accompanies this distribution.
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Apache License v2.0 is available at
+ * http://www.opensource.org/licenses/apache2.0.php.
+ * You may elect to redistribute this code under either of these licenses.
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.gemini.web.internal.url;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.junit.Test;
+
+
+import org.eclipse.gemini.web.core.InstallationOptions;
+import org.eclipse.gemini.web.internal.WebContainerUtils;
+import org.eclipse.gemini.web.internal.url.SystemBundleExportsImportingWebBundleManifestTransformer;
+import org.eclipse.virgo.util.osgi.VersionRange;
+import org.eclipse.virgo.util.osgi.manifest.BundleManifest;
+import org.eclipse.virgo.util.osgi.manifest.BundleManifestFactory;
+import org.eclipse.virgo.util.osgi.manifest.ImportedPackage;
+
+public class SystemBundleExportsImportingWebBundleManifestTransformerTests {
+
+ @Test
+ public void emptyTransform() throws IOException {
+ Map<String, VersionRange> exports = Collections.<String, VersionRange> emptyMap();
+ SystemBundleExportsImportingWebBundleManifestTransformer transformer = new SystemBundleExportsImportingWebBundleManifestTransformer(exports);
+ transformer.transform(null, null, null, false);
+ }
+
+ @Test
+ public void nothingToImport() throws IOException {
+ Map<String, VersionRange> exports = Collections.<String, VersionRange> emptyMap();
+ SystemBundleExportsImportingWebBundleManifestTransformer transformer = new SystemBundleExportsImportingWebBundleManifestTransformer(exports);
+
+ InstallationOptions options = new InstallationOptions(Collections.<String, String> emptyMap());
+ BundleManifest bundleManifest = BundleManifestFactory.createBundleManifest();
+ transformer.transform(bundleManifest, null, options, WebContainerUtils.isWebApplicationBundle(bundleManifest));
+
+ assertEquals(0, bundleManifest.getImportPackage().getImportedPackages().size());
+ }
+
+ @Test
+ public void importOfExports() throws IOException {
+ Map<String, VersionRange> exports = new HashMap<String, VersionRange>();
+ VersionRange vr1 = new VersionRange("[1.2.3,2.0.0)");
+ VersionRange vr2 = new VersionRange("[2.0.0,3.0.0]");
+ exports.put("a", vr1);
+ exports.put("b", vr2);
+
+ SystemBundleExportsImportingWebBundleManifestTransformer transformer = new SystemBundleExportsImportingWebBundleManifestTransformer(exports);
+
+ Map<String, String> optionsMap = new HashMap<String, String>();
+ InstallationOptions options = new InstallationOptions(optionsMap);
+ BundleManifest bundleManifest = BundleManifestFactory.createBundleManifest();
+ transformer.transform(bundleManifest, null, options, WebContainerUtils.isWebApplicationBundle(bundleManifest));
+
+ List<ImportedPackage> importedPackages = bundleManifest.getImportPackage().getImportedPackages();
+ assertEquals(2, importedPackages.size());
+
+ for (ImportedPackage importedPackage : importedPackages) {
+ if (importedPackage.getPackageName().equals("a")) {
+ assertEquals(vr1, importedPackage.getVersion());
+ } else if (importedPackage.getPackageName().equals("b")) {
+ assertEquals(vr2, importedPackage.getVersion());
+ } else {
+ fail("Unexpected import of package " + importedPackage);
+ }
+ }
+ }
+
+ @Test
+ public void existingImportsShouldNotBeOverridden() throws IOException {
+ Map<String, VersionRange> exports = new HashMap<String, VersionRange>();
+ VersionRange vr1 = new VersionRange("[1.2.3,2.0.0)");
+ VersionRange vr2 = new VersionRange("[2.0.0,3.0.0]");
+ exports.put("a", vr1);
+ exports.put("b", vr2);
+
+ SystemBundleExportsImportingWebBundleManifestTransformer transformer = new SystemBundleExportsImportingWebBundleManifestTransformer(exports);
+
+ Map<String, String> optionsMap = new HashMap<String, String>();
+ InstallationOptions options = new InstallationOptions(optionsMap);
+
+ BundleManifest bundleManifest = BundleManifestFactory.createBundleManifest();
+ bundleManifest.getImportPackage().addImportedPackage("a").setVersion(new VersionRange("[1.0.0,1.0.0]"));
+
+ transformer.transform(bundleManifest, null, options, WebContainerUtils.isWebApplicationBundle(bundleManifest));
+
+ List<ImportedPackage> importedPackages = bundleManifest.getImportPackage().getImportedPackages();
+ assertEquals(1, importedPackages.size());
+ }
+
+ @Test
+ public void packagesInWarShouldNotBeImported() throws IOException {
+ Map<String, VersionRange> exports = new HashMap<String, VersionRange>();
+ exports.put("from.classes", new VersionRange("[1.0.0,1.0.0]"));
+ exports.put("from.lib", new VersionRange("[1.0.0,1.0.0]"));
+ exports.put("javax.sql", new VersionRange("[0,0]"));
+
+ SystemBundleExportsImportingWebBundleManifestTransformer transformer = new SystemBundleExportsImportingWebBundleManifestTransformer(exports);
+
+ Map<String, String> optionsMap = new HashMap<String, String>();
+ InstallationOptions options = new InstallationOptions(optionsMap);
+
+ BundleManifest bundleManifest = BundleManifestFactory.createBundleManifest();
+
+ transformer.transform(bundleManifest, new File("src/test/resources/contains-system-bundle-package.war").toURI().toURL(), options,
+ WebContainerUtils.isWebApplicationBundle(bundleManifest));
+
+ List<ImportedPackage> importedPackages = bundleManifest.getImportPackage().getImportedPackages();
+ assertEquals(1, importedPackages.size());
+ assertEquals("javax.sql", importedPackages.get(0).getPackageName());
+ }
+}
diff --git a/org.eclipse.gemini.web.core/src/test/java/org/eclipse/gemini/web/internal/url/WebBundleScannerTests.java b/org.eclipse.gemini.web.core/src/test/java/org/eclipse/gemini/web/internal/url/WebBundleScannerTests.java
new file mode 100644
index 0000000..5b43ae9
--- /dev/null
+++ b/org.eclipse.gemini.web.core/src/test/java/org/eclipse/gemini/web/internal/url/WebBundleScannerTests.java
@@ -0,0 +1,481 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 VMware Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Apache License v2.0 which accompanies this distribution.
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Apache License v2.0 is available at
+ * http://www.opensource.org/licenses/apache2.0.php.
+ * You may elect to redistribute this code under either of these licenses.
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.gemini.web.internal.url;
+
+import static org.easymock.EasyMock.replay;
+import static org.easymock.EasyMock.verify;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.easymock.EasyMock;
+import org.junit.Test;
+
+import org.eclipse.gemini.web.internal.url.WebBundleScanner;
+import org.eclipse.gemini.web.internal.url.WebBundleScannerCallback;
+import org.eclipse.virgo.util.io.JarUtils;
+import org.eclipse.virgo.util.io.PathReference;
+
+public class WebBundleScannerTests {
+
+ private static final File WAR_FILE = new File("src/test/resources/simple-war.war");
+
+ @Test
+ public void testScanLib() throws IOException {
+ WebBundleScannerCallback callback = EasyMock.createMock(WebBundleScannerCallback.class);
+
+ setExpectations(callback);
+
+ replay(callback);
+
+ WebBundleScanner scanner = new WebBundleScanner(WAR_FILE.toURI().toURL(), callback);
+ scanner.scanWar();
+
+ verify(callback);
+ }
+
+ private void setExpectations(WebBundleScannerCallback callback) {
+ callback.jarFound("WEB-INF/lib/com.springsource.ch.qos.logback.classic-0.9.18.jar");
+ callback.jarFound("WEB-INF/lib/com.springsource.ch.qos.logback.core-0.9.18.jar");
+ callback.jarFound("WEB-INF/lib/com.springsource.slf4j.api-1.5.10.jar");
+
+ callback.classFound("foo/A.class");
+ callback.classFound("foo/bar/C.class");
+ callback.classFound("goo/B.class");
+ callback.classFound("D.class");
+ }
+
+ private void setExpectationsIncludingNestedJars(WebBundleScannerCallback callback) {
+ setExpectations(callback);
+
+ callback.classFound("ch/qos/logback/classic/BasicConfigurator.class");
+ callback.classFound("ch/qos/logback/classic/boolex/JaninoEventEvaluator.class");
+ callback.classFound("ch/qos/logback/classic/boolex/OnErrorEvaluator.class");
+ callback.classFound("ch/qos/logback/classic/ClassicConstants.class");
+ callback.classFound("ch/qos/logback/classic/db/DBAppender.class");
+ callback.classFound("ch/qos/logback/classic/db/DBHelper.class");
+ callback.classFound("ch/qos/logback/classic/filter/LevelFilter.class");
+ callback.classFound("ch/qos/logback/classic/filter/ThresholdFilter.class");
+ callback.classFound("ch/qos/logback/classic/html/DefaultCssBuilder.class");
+ callback.classFound("ch/qos/logback/classic/html/DefaultThrowableRenderer.class");
+ callback.classFound("ch/qos/logback/classic/html/HTMLLayout.class");
+ callback.classFound("ch/qos/logback/classic/html/UrlCssBuilder.class");
+ callback.classFound("ch/qos/logback/classic/jmx/JMXConfigurator.class");
+ callback.classFound("ch/qos/logback/classic/jmx/JMXConfiguratorMBean.class");
+ callback.classFound("ch/qos/logback/classic/jmx/MBeanUtil.class");
+ callback.classFound("ch/qos/logback/classic/joran/action/ConfigurationAction.class");
+ callback.classFound("ch/qos/logback/classic/joran/action/ConsolePluginAction.class");
+ callback.classFound("ch/qos/logback/classic/joran/action/ContextNameAction.class");
+ callback.classFound("ch/qos/logback/classic/joran/action/EvaluatorAction.class");
+ callback.classFound("ch/qos/logback/classic/joran/action/InsertFromJNDIAction.class");
+ callback.classFound("ch/qos/logback/classic/joran/action/JMXConfiguratorAction.class");
+ callback.classFound("ch/qos/logback/classic/joran/action/LevelAction.class");
+ callback.classFound("ch/qos/logback/classic/joran/action/LoggerAction.class");
+ callback.classFound("ch/qos/logback/classic/joran/action/RootLoggerAction.class");
+ callback.classFound("ch/qos/logback/classic/joran/JoranConfigurator.class");
+ callback.classFound("ch/qos/logback/classic/Level.class");
+ callback.classFound("ch/qos/logback/classic/log4j/XMLLayout.class");
+ callback.classFound("ch/qos/logback/classic/Logger.class");
+ callback.classFound("ch/qos/logback/classic/LoggerContext.class");
+ callback.classFound("ch/qos/logback/classic/net/JMSQueueAppender.class");
+ callback.classFound("ch/qos/logback/classic/net/JMSQueueSink.class");
+ callback.classFound("ch/qos/logback/classic/net/JMSTopicAppender.class");
+ callback.classFound("ch/qos/logback/classic/net/JMSTopicSink.class");
+ callback.classFound("ch/qos/logback/classic/net/LoggingEventPreSerializationTransformer.class");
+ callback.classFound("ch/qos/logback/classic/net/SimpleSocketServer.class");
+ callback.classFound("ch/qos/logback/classic/net/SMTPAppender.class");
+ callback.classFound("ch/qos/logback/classic/net/SocketAcceptor.class");
+ callback.classFound("ch/qos/logback/classic/net/SocketAppender.class");
+ callback.classFound("ch/qos/logback/classic/net/SocketNode.class");
+ callback.classFound("ch/qos/logback/classic/net/SyslogAppender.class");
+ callback.classFound("ch/qos/logback/classic/pattern/Abbreviator.class");
+ callback.classFound("ch/qos/logback/classic/pattern/CallerDataConverter.class");
+ callback.classFound("ch/qos/logback/classic/pattern/ClassicConverter.class");
+ callback.classFound("ch/qos/logback/classic/pattern/ClassNameOnlyAbbreviator.class");
+ callback.classFound("ch/qos/logback/classic/pattern/ClassOfCallerConverter.class");
+ callback.classFound("ch/qos/logback/classic/pattern/ContextNameConverter.class");
+ callback.classFound("ch/qos/logback/classic/pattern/DateConverter.class");
+ callback.classFound("ch/qos/logback/classic/pattern/EnsureExceptionHandling.class");
+ callback.classFound("ch/qos/logback/classic/pattern/ExtendedThrowableProxyConverter.class");
+ callback.classFound("ch/qos/logback/classic/pattern/FileOfCallerConverter.class");
+ callback.classFound("ch/qos/logback/classic/pattern/LevelConverter.class");
+ callback.classFound("ch/qos/logback/classic/pattern/LineOfCallerConverter.class");
+ callback.classFound("ch/qos/logback/classic/pattern/LineSeparatorConverter.class");
+ callback.classFound("ch/qos/logback/classic/pattern/LoggerConverter.class");
+ callback.classFound("ch/qos/logback/classic/pattern/LRUCache.class");
+ callback.classFound("ch/qos/logback/classic/pattern/MarkerConverter.class");
+ callback.classFound("ch/qos/logback/classic/pattern/MDCConverter.class");
+ callback.classFound("ch/qos/logback/classic/pattern/MessageConverter.class");
+ callback.classFound("ch/qos/logback/classic/pattern/MethodOfCallerConverter.class");
+ callback.classFound("ch/qos/logback/classic/pattern/NamedConverter.class");
+ callback.classFound("ch/qos/logback/classic/pattern/NopThrowableInformationConverter.class");
+ callback.classFound("ch/qos/logback/classic/pattern/PropertyConverter.class");
+ callback.classFound("ch/qos/logback/classic/pattern/RelativeTimeConverter.class");
+ callback.classFound("ch/qos/logback/classic/pattern/SyslogStartConverter.class");
+ callback.classFound("ch/qos/logback/classic/pattern/TargetLengthBasedClassNameAbbreviator.class");
+ callback.classFound("ch/qos/logback/classic/pattern/ThreadConverter.class");
+ callback.classFound("ch/qos/logback/classic/pattern/ThrowableHandlingConverter.class");
+ callback.classFound("ch/qos/logback/classic/pattern/ThrowableProxyConverter.class");
+ callback.classFound("ch/qos/logback/classic/pattern/Util.class");
+ callback.classFound("ch/qos/logback/classic/PatternLayout.class");
+ callback.classFound("ch/qos/logback/classic/selector/ContextJNDISelector.class");
+ callback.classFound("ch/qos/logback/classic/selector/ContextSelector.class");
+ callback.classFound("ch/qos/logback/classic/selector/DefaultContextSelector.class");
+ callback.classFound("ch/qos/logback/classic/selector/servlet/ContextDetachingSCL.class");
+ callback.classFound("ch/qos/logback/classic/selector/servlet/LoggerContextFilter.class");
+ callback.classFound("ch/qos/logback/classic/sift/AppenderFactory.class");
+ callback.classFound("ch/qos/logback/classic/sift/ContextBasedDiscriminator.class");
+ callback.classFound("ch/qos/logback/classic/sift/MDCBasedDiscriminator.class");
+ callback.classFound("ch/qos/logback/classic/sift/SiftAction.class");
+ callback.classFound("ch/qos/logback/classic/sift/SiftingAppender.class");
+ callback.classFound("ch/qos/logback/classic/sift/SiftingJoranConfigurator.class");
+ callback.classFound("ch/qos/logback/classic/spi/CallerData.class");
+ callback.classFound("ch/qos/logback/classic/spi/ClassPackagingData.class");
+ callback.classFound("ch/qos/logback/classic/spi/ILoggingEvent.class");
+ callback.classFound("ch/qos/logback/classic/spi/IThrowableProxy.class");
+ callback.classFound("ch/qos/logback/classic/spi/LoggerComparator.class");
+ callback.classFound("ch/qos/logback/classic/spi/LoggerContextAware.class");
+ callback.classFound("ch/qos/logback/classic/spi/LoggerContextAwareBase.class");
+ callback.classFound("ch/qos/logback/classic/spi/LoggerContextListener.class");
+ callback.classFound("ch/qos/logback/classic/spi/LoggerContextVO.class");
+ callback.classFound("ch/qos/logback/classic/spi/LoggerRemoteView.class");
+ callback.classFound("ch/qos/logback/classic/spi/LoggingEvent.class");
+ callback.classFound("ch/qos/logback/classic/spi/LoggingEventVO.class");
+ callback.classFound("ch/qos/logback/classic/spi/PackagingDataCalculator.class");
+ callback.classFound("ch/qos/logback/classic/spi/PlatformInfo.class");
+ callback.classFound("ch/qos/logback/classic/spi/StackTraceElementProxy.class");
+ callback.classFound("ch/qos/logback/classic/spi/STEUtil.class");
+ callback.classFound("ch/qos/logback/classic/spi/ThrowableProxy.class");
+ callback.classFound("ch/qos/logback/classic/spi/ThrowableProxyUtil.class");
+ callback.classFound("ch/qos/logback/classic/spi/ThrowableProxyVO.class");
+ callback.classFound("ch/qos/logback/classic/spi/TurboFilterList.class");
+ callback.classFound("ch/qos/logback/classic/turbo/DuplicateMessageFilter.class");
+ callback.classFound("ch/qos/logback/classic/turbo/DynamicThresholdFilter.class");
+ callback.classFound("ch/qos/logback/classic/turbo/LRUMessageCache.class");
+ callback.classFound("ch/qos/logback/classic/turbo/MarkerFilter.class");
+ callback.classFound("ch/qos/logback/classic/turbo/MatchingFilter.class");
+ callback.classFound("ch/qos/logback/classic/turbo/MDCFilter.class");
+ callback.classFound("ch/qos/logback/classic/turbo/MDCValueLevelPair.class");
+ callback.classFound("ch/qos/logback/classic/turbo/ReconfigureOnChangeFilter$ReconfiguringThread.class");
+ callback.classFound("ch/qos/logback/classic/turbo/ReconfigureOnChangeFilter.class");
+ callback.classFound("ch/qos/logback/classic/turbo/TurboFilter.class");
+ callback.classFound("ch/qos/logback/classic/util/ContextInitializer.class");
+ callback.classFound("ch/qos/logback/classic/util/DefaultNestedComponentRules.class");
+ callback.classFound("ch/qos/logback/classic/util/JNDIUtil.class");
+ callback.classFound("ch/qos/logback/classic/util/LevelToSyslogSeverity.class");
+ callback.classFound("ch/qos/logback/classic/util/LoggerStatusPrinter.class");
+ callback.classFound("ch/qos/logback/classic/util/StatusListenerConfigHelper.class");
+ callback.classFound("ch/qos/logback/classic/ViewStatusMessagesServlet.class");
+ callback.classFound("org/slf4j/impl/CopyOnInheritThreadLocal.class");
+ callback.classFound("org/slf4j/impl/LogbackMDCAdapter.class");
+ callback.classFound("org/slf4j/impl/StaticLoggerBinder.class");
+ callback.classFound("org/slf4j/impl/StaticMarkerBinder.class");
+ callback.classFound("org/slf4j/impl/StaticMDCBinder.class");
+ callback.classFound("ch/qos/logback/core/Appender.class");
+ callback.classFound("ch/qos/logback/core/AppenderBase.class");
+ callback.classFound("ch/qos/logback/core/BasicStatusManager.class");
+ callback.classFound("ch/qos/logback/core/boolex/EvaluationException.class");
+ callback.classFound("ch/qos/logback/core/boolex/EventEvaluator.class");
+ callback.classFound("ch/qos/logback/core/boolex/EventEvaluatorBase.class");
+ callback.classFound("ch/qos/logback/core/boolex/JaninoEventEvaluatorBase.class");
+ callback.classFound("ch/qos/logback/core/boolex/Matcher.class");
+ callback.classFound("ch/qos/logback/core/ConsoleAppender.class");
+ callback.classFound("ch/qos/logback/core/Context.class");
+ callback.classFound("ch/qos/logback/core/ContextBase.class");
+ callback.classFound("ch/qos/logback/core/CoreConstants.class");
+ callback.classFound("ch/qos/logback/core/db/BindDataSourceToJNDIAction.class");
+ callback.classFound("ch/qos/logback/core/db/ConnectionSource.class");
+ callback.classFound("ch/qos/logback/core/db/ConnectionSourceBase.class");
+ callback.classFound("ch/qos/logback/core/db/DataSourceConnectionSource.class");
+ callback.classFound("ch/qos/logback/core/db/DBAppenderBase.class");
+ callback.classFound("ch/qos/logback/core/db/DBHelper.class");
+ callback.classFound("ch/qos/logback/core/db/dialect/DBUtil$1.class");
+ callback.classFound("ch/qos/logback/core/db/dialect/DBUtil.class");
+ callback.classFound("ch/qos/logback/core/db/dialect/HSQLDBDialect.class");
+ callback.classFound("ch/qos/logback/core/db/dialect/MsSQLDialect.class");
+ callback.classFound("ch/qos/logback/core/db/dialect/MySQLDialect.class");
+ callback.classFound("ch/qos/logback/core/db/dialect/OracleDialect.class");
+ callback.classFound("ch/qos/logback/core/db/dialect/PostgreSQLDialect.class");
+ callback.classFound("ch/qos/logback/core/db/dialect/SQLDialect.class");
+ callback.classFound("ch/qos/logback/core/db/dialect/SQLDialectCode.class");
+ callback.classFound("ch/qos/logback/core/db/DriverManagerConnectionSource.class");
+ callback.classFound("ch/qos/logback/core/db/JNDIConnectionSource.class");
+ callback.classFound("ch/qos/logback/core/FileAppender.class");
+ callback.classFound("ch/qos/logback/core/filter/AbstractMatcherFilter.class");
+ callback.classFound("ch/qos/logback/core/filter/EvaluatorFilter.class");
+ callback.classFound("ch/qos/logback/core/filter/Filter.class");
+ callback.classFound("ch/qos/logback/core/helpers/CyclicBuffer.class");
+ callback.classFound("ch/qos/logback/core/helpers/ThrowableToStringArray.class");
+ callback.classFound("ch/qos/logback/core/helpers/Transform.class");
+ callback.classFound("ch/qos/logback/core/html/CssBuilder.class");
+ callback.classFound("ch/qos/logback/core/html/HTMLLayoutBase.class");
+ callback.classFound("ch/qos/logback/core/html/IThrowableRenderer.class");
+ callback.classFound("ch/qos/logback/core/html/NOPThrowableRenderer.class");
+ callback.classFound("ch/qos/logback/core/joran/action/AbstractEventEvaluatorAction.class");
+ callback.classFound("ch/qos/logback/core/joran/action/Action.class");
+ callback.classFound("ch/qos/logback/core/joran/action/ActionConst.class");
+ callback.classFound("ch/qos/logback/core/joran/action/AppenderAction.class");
+ callback.classFound("ch/qos/logback/core/joran/action/AppenderRefAction.class");
+ callback.classFound("ch/qos/logback/core/joran/action/ContextPropertyAction.class");
+ callback.classFound("ch/qos/logback/core/joran/action/ConversionRuleAction.class");
+ callback.classFound("ch/qos/logback/core/joran/action/IADataForBasicProperty.class");
+ callback.classFound("ch/qos/logback/core/joran/action/IADataForComplexProperty.class");
+ callback.classFound("ch/qos/logback/core/joran/action/ImplicitAction.class");
+ callback.classFound("ch/qos/logback/core/joran/action/IncludeAction.class");
+ callback.classFound("ch/qos/logback/core/joran/action/NestedBasicPropertyIA$1.class");
+ callback.classFound("ch/qos/logback/core/joran/action/NestedBasicPropertyIA.class");
+ callback.classFound("ch/qos/logback/core/joran/action/NestedComplexPropertyIA$1.class");
+ callback.classFound("ch/qos/logback/core/joran/action/NestedComplexPropertyIA.class");
+ callback.classFound("ch/qos/logback/core/joran/action/NewRuleAction.class");
+ callback.classFound("ch/qos/logback/core/joran/action/NOPAction.class");
+ callback.classFound("ch/qos/logback/core/joran/action/ParamAction.class");
+ callback.classFound("ch/qos/logback/core/joran/action/PropertyAction.class");
+ callback.classFound("ch/qos/logback/core/joran/action/StatusListenerAction.class");
+ callback.classFound("ch/qos/logback/core/joran/action/TimestampAction.class");
+ callback.classFound("ch/qos/logback/core/joran/event/BodyEvent.class");
+ callback.classFound("ch/qos/logback/core/joran/event/EndEvent.class");
+ callback.classFound("ch/qos/logback/core/joran/event/InPlayListener.class");
+ callback.classFound("ch/qos/logback/core/joran/event/SaxEvent.class");
+ callback.classFound("ch/qos/logback/core/joran/event/SaxEventRecorder.class");
+ callback.classFound("ch/qos/logback/core/joran/event/StartEvent.class");
+ callback.classFound("ch/qos/logback/core/joran/GenericConfigurator.class");
+ callback.classFound("ch/qos/logback/core/joran/JoranConfiguratorBase.class");
+ callback.classFound("ch/qos/logback/core/joran/spi/ActionException.class");
+ callback.classFound("ch/qos/logback/core/joran/spi/CAI_WithLocatorSupport.class");
+ callback.classFound("ch/qos/logback/core/joran/spi/DefaultClass.class");
+ callback.classFound("ch/qos/logback/core/joran/spi/DefaultNestedComponentRegistry.class");
+ callback.classFound("ch/qos/logback/core/joran/spi/EventPlayer.class");
+ callback.classFound("ch/qos/logback/core/joran/spi/HostClassAndPropertyDouble.class");
+ callback.classFound("ch/qos/logback/core/joran/spi/InterpretationContext.class");
+ callback.classFound("ch/qos/logback/core/joran/spi/Interpreter.class");
+ callback.classFound("ch/qos/logback/core/joran/spi/JoranException.class");
+ callback.classFound("ch/qos/logback/core/joran/spi/NoAutoStart.class");
+ callback.classFound("ch/qos/logback/core/joran/spi/NoAutoStartUtil.class");
+ callback.classFound("ch/qos/logback/core/joran/spi/Pattern.class");
+ callback.classFound("ch/qos/logback/core/joran/spi/PropertySetter$1.class");
+ callback.classFound("ch/qos/logback/core/joran/spi/PropertySetter.class");
+ callback.classFound("ch/qos/logback/core/joran/spi/RuleStore.class");
+ callback.classFound("ch/qos/logback/core/joran/spi/SimpleRuleStore.class");
+ callback.classFound("ch/qos/logback/core/joran/spi/XMLUtil.class");
+ callback.classFound("ch/qos/logback/core/layout/EchoLayout.class");
+ callback.classFound("ch/qos/logback/core/Layout.class");
+ callback.classFound("ch/qos/logback/core/LayoutBase.class");
+ callback.classFound("ch/qos/logback/core/LogbackException.class");
+ callback.classFound("ch/qos/logback/core/net/JMSAppenderBase.class");
+ callback.classFound("ch/qos/logback/core/net/LoginAuthenticator.class");
+ callback.classFound("ch/qos/logback/core/net/SMTPAppenderBase.class");
+ callback.classFound("ch/qos/logback/core/net/SocketAppenderBase$Connector.class");
+ callback.classFound("ch/qos/logback/core/net/SocketAppenderBase.class");
+ callback.classFound("ch/qos/logback/core/net/SyslogAppenderBase.class");
+ callback.classFound("ch/qos/logback/core/net/SyslogConstants.class");
+ callback.classFound("ch/qos/logback/core/net/SyslogWriter.class");
+ callback.classFound("ch/qos/logback/core/net/TelnetAppender.class");
+ callback.classFound("ch/qos/logback/core/pattern/CompositeConverter.class");
+ callback.classFound("ch/qos/logback/core/pattern/Converter.class");
+ callback.classFound("ch/qos/logback/core/pattern/ConverterUtil.class");
+ callback.classFound("ch/qos/logback/core/pattern/DynamicConverter.class");
+ callback.classFound("ch/qos/logback/core/pattern/FormatInfo.class");
+ callback.classFound("ch/qos/logback/core/pattern/FormattingConverter.class");
+ callback.classFound("ch/qos/logback/core/pattern/LiteralConverter.class");
+ callback.classFound("ch/qos/logback/core/pattern/parser/Compiler.class");
+ callback.classFound("ch/qos/logback/core/pattern/parser/CompositeNode.class");
+ callback.classFound("ch/qos/logback/core/pattern/parser/FormattingNode.class");
+ callback.classFound("ch/qos/logback/core/pattern/parser/KeywordNode.class");
+ callback.classFound("ch/qos/logback/core/pattern/parser/Node.class");
+ callback.classFound("ch/qos/logback/core/pattern/parser/OptionTokenizer.class");
+ callback.classFound("ch/qos/logback/core/pattern/parser/Parser.class");
+ callback.classFound("ch/qos/logback/core/pattern/parser/ScanException.class");
+ callback.classFound("ch/qos/logback/core/pattern/parser/Token.class");
+ callback.classFound("ch/qos/logback/core/pattern/parser/TokenStream.class");
+ callback.classFound("ch/qos/logback/core/pattern/PatternLayoutBase.class");
+ callback.classFound("ch/qos/logback/core/pattern/PostCompileProcessor.class");
+ callback.classFound("ch/qos/logback/core/pattern/SpacePadder.class");
+ callback.classFound("ch/qos/logback/core/pattern/util/AlmostAsIsEscapeUtil.class");
+ callback.classFound("ch/qos/logback/core/pattern/util/IEscapeUtil.class");
+ callback.classFound("ch/qos/logback/core/pattern/util/RegularEscapeUtil.class");
+ callback.classFound("ch/qos/logback/core/read/CyclicBufferAppender.class");
+ callback.classFound("ch/qos/logback/core/read/ListAppender.class");
+ callback.classFound("ch/qos/logback/core/rolling/DefaultTimeBasedFileNamingAndTriggeringPolicy.class");
+ callback.classFound("ch/qos/logback/core/rolling/FixedWindowRollingPolicy$1.class");
+ callback.classFound("ch/qos/logback/core/rolling/FixedWindowRollingPolicy.class");
+ callback.classFound("ch/qos/logback/core/rolling/helper/ArchiveRemover.class");
+ callback.classFound("ch/qos/logback/core/rolling/helper/AsynchronousCompressor.class");
+ callback.classFound("ch/qos/logback/core/rolling/helper/CompressionMode.class");
+ callback.classFound("ch/qos/logback/core/rolling/helper/CompressionRunnable.class");
+ callback.classFound("ch/qos/logback/core/rolling/helper/Compressor$1.class");
+ callback.classFound("ch/qos/logback/core/rolling/helper/Compressor.class");
+ callback.classFound("ch/qos/logback/core/rolling/helper/DatePatternToRegexUtil.class");
+ callback.classFound("ch/qos/logback/core/rolling/helper/DateTokenConverter.class");
+ callback.classFound("ch/qos/logback/core/rolling/helper/DefaultArchiveRemover.class");
+ callback.classFound("ch/qos/logback/core/rolling/helper/FileFilterUtil$1.class");
+ callback.classFound("ch/qos/logback/core/rolling/helper/FileFilterUtil$2.class");
+ callback.classFound("ch/qos/logback/core/rolling/helper/FileFilterUtil$3.class");
+ callback.classFound("ch/qos/logback/core/rolling/helper/FileFilterUtil.class");
+ callback.classFound("ch/qos/logback/core/rolling/helper/FileNamePattern.class");
+ callback.classFound("ch/qos/logback/core/rolling/helper/IntegerTokenConverter.class");
+ callback.classFound("ch/qos/logback/core/rolling/helper/MonoTypedConverter.class");
+ callback.classFound("ch/qos/logback/core/rolling/helper/PeriodicityType.class");
+ callback.classFound("ch/qos/logback/core/rolling/helper/RenameUtil.class");
+ callback.classFound("ch/qos/logback/core/rolling/helper/RollingCalendar$1.class");
+ callback.classFound("ch/qos/logback/core/rolling/helper/RollingCalendar.class");
+ callback.classFound("ch/qos/logback/core/rolling/helper/SequenceToRegex4SDF.class");
+ callback.classFound("ch/qos/logback/core/rolling/helper/SizeAndTimeBasedArchiveRemover.class");
+ callback.classFound("ch/qos/logback/core/rolling/helper/TokenConverter.class");
+ callback.classFound("ch/qos/logback/core/rolling/RollingFileAppender.class");
+ callback.classFound("ch/qos/logback/core/rolling/RollingPolicy.class");
+ callback.classFound("ch/qos/logback/core/rolling/RollingPolicyBase.class");
+ callback.classFound("ch/qos/logback/core/rolling/RolloverFailure.class");
+ callback.classFound("ch/qos/logback/core/rolling/SizeAndTimeBasedFNATP.class");
+ callback.classFound("ch/qos/logback/core/rolling/SizeBasedTriggeringPolicy.class");
+ callback.classFound("ch/qos/logback/core/rolling/TimeBasedFileNamingAndTriggeringPolicy.class");
+ callback.classFound("ch/qos/logback/core/rolling/TimeBasedFileNamingAndTriggeringPolicyBase.class");
+ callback.classFound("ch/qos/logback/core/rolling/TimeBasedRollingPolicy$1.class");
+ callback.classFound("ch/qos/logback/core/rolling/TimeBasedRollingPolicy.class");
+ callback.classFound("ch/qos/logback/core/rolling/TriggeringPolicy.class");
+ callback.classFound("ch/qos/logback/core/rolling/TriggeringPolicyBase.class");
+ callback.classFound("ch/qos/logback/core/sift/AppenderFactoryBase.class");
+ callback.classFound("ch/qos/logback/core/sift/AppenderTracker.class");
+ callback.classFound("ch/qos/logback/core/sift/AppenderTrackerImpl$Entry.class");
+ callback.classFound("ch/qos/logback/core/sift/AppenderTrackerImpl.class");
+ callback.classFound("ch/qos/logback/core/sift/Discriminator.class");
+ callback.classFound("ch/qos/logback/core/sift/SiftingAppenderBase.class");
+ callback.classFound("ch/qos/logback/core/sift/SiftingJoranConfiguratorBase.class");
+ callback.classFound("ch/qos/logback/core/spi/AppenderAttachable.class");
+ callback.classFound("ch/qos/logback/core/spi/AppenderAttachableImpl.class");
+ callback.classFound("ch/qos/logback/core/spi/ContextAware.class");
+ callback.classFound("ch/qos/logback/core/spi/ContextAwareBase.class");
+ callback.classFound("ch/qos/logback/core/spi/ContextAwareImpl.class");
+ callback.classFound("ch/qos/logback/core/spi/FilterAttachable.class");
+ callback.classFound("ch/qos/logback/core/spi/FilterAttachableImpl.class");
+ callback.classFound("ch/qos/logback/core/spi/FilterReply.class");
+ callback.classFound("ch/qos/logback/core/spi/LifeCycle.class");
+ callback.classFound("ch/qos/logback/core/spi/PreSerializationTransformer.class");
+ callback.classFound("ch/qos/logback/core/spi/PropertyContainer.class");
+ callback.classFound("ch/qos/logback/core/status/ErrorStatus.class");
+ callback.classFound("ch/qos/logback/core/status/InfoStatus.class");
+ callback.classFound("ch/qos/logback/core/status/OnConsoleStatusListener.class");
+ callback.classFound("ch/qos/logback/core/status/Status.class");
+ callback.classFound("ch/qos/logback/core/status/StatusBase.class");
+ callback.classFound("ch/qos/logback/core/status/StatusListener.class");
+ callback.classFound("ch/qos/logback/core/status/StatusListenerAsList.class");
+ callback.classFound("ch/qos/logback/core/status/StatusManager.class");
+ callback.classFound("ch/qos/logback/core/status/StatusUtil.class");
+ callback.classFound("ch/qos/logback/core/status/ViewStatusMessagesServletBase.class");
+ callback.classFound("ch/qos/logback/core/status/WarnStatus.class");
+ callback.classFound("ch/qos/logback/core/UnsynchronizedAppenderBase$1.class");
+ callback.classFound("ch/qos/logback/core/UnsynchronizedAppenderBase.class");
+ callback.classFound("ch/qos/logback/core/util/AggregationType.class");
+ callback.classFound("ch/qos/logback/core/util/ContentTypeUtil.class");
+ callback.classFound("ch/qos/logback/core/util/Duration.class");
+ callback.classFound("ch/qos/logback/core/util/DynamicClassLoadingException.class");
+ callback.classFound("ch/qos/logback/core/util/FileSize.class");
+ callback.classFound("ch/qos/logback/core/util/FileUtil.class");
+ callback.classFound("ch/qos/logback/core/util/IncompatibleClassException.class");
+ callback.classFound("ch/qos/logback/core/util/Loader.class");
+ callback.classFound("ch/qos/logback/core/util/OptionHelper.class");
+ callback.classFound("ch/qos/logback/core/util/PropertySetterException.class");
+ callback.classFound("ch/qos/logback/core/util/StatusPrinter.class");
+ callback.classFound("ch/qos/logback/core/util/SystemInfo.class");
+ callback.classFound("ch/qos/logback/core/util/TimeUtil.class");
+ callback.classFound("ch/qos/logback/core/WriterAppender.class");
+ callback.classFound("org/slf4j/helpers/BasicMarker.class");
+ callback.classFound("org/slf4j/helpers/BasicMarkerFactory.class");
+ callback.classFound("org/slf4j/helpers/BasicMDCAdapter.class");
+ callback.classFound("org/slf4j/helpers/MarkerIgnoringBase.class");
+ callback.classFound("org/slf4j/helpers/MessageFormatter.class");
+ callback.classFound("org/slf4j/helpers/NamedLoggerBase.class");
+ callback.classFound("org/slf4j/helpers/NOPLogger.class");
+ callback.classFound("org/slf4j/helpers/NOPMakerAdapter.class");
+ callback.classFound("org/slf4j/helpers/SubstituteLoggerFactory.class");
+ callback.classFound("org/slf4j/helpers/Util.class");
+ callback.classFound("org/slf4j/ILoggerFactory.class");
+ callback.classFound("org/slf4j/IMarkerFactory.class");
+ callback.classFound("org/slf4j/Logger.class");
+ callback.classFound("org/slf4j/LoggerFactory.class");
+ callback.classFound("org/slf4j/Marker.class");
+ callback.classFound("org/slf4j/MarkerFactory.class");
+ callback.classFound("org/slf4j/MDC.class");
+ callback.classFound("org/slf4j/spi/LocationAwareLogger.class");
+ callback.classFound("org/slf4j/spi/LoggerFactoryBinder.class");
+ callback.classFound("org/slf4j/spi/MarkerFactoryBinder.class");
+ callback.classFound("org/slf4j/spi/MDCAdapter.class");
+
+
+ }
+
+ @SuppressWarnings("deprecation")
+ @Test
+ public void testScanLibIncludingNestedJars() throws IOException {
+
+ WebBundleScannerCallback callback = EasyMock.createMock(WebBundleScannerCallback.class);
+
+ setExpectationsIncludingNestedJars(callback);
+
+ replay(callback);
+
+ WebBundleScanner scanner = new WebBundleScanner(WAR_FILE.toURL(), callback, true);
+ scanner.scanWar();
+
+ verify(callback);
+ }
+
+ @Test
+ public void testScanDir() throws Exception {
+ PathReference pr = unpackToDir();
+ try {
+ WebBundleScannerCallback callback = EasyMock.createMock(WebBundleScannerCallback.class);
+
+ setExpectations(callback);
+
+ replay(callback);
+
+ WebBundleScanner scanner = new WebBundleScanner(pr.toURI().toURL(), callback);
+ scanner.scanWar();
+
+ verify(callback);
+ } finally {
+ pr.delete(true);
+ }
+ }
+
+ @Test
+ public void testScanDirIncludingNestedJars() throws Exception {
+ PathReference pr = unpackToDir();
+ try {
+ WebBundleScannerCallback callback = EasyMock.createMock(WebBundleScannerCallback.class);
+
+ setExpectationsIncludingNestedJars(callback);
+
+ replay(callback);
+
+ WebBundleScanner scanner = new WebBundleScanner(pr.toURI().toURL(), callback, true);
+ scanner.scanWar();
+
+ verify(callback);
+ } finally {
+ pr.delete(true);
+ }
+ }
+
+ private PathReference unpackToDir() throws IOException {
+ String tmpDir = System.getProperty("java.io.tmpdir");
+ PathReference dest = new PathReference(new File(tmpDir, "unpack-" + System.currentTimeMillis()));
+ PathReference src = new PathReference(WAR_FILE);
+ JarUtils.unpackTo(src, dest);
+ return dest;
+ }
+}
diff --git a/org.eclipse.gemini.web.core/src/test/java/org/eclipse/gemini/web/internal/url/WebBundleUrlStreamHandlerServiceTests.java b/org.eclipse.gemini.web.core/src/test/java/org/eclipse/gemini/web/internal/url/WebBundleUrlStreamHandlerServiceTests.java
new file mode 100644
index 0000000..b0a31c8
--- /dev/null
+++ b/org.eclipse.gemini.web.core/src/test/java/org/eclipse/gemini/web/internal/url/WebBundleUrlStreamHandlerServiceTests.java
@@ -0,0 +1,74 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 VMware Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Apache License v2.0 which accompanies this distribution.
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Apache License v2.0 is available at
+ * http://www.opensource.org/licenses/apache2.0.php.
+ * You may elect to redistribute this code under either of these licenses.
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.gemini.web.internal.url;
+
+import static org.junit.Assert.assertNotNull;
+
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.net.URLStreamHandler;
+import java.util.Map;
+import java.util.Set;
+import java.util.Map.Entry;
+import java.util.jar.Attributes;
+import java.util.jar.JarInputStream;
+import java.util.jar.Manifest;
+
+import org.eclipse.gemini.web.internal.url.SpecificationWebBundleManifestTransformer;
+import org.eclipse.gemini.web.internal.url.WebBundleUrl;
+import org.eclipse.gemini.web.internal.url.WebBundleUrlStreamHandlerService;
+import org.junit.Test;
+
+public class WebBundleUrlStreamHandlerServiceTests {
+
+ @Test
+ public void testOpenWarConnection() throws Exception {
+ WebBundleUrl url = new TestWarUrl("file:src/test/resources/simple-war.war?Web-ContextPath=/", null);
+ URLConnection connection = url.toURL().openConnection();
+ assertNotNull(connection);
+
+ InputStream inputStream = connection.getInputStream();
+ JarInputStream jarInputStream = new JarInputStream(inputStream);
+ Manifest manifest = jarInputStream.getManifest();
+
+ if (manifest != null) {
+ Attributes mainAttributes = manifest.getMainAttributes();
+ Set<Entry<Object, Object>> entrySet = mainAttributes.entrySet();
+ for (Entry<Object, Object> entry : entrySet) {
+ System.out.println(entry.getKey() + ": " + entry.getValue());
+ }
+ }
+ }
+
+ private static class TestWarUrl extends WebBundleUrl {
+
+ public TestWarUrl(String location, Map<String, String> options) throws MalformedURLException {
+ super(location, options);
+ }
+
+ public TestWarUrl(URL url) {
+ super(url);
+ }
+
+ @Override
+ protected URLStreamHandler createURLStreamHandler() {
+ return new WebBundleUrlStreamHandlerService(new SpecificationWebBundleManifestTransformer());
+ }
+ }
+}
diff --git a/org.eclipse.gemini.web.core/src/test/java/org/eclipse/gemini/web/internal/url/WebBundleUrlTests.java b/org.eclipse.gemini.web.core/src/test/java/org/eclipse/gemini/web/internal/url/WebBundleUrlTests.java
new file mode 100644
index 0000000..9984e8c
--- /dev/null
+++ b/org.eclipse.gemini.web.core/src/test/java/org/eclipse/gemini/web/internal/url/WebBundleUrlTests.java
@@ -0,0 +1,110 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 VMware Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Apache License v2.0 which accompanies this distribution.
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Apache License v2.0 is available at
+ * http://www.opensource.org/licenses/apache2.0.php.
+ * You may elect to redistribute this code under either of these licenses.
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.gemini.web.internal.url;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.net.URLStreamHandler;
+import java.util.Map;
+import java.util.TreeMap;
+
+import org.eclipse.gemini.web.internal.url.WebBundleUrl;
+import org.junit.Test;
+
+
+public class WebBundleUrlTests {
+
+ private static final String FILE_LOCATION = "file:/tmp/foo.jar";
+
+ @Test
+ public void encodeBasicFileUrl() throws MalformedURLException {
+ WebBundleUrl url = new TestWarUrl(FILE_LOCATION, null);
+ URL u = url.toURL();
+ assertNotNull(u);
+ assertEquals(WebBundleUrl.SCHEME, u.getProtocol());
+ assertEquals(FILE_LOCATION, u.getPath());
+ assertNotNull(url.getOptions());
+ assertEquals(FILE_LOCATION, url.getLocation());
+ }
+
+ @Test
+ public void encodeWithOptions() throws Exception {
+ Map<String, String> options = new TreeMap<String, String>();
+ options.put("Web-ContextPath", "/foo");
+ options.put("name", "Rob Harrop");
+ WebBundleUrl url = new TestWarUrl(FILE_LOCATION, options);
+ URL u = url.toURL();
+
+ assertNotNull(u);
+
+ assertEquals(WebBundleUrl.SCHEME, u.getProtocol());
+ assertEquals(FILE_LOCATION, u.getPath());
+ assertEquals("Web-ContextPath=/foo&name=Rob Harrop", u.getQuery());
+ }
+
+ @Test
+ public void createFromUrl() throws Exception {
+ URL url = new URL(WebBundleUrl.SCHEME, null, -1, FILE_LOCATION + "?Web-ContextPath=/foo", new DummyHandler());
+ WebBundleUrl warUrl = new WebBundleUrl(url);
+ assertEquals(FILE_LOCATION, warUrl.getLocation());
+ assertEquals("/foo", warUrl.getOptions().get("Web-ContextPath"));
+ }
+
+ @Test
+ public void createFromUrlWithVersionedImports() throws Exception {
+ URL url = new URL(WebBundleUrl.SCHEME, null, -1, FILE_LOCATION + "?Import-Package=x;version=1,y;version=2", new DummyHandler());
+ WebBundleUrl warUrl = new WebBundleUrl(url);
+ assertEquals(FILE_LOCATION, warUrl.getLocation());
+ assertEquals("x;version=1,y;version=2", warUrl.getOptions().get("Import-Package"));
+ }
+
+ private static class TestWarUrl extends WebBundleUrl {
+
+ public TestWarUrl(String location, Map<String, String> options) throws MalformedURLException {
+ super(location, options);
+ }
+
+ /**
+ * @param url to test
+ * @throws URISyntaxException thrown by superclass, possibly
+ */
+ public TestWarUrl(URL url) throws URISyntaxException {
+ super(url);
+ }
+
+ @Override
+ protected URLStreamHandler createURLStreamHandler() {
+ return new DummyHandler();
+ }
+
+ }
+
+ private static final class DummyHandler extends URLStreamHandler {
+
+ @Override
+ protected URLConnection openConnection(URL u) throws IOException {
+ return null;
+ }
+ }
+
+}
diff --git a/org.eclipse.gemini.web.core/src/test/resources/META-INF/TEST.MF b/org.eclipse.gemini.web.core/src/test/resources/META-INF/TEST.MF
new file mode 100644
index 0000000..a252f55
--- /dev/null
+++ b/org.eclipse.gemini.web.core/src/test/resources/META-INF/TEST.MF
@@ -0,0 +1,7 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Import-Package: junit.framework,org.easymock,org.eclipse.gemini.web.in
+ ternal,org.eclipse.virgo.teststubs.osgi.framework,org.eclipse.virgo.t
+ eststubs.osgi.service.event,org.eclipse.virgo.teststubs.osgi.support,
+ org.junit
+
diff --git a/org.eclipse.gemini.web.core/src/test/resources/contains-system-bundle-package.war/WEB-INF/classes/from/classes/Bar.class b/org.eclipse.gemini.web.core/src/test/resources/contains-system-bundle-package.war/WEB-INF/classes/from/classes/Bar.class
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/org.eclipse.gemini.web.core/src/test/resources/contains-system-bundle-package.war/WEB-INF/classes/from/classes/Bar.class
diff --git a/org.eclipse.gemini.web.core/src/test/resources/contains-system-bundle-package.war/WEB-INF/lib/a.jar b/org.eclipse.gemini.web.core/src/test/resources/contains-system-bundle-package.war/WEB-INF/lib/a.jar
new file mode 100644
index 0000000..e96b5ee
--- /dev/null
+++ b/org.eclipse.gemini.web.core/src/test/resources/contains-system-bundle-package.war/WEB-INF/lib/a.jar
Binary files differ
diff --git a/org.eclipse.gemini.web.core/src/test/resources/simple-manifest-only.jar b/org.eclipse.gemini.web.core/src/test/resources/simple-manifest-only.jar
new file mode 100644
index 0000000..9287561
--- /dev/null
+++ b/org.eclipse.gemini.web.core/src/test/resources/simple-manifest-only.jar
Binary files differ
diff --git a/org.eclipse.gemini.web.core/src/test/resources/simple-war.war b/org.eclipse.gemini.web.core/src/test/resources/simple-war.war
new file mode 100644
index 0000000..3c2ac38
--- /dev/null
+++ b/org.eclipse.gemini.web.core/src/test/resources/simple-war.war
Binary files differ
diff --git a/org.eclipse.gemini.web.core/template.mf b/org.eclipse.gemini.web.core/template.mf
new file mode 100644
index 0000000..24fb9ef
--- /dev/null
+++ b/org.eclipse.gemini.web.core/template.mf
@@ -0,0 +1,18 @@
+Manifest-Version: 1
+Bundle-ManifestVersion: 2
+Bundle-SymbolicName: org.eclipse.gemini.web.core
+Bundle-Version: 1.0
+Bundle-Name: SpringSource OSGi Web Container
+Import-Template: org.osgi.framework;version="0",
+ org.eclipse.virgo.util.*;version="2.0",
+ javax.servlet.*;version="2.5",
+ org.osgi.service.packageadmin.*;version="1.2",
+ org.osgi.service.url.*;version="1.0",
+ org.osgi.util.tracker.*;version="1.4.2",
+ org.slf4j;version="1.5.0",
+ org.osgi.service.event;resolution:="optional";version="1.1"
+Export-Template: org.osgi.services.webcontainer.*;version="1.0",
+ org.eclipse.gemini.web.*;version="1.0"
+Excluded-Imports: org.eclipse.virgo.util.io.JarTransformer
+Excluded-Exports: org.eclipse.gemini.web.internal
+Bundle-Activator: org.eclipse.gemini.web.internal.WebContainerActivator
diff --git a/org.eclipse.gemini.web.extender/.classpath b/org.eclipse.gemini.web.extender/.classpath
new file mode 100644
index 0000000..45a5555
--- /dev/null
+++ b/org.eclipse.gemini.web.extender/.classpath
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="src" path="src/main/java">
+ <attributes>
+ <attribute name="com.springsource.server.ide.jdt.core.test.classpathentry" value="false"/>
+ </attributes>
+ </classpathentry>
+ <classpathentry kind="src" path="src/main/resources">
+ <attributes>
+ <attribute name="com.springsource.server.ide.jdt.core.test.classpathentry" value="false"/>
+ </attributes>
+ </classpathentry>
+ <classpathentry kind="src" output="target/test-classes" path="src/test/java">
+ <attributes>
+ <attribute name="com.springsource.server.ide.jdt.core.test.classpathentry" value="true"/>
+ </attributes>
+ </classpathentry>
+ <classpathentry kind="src" output="target/test-classes" path="src/test/resources">
+ <attributes>
+ <attribute name="com.springsource.server.ide.jdt.core.test.classpathentry" value="true"/>
+ </attributes>
+ </classpathentry>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+ <classpathentry combineaccessrules="false" kind="src" path="/org.eclipse.gemini.web.core"/>
+ <classpathentry kind="var" path="WEB_CONTAINER_IVY_CACHE/org.eclipse.osgi/org.eclipse.osgi/3.5.1.R35x_v20091005/org.eclipse.osgi-3.5.1.R35x_v20091005.jar"/>
+ <classpathentry kind="var" path="WEB_CONTAINER_IVY_CACHE/javax.servlet/com.springsource.javax.servlet/2.5.0/com.springsource.javax.servlet-2.5.0.jar"/>
+ <classpathentry kind="var" path="WEB_CONTAINER_IVY_CACHE/org.eclipse.virgo.teststubs/org.eclipse.virgo.teststubs.osgi/1.0.0.D-20100420091314/org.eclipse.virgo.teststubs.osgi-1.0.0.D-20100420091314.jar" sourcepath="/WEB_CONTAINER_IVY_CACHE/org.eclispe.virgo.osgi.test/org.eclipse.virgo.teststubs.osgi/1.0.0.CI-B40/org.eclipse.virgo.teststubs.osgi-sources-1.0.0.CI-B40.jar"/>
+ <classpathentry kind="var" path="WEB_CONTAINER_IVY_CACHE/org.junit/com.springsource.org.junit/4.7.0/com.springsource.org.junit-4.7.0.jar" sourcepath="/WEB_CONTAINER_IVY_CACHE/org.junit/com.springsource.org.junit/4.5.0/com.springsource.org.junit-sources-4.5.0.jar"/>
+ <classpathentry kind="output" path="target/classes"/>
+</classpath>
diff --git a/org.eclipse.gemini.web.extender/.project b/org.eclipse.gemini.web.extender/.project
new file mode 100644
index 0000000..90d4956
--- /dev/null
+++ b/org.eclipse.gemini.web.extender/.project
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>org.eclipse.gemini.web.extender</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.wst.common.project.facet.core.builder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.springframework.ide.eclipse.core.springbuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>com.springsource.server.ide.bundlor.core.builder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>com.springsource.server.ide.facet.core.bundlenature</nature>
+ <nature>org.springframework.ide.eclipse.core.springnature</nature>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ <nature>org.eclipse.wst.common.project.facet.core.nature</nature>
+ </natures>
+</projectDescription>
diff --git a/org.eclipse.gemini.web.extender/.settings/com.springsource.server.ide.jdt.core.xml b/org.eclipse.gemini.web.extender/.settings/com.springsource.server.ide.jdt.core.xml
new file mode 100644
index 0000000..0a4413c
--- /dev/null
+++ b/org.eclipse.gemini.web.extender/.settings/com.springsource.server.ide.jdt.core.xml
@@ -0,0 +1,2 @@
+<classpath>
+</classpath>
diff --git a/org.eclipse.gemini.web.extender/.settings/org.eclipse.wst.common.project.facet.core.xml b/org.eclipse.gemini.web.extender/.settings/org.eclipse.wst.common.project.facet.core.xml
new file mode 100644
index 0000000..801f856
--- /dev/null
+++ b/org.eclipse.gemini.web.extender/.settings/org.eclipse.wst.common.project.facet.core.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<faceted-project>
+ <installed facet="com.springsource.server.bundle" version="1.0"/>
+</faceted-project>
diff --git a/org.eclipse.gemini.web.extender/.settings/org.springframework.ide.eclipse.core.prefs b/org.eclipse.gemini.web.extender/.settings/org.springframework.ide.eclipse.core.prefs
new file mode 100644
index 0000000..a39c6ee
--- /dev/null
+++ b/org.eclipse.gemini.web.extender/.settings/org.springframework.ide.eclipse.core.prefs
@@ -0,0 +1,67 @@
+#Mon Nov 02 12:03:15 GMT 2009
+eclipse.preferences.version=1
+org.springframework.ide.eclipse.core.builders.enable.aopreferencemodelbuilder=true
+org.springframework.ide.eclipse.core.builders.enable.beanmetadatabuilder=true
+org.springframework.ide.eclipse.core.builders.enable.osgibundleupdater=false
+org.springframework.ide.eclipse.core.enable.project.preferences=false
+org.springframework.ide.eclipse.core.validator.enable.com.springsource.server.ide.manifest.core.manifestvalidator=true
+org.springframework.ide.eclipse.core.validator.enable.com.springsource.sts.bestpractices.beansvalidator=false
+org.springframework.ide.eclipse.core.validator.enable.com.springsource.sts.server.quickfix.manifestvalidator=false
+org.springframework.ide.eclipse.core.validator.enable.org.springframework.ide.eclipse.beans.core.beansvalidator=true
+org.springframework.ide.eclipse.core.validator.enable.org.springframework.ide.eclipse.core.springvalidator=false
+org.springframework.ide.eclipse.core.validator.enable.org.springframework.ide.eclipse.webflow.core.validator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.com.springsource.server.ide.manifest.core.applicationSymbolicNameRule-com.springsource.server.ide.manifest.core.manifestvalidator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.com.springsource.server.ide.manifest.core.applicationVersionRule-com.springsource.server.ide.manifest.core.manifestvalidator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.com.springsource.server.ide.manifest.core.bundleActivationPolicyRule-com.springsource.server.ide.manifest.core.manifestvalidator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.com.springsource.server.ide.manifest.core.bundleActivatorRule-com.springsource.server.ide.manifest.core.manifestvalidator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.com.springsource.server.ide.manifest.core.bundleManifestVersionRule-com.springsource.server.ide.manifest.core.manifestvalidator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.com.springsource.server.ide.manifest.core.bundleNameRule-com.springsource.server.ide.manifest.core.manifestvalidator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.com.springsource.server.ide.manifest.core.bundleSymbolicNameRule-com.springsource.server.ide.manifest.core.manifestvalidator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.com.springsource.server.ide.manifest.core.bundleVersionRule-com.springsource.server.ide.manifest.core.manifestvalidator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.com.springsource.server.ide.manifest.core.exportPackageRule-com.springsource.server.ide.manifest.core.manifestvalidator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.com.springsource.server.ide.manifest.core.importRule-com.springsource.server.ide.manifest.core.manifestvalidator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.com.springsource.server.ide.manifest.core.parsingProblemsRule-com.springsource.server.ide.manifest.core.manifestvalidator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.com.springsource.server.ide.manifest.core.requireBundleRule-com.springsource.server.ide.manifest.core.manifestvalidator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.com.springsource.sts.bestpractices.com.springsource.sts.bestpractices.AvoidDriverManagerDataSource-com.springsource.sts.bestpractices.beansvalidator=false
+org.springframework.ide.eclipse.core.validator.rule.enable.com.springsource.sts.bestpractices.com.springsource.sts.bestpractices.ImportElementsAtTopRulee-com.springsource.sts.bestpractices.beansvalidator=false
+org.springframework.ide.eclipse.core.validator.rule.enable.com.springsource.sts.bestpractices.com.springsource.sts.bestpractices.ParentBeanSpecifiesAbstractClassRule-com.springsource.sts.bestpractices.beansvalidator=false
+org.springframework.ide.eclipse.core.validator.rule.enable.com.springsource.sts.bestpractices.com.springsource.sts.bestpractices.RefElementRule-com.springsource.sts.bestpractices.beansvalidator=false
+org.springframework.ide.eclipse.core.validator.rule.enable.com.springsource.sts.bestpractices.com.springsource.sts.bestpractices.TooManyBeansInFileRule-com.springsource.sts.bestpractices.beansvalidator=false
+org.springframework.ide.eclipse.core.validator.rule.enable.com.springsource.sts.bestpractices.com.springsource.sts.bestpractices.UnnecessaryValueElementRule-com.springsource.sts.bestpractices.beansvalidator=false
+org.springframework.ide.eclipse.core.validator.rule.enable.com.springsource.sts.bestpractices.com.springsource.sts.bestpractices.UseBeanInheritance-com.springsource.sts.bestpractices.beansvalidator=false
+org.springframework.ide.eclipse.core.validator.rule.enable.com.springsource.sts.bestpractices.legacyxmlusage.jndiobjectfactory-com.springsource.sts.bestpractices.beansvalidator=false
+org.springframework.ide.eclipse.core.validator.rule.enable.com.springsource.sts.server.quickfix.importBundleVersionRule-com.springsource.sts.server.quickfix.manifestvalidator=false
+org.springframework.ide.eclipse.core.validator.rule.enable.com.springsource.sts.server.quickfix.importLibraryVersionRule-com.springsource.sts.server.quickfix.manifestvalidator=false
+org.springframework.ide.eclipse.core.validator.rule.enable.com.springsource.sts.server.quickfix.importPackageVersionRule-com.springsource.sts.server.quickfix.manifestvalidator=false
+org.springframework.ide.eclipse.core.validator.rule.enable.com.springsource.sts.server.quickfix.requireBundleVersionRule-com.springsource.sts.server.quickfix.manifestvalidator=false
+org.springframework.ide.eclipse.core.validator.rule.enable.org.springframework.ide.eclipse.beans.core.beanAlias-org.springframework.ide.eclipse.beans.core.beansvalidator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.org.springframework.ide.eclipse.beans.core.beanClass-org.springframework.ide.eclipse.beans.core.beansvalidator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.org.springframework.ide.eclipse.beans.core.beanConstructorArgument-org.springframework.ide.eclipse.beans.core.beansvalidator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.org.springframework.ide.eclipse.beans.core.beanDefinition-org.springframework.ide.eclipse.beans.core.beansvalidator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.org.springframework.ide.eclipse.beans.core.beanDefinitionHolder-org.springframework.ide.eclipse.beans.core.beansvalidator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.org.springframework.ide.eclipse.beans.core.beanFactory-org.springframework.ide.eclipse.beans.core.beansvalidator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.org.springframework.ide.eclipse.beans.core.beanInitDestroyMethod-org.springframework.ide.eclipse.beans.core.beansvalidator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.org.springframework.ide.eclipse.beans.core.beanProperty-org.springframework.ide.eclipse.beans.core.beansvalidator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.org.springframework.ide.eclipse.beans.core.beanReference-org.springframework.ide.eclipse.beans.core.beansvalidator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.org.springframework.ide.eclipse.beans.core.methodOverride-org.springframework.ide.eclipse.beans.core.beansvalidator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.org.springframework.ide.eclipse.beans.core.parsingProblems-org.springframework.ide.eclipse.beans.core.beansvalidator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.org.springframework.ide.eclipse.beans.core.requiredProperty-org.springframework.ide.eclipse.beans.core.beansvalidator=false
+org.springframework.ide.eclipse.core.validator.rule.enable.org.springframework.ide.eclipse.core.springClasspath-org.springframework.ide.eclipse.core.springvalidator=false
+org.springframework.ide.eclipse.core.validator.rule.enable.org.springframework.ide.eclipse.webflow.core.validation.action-org.springframework.ide.eclipse.webflow.core.validator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.org.springframework.ide.eclipse.webflow.core.validation.actionstate-org.springframework.ide.eclipse.webflow.core.validator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.org.springframework.ide.eclipse.webflow.core.validation.attribute-org.springframework.ide.eclipse.webflow.core.validator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.org.springframework.ide.eclipse.webflow.core.validation.attributemapper-org.springframework.ide.eclipse.webflow.core.validator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.org.springframework.ide.eclipse.webflow.core.validation.beanaction-org.springframework.ide.eclipse.webflow.core.validator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.org.springframework.ide.eclipse.webflow.core.validation.evaluationaction-org.springframework.ide.eclipse.webflow.core.validator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.org.springframework.ide.eclipse.webflow.core.validation.evaluationresult-org.springframework.ide.eclipse.webflow.core.validator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.org.springframework.ide.eclipse.webflow.core.validation.exceptionhandler-org.springframework.ide.eclipse.webflow.core.validator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.org.springframework.ide.eclipse.webflow.core.validation.import-org.springframework.ide.eclipse.webflow.core.validator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.org.springframework.ide.eclipse.webflow.core.validation.inputattribute-org.springframework.ide.eclipse.webflow.core.validator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.org.springframework.ide.eclipse.webflow.core.validation.mapping-org.springframework.ide.eclipse.webflow.core.validator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.org.springframework.ide.eclipse.webflow.core.validation.outputattribute-org.springframework.ide.eclipse.webflow.core.validator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.org.springframework.ide.eclipse.webflow.core.validation.set-org.springframework.ide.eclipse.webflow.core.validator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.org.springframework.ide.eclipse.webflow.core.validation.state-org.springframework.ide.eclipse.webflow.core.validator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.org.springframework.ide.eclipse.webflow.core.validation.subflowstate-org.springframework.ide.eclipse.webflow.core.validator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.org.springframework.ide.eclipse.webflow.core.validation.transition-org.springframework.ide.eclipse.webflow.core.validator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.org.springframework.ide.eclipse.webflow.core.validation.variable-org.springframework.ide.eclipse.webflow.core.validator=true
+org.springframework.ide.eclipse.core.validator.rule.enable.org.springframework.ide.eclipse.webflow.core.validation.webflowstate-org.springframework.ide.eclipse.webflow.core.validator=true
diff --git a/org.eclipse.gemini.web.extender/.springBeans b/org.eclipse.gemini.web.extender/.springBeans
new file mode 100644
index 0000000..2257068
--- /dev/null
+++ b/org.eclipse.gemini.web.extender/.springBeans
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<beansProjectDescription>
+ <version>1</version>
+ <pluginVersion><![CDATA[2.2.2.RELEASE]]></pluginVersion>
+ <configSuffixes>
+ <configSuffix><![CDATA[xml]]></configSuffix>
+ </configSuffixes>
+ <enableImports><![CDATA[false]]></enableImports>
+ <configs>
+ </configs>
+ <configSets>
+ </configSets>
+</beansProjectDescription>
diff --git a/org.eclipse.gemini.web.extender/build.xml b/org.eclipse.gemini.web.extender/build.xml
new file mode 100644
index 0000000..cf2573d
--- /dev/null
+++ b/org.eclipse.gemini.web.extender/build.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project name="org.eclipse.gemini.web.extender">
+
+ <property name="clover.coverage" value="43%"/>
+ <property name="findbugs.exclude.file" value="${basedir}/findbugs-exclude.xml"/>
+ <property file="${basedir}/../build.properties"/>
+ <property file="${basedir}/../build.versions"/>
+ <import file="${basedir}/../virgo-build/standard/default.xml"/>
+
+ <target name="collect">
+ <copy file="${jar.output.file}" tofile="${collect.output.dir}/${ant.project.name}.jar"/>
+ </target>
+</project>
diff --git a/org.eclipse.gemini.web.extender/findbugs-exclude.xml b/org.eclipse.gemini.web.extender/findbugs-exclude.xml
new file mode 100644
index 0000000..b98ad71
--- /dev/null
+++ b/org.eclipse.gemini.web.extender/findbugs-exclude.xml
@@ -0,0 +1,23 @@
+<FindBugsFilter>
+ <!-- Exclusions -->
+ <Match>
+ <Bug pattern="EI_EXPOSE_REP"/>
+ <Or>
+ <Class name="org.eclipse.virgo.kernel.config.internal.validate.KernelConfigPointSchemaWalker"/>
+ <Class name="org.eclipse.virgo.kernel.config.internal.validate.KernelConfigPointSchemaParser"/>
+ </Or>
+ <Method name="getTokenNames"/>
+ </Match>
+ <Match>
+ <Bug pattern="SIC_INNER_SHOULD_BE_STATIC"/>
+ <Class name="org.eclipse.virgo.kernel.config.internal.validate.KernelConfigPointSchemaParser$DFA15"/>
+ </Match>
+ <Match>
+ <Bug pattern="MS_PKGPROTECT"/>
+ <Or>
+ <Class name="org.eclipse.virgo.kernel.config.internal.validate.KernelConfigPointSchemaParser"/>
+ <Class name="org.eclipse.virgo.kernel.config.internal.validate.KernelConfigPointSchemaWalker"/>
+ </Or>
+ <Field name="tokenNames"/>
+ </Match>
+</FindBugsFilter>
diff --git a/org.eclipse.gemini.web.extender/ivy.xml b/org.eclipse.gemini.web.extender/ivy.xml
new file mode 100644
index 0000000..1695f5b
--- /dev/null
+++ b/org.eclipse.gemini.web.extender/ivy.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?xml-stylesheet type="text/xsl" href="http://ivyrep.jayasoft.org/ivy-doc.xsl"?>
+<ivy-module
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:noNamespaceSchemaLocation="http://incubator.apache.org/ivy/schemas/ivy.xsd"
+ version="1.3">
+
+ <info organisation="org.eclipse.gemini.web" module="${ant.project.name}"/>
+
+ <configurations>
+ <include file="${virgo.build.dir}/common/default-ivy-configurations.xml"/>
+ </configurations>
+
+ <publications>
+ <artifact name="${ant.project.name}"/>
+ <artifact name="${ant.project.name}-sources" type="src" ext="jar"/>
+ </publications>
+
+ <dependencies>
+ <dependency org="org.eclipse.virgo.util" name="org.eclipse.virgo.util.osgi" rev="${org.eclipse.virgo.util}" conf="compile->runtime"/>
+ <dependency org="org.eclipse.virgo.util" name="org.eclipse.virgo.util.io" rev="${org.eclipse.virgo.util}" conf="compile->runtime"/>
+ <dependency org="org.eclipse.osgi" name="org.eclipse.osgi" rev="${org.eclipse.osgi}" conf="compile->compile"/>
+ <dependency org="javax.servlet" name="com.springsource.javax.servlet" rev="${javax.servlet}" conf="compile->runtime"/>
+ <dependency org="org.junit" name="com.springsource.org.junit" rev="${org.junit}" conf="test->runtime"/>
+ <dependency org="org.slf4j" name="com.springsource.slf4j.api" rev="${org.slf4j}" conf="compile->runtime"/>
+ <dependency org="org.slf4j" name="com.springsource.slf4j.nop" rev="${org.slf4j}" conf="test->runtime"/>
+ <dependency org="org.easymock" name="com.springsource.org.easymock" rev="${org.easymock}" conf="test->runtime"/>
+ <dependency org="org.eclipse.gemini.web" name="org.eclipse.gemini.web.core" rev="latest.integration" conf="compile->compile"/>
+ <dependency org="org.eclipse.virgo.teststubs" name="org.eclipse.virgo.teststubs.osgi" rev="${org.eclipse.virgo.teststubs}" conf="test->runtime"/>
+ </dependencies>
+
+</ivy-module>
diff --git a/org.eclipse.gemini.web.extender/src/main/java/org/eclipse/gemini/web/extender/ExtenderActivator.java b/org.eclipse.gemini.web.extender/src/main/java/org/eclipse/gemini/web/extender/ExtenderActivator.java
new file mode 100644
index 0000000..5e67038
--- /dev/null
+++ b/org.eclipse.gemini.web.extender/src/main/java/org/eclipse/gemini/web/extender/ExtenderActivator.java
@@ -0,0 +1,72 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 VMware Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Apache License v2.0 which accompanies this distribution.
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Apache License v2.0 is available at
+ * http://www.opensource.org/licenses/apache2.0.php.
+ * You may elect to redistribute this code under either of these licenses.
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.gemini.web.extender;
+
+import org.eclipse.gemini.web.core.WebContainer;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.util.tracker.BundleTracker;
+import org.osgi.util.tracker.ServiceTracker;
+import org.osgi.util.tracker.ServiceTrackerCustomizer;
+
+
+
+
+public final class ExtenderActivator implements BundleActivator {
+
+ private ServiceTracker serviceTracker;
+
+ public void start(BundleContext context) {
+ this.serviceTracker = new ServiceTracker(context, WebContainer.class.getName(), new ExtendedWebContainerTracker(context));
+ this.serviceTracker.open();
+ }
+
+ public void stop(BundleContext context) {
+ this.serviceTracker.close();
+ }
+
+ private static final class ExtendedWebContainerTracker implements ServiceTrackerCustomizer {
+
+ private final BundleContext context;
+
+ private BundleTracker bundleTracker;
+
+ public ExtendedWebContainerTracker(BundleContext context) {
+ this.context = context;
+ }
+
+ public Object addingService(ServiceReference reference) {
+ if(this.bundleTracker == null) {
+ WebContainer container = (WebContainer) this.context.getService(reference);
+ this.bundleTracker = new BundleTracker(this.context, Bundle.ACTIVE, new WebContainerBundleCustomizer(container, context.getBundle()));
+ }
+ this.bundleTracker.open();
+ return reference.getBundle().getSymbolicName();
+ }
+
+ public void modifiedService(ServiceReference reference, Object service) {
+ }
+
+ public void removedService(ServiceReference reference, Object service) {
+ this.bundleTracker.close();
+ }
+
+ }
+
+}
diff --git a/org.eclipse.gemini.web.extender/src/main/java/org/eclipse/gemini/web/extender/WebContainerBundleCustomizer.java b/org.eclipse.gemini.web.extender/src/main/java/org/eclipse/gemini/web/extender/WebContainerBundleCustomizer.java
new file mode 100644
index 0000000..c9bdec1
--- /dev/null
+++ b/org.eclipse.gemini.web.extender/src/main/java/org/eclipse/gemini/web/extender/WebContainerBundleCustomizer.java
@@ -0,0 +1,66 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 VMware Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Apache License v2.0 which accompanies this distribution.
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Apache License v2.0 is available at
+ * http://www.opensource.org/licenses/apache2.0.php.
+ * You may elect to redistribute this code under either of these licenses.
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.gemini.web.extender;
+
+import org.eclipse.gemini.web.core.WebApplication;
+import org.eclipse.gemini.web.core.WebApplicationStartFailedException;
+import org.eclipse.gemini.web.core.WebContainer;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleEvent;
+import org.osgi.framework.BundleException;
+import org.osgi.util.tracker.BundleTrackerCustomizer;
+
+
+
+final class WebContainerBundleCustomizer implements BundleTrackerCustomizer {
+
+ private final WebContainer container;
+
+ private final Bundle extenderBundle;
+
+ public WebContainerBundleCustomizer(WebContainer container, Bundle extenderBundle) {
+ this.container = container;
+ this.extenderBundle = extenderBundle;
+ }
+
+ public Object addingBundle(Bundle bundle, BundleEvent event) {
+ Object handle = null;
+ if (this.container.isWebBundle(bundle)) {
+ try {
+ WebApplication webApplication = this.container.createWebApplication(bundle, this.extenderBundle);
+ handle = webApplication;
+ webApplication.start();
+ } catch (BundleException e) {
+ e.printStackTrace();
+ } catch (WebApplicationStartFailedException _) {
+ // ignore in order to track this bundle
+ }
+ }
+ return handle;
+ }
+
+ public void modifiedBundle(Bundle bundle, BundleEvent event, Object object) {
+ // no-op
+ }
+
+ public void removedBundle(Bundle bundle, BundleEvent event, Object object) {
+ if (this.container.isWebBundle(bundle)) {
+ ((WebApplication) object).stop();
+ }
+ }
+
+}
diff --git a/org.eclipse.gemini.web.extender/src/main/resources/META-INF/MANIFEST.MF b/org.eclipse.gemini.web.extender/src/main/resources/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..bb3a46b
--- /dev/null
+++ b/org.eclipse.gemini.web.extender/src/main/resources/META-INF/MANIFEST.MF
@@ -0,0 +1,12 @@
+Manifest-Version: 1
+Export-Package: org.eclipse.gemini.web.extender;version="1";uses:="org
+ .eclipse.gemini.web.core,org.osgi.framework,org.osgi.util.tracker"
+Bundle-Version: 1
+Tool: Bundlor 1.0.0.M6
+Bundle-Name: SpringSource OSGi Web Container Extender
+Bundle-ManifestVersion: 2
+Bundle-Activator: org.eclipse.gemini.web.extender.ExtenderActivator
+Bundle-SymbolicName: org.eclipse.gemini.web.extender
+Import-Package: org.eclipse.gemini.web.core;version="1.0",org.osgi.fra
+ mework;version="0",org.osgi.util.tracker;version="1.4.2"
+
diff --git a/org.eclipse.gemini.web.extender/src/test/java/org/eclipse/gemini/web/extender/WebContainerCustomizerTests.java b/org.eclipse.gemini.web.extender/src/test/java/org/eclipse/gemini/web/extender/WebContainerCustomizerTests.java
new file mode 100644
index 0000000..c15032a
--- /dev/null
+++ b/org.eclipse.gemini.web.extender/src/test/java/org/eclipse/gemini/web/extender/WebContainerCustomizerTests.java
@@ -0,0 +1,122 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 VMware Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Apache License v2.0 which accompanies this distribution.
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Apache License v2.0 is available at
+ * http://www.opensource.org/licenses/apache2.0.php.
+ * You may elect to redistribute this code under either of these licenses.
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.gemini.web.extender;
+
+import static junit.framework.Assert.assertFalse;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import javax.servlet.ServletContext;
+
+import org.junit.Test;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleException;
+
+import org.eclipse.gemini.web.core.WebApplication;
+import org.eclipse.gemini.web.core.WebContainer;
+import org.eclipse.gemini.web.extender.WebContainerBundleCustomizer;
+import org.eclipse.virgo.teststubs.osgi.framework.StubBundle;
+
+
+/**
+ */
+public class WebContainerCustomizerTests {
+
+ private final StubBundle extenderBundle = new StubBundle();
+
+ private final WebContainer container = new StubWebContainer();
+
+ private final WebContainerBundleCustomizer customizer = new WebContainerBundleCustomizer(container, this.extenderBundle);
+
+ @Test
+ public void testAddWebBundle() {
+ StubBundle bundle = new StubBundle();
+ bundle.addHeader("Web-ContextPath", "foo");
+
+ Object result = customizer.addingBundle(bundle, null);
+ assertTrue(result instanceof WebApplication);
+
+ StubWebApplication wa = (StubWebApplication) result;
+ assertEquals(wa.bundle, bundle);
+ assertTrue(wa.started);
+ }
+
+ @Test
+ public void testAddNonWebBundle() {
+ StubBundle bundle = new StubBundle();
+
+ Object result = customizer.addingBundle(bundle, null);
+ assertNull(result);
+ }
+
+ @Test
+ public void testRemoveWebBundle() throws BundleException {
+ StubBundle bundle = new StubBundle();
+ bundle.addHeader("Web-ContextPath", "foo");
+
+ StubWebApplication wa = (StubWebApplication) container.createWebApplication(bundle, this.extenderBundle);
+
+ customizer.removedBundle(bundle,null, wa);
+ assertFalse(wa.started);
+ }
+
+ private static class StubWebContainer implements WebContainer {
+
+ public WebApplication createWebApplication(Bundle bundle) throws BundleException {
+ return new StubWebApplication(bundle);
+ }
+
+ public WebApplication createWebApplication(Bundle bundle, Bundle extenderBundle) throws BundleException {
+ return new StubWebApplication(bundle);
+ }
+
+ public boolean isWebBundle(Bundle bundle) {
+ return bundle.getHeaders().get("Web-ContextPath") != null;
+ }
+
+ public void halt() {
+ }
+ }
+
+ private static class StubWebApplication implements WebApplication {
+
+ final Bundle bundle;
+
+ boolean started = false;
+
+ public StubWebApplication(Bundle bundle) {
+ this.bundle = bundle;
+ }
+
+ public ServletContext getServletContext() {
+ return null;
+ }
+
+ public void start() {
+ this.started = true;
+ }
+
+ public void stop() {
+ this.started = false;
+ }
+
+ public ClassLoader getClassLoader() {
+ return null;
+ }
+ }
+}
diff --git a/org.eclipse.gemini.web.extender/src/test/resources/.gitignore b/org.eclipse.gemini.web.extender/src/test/resources/.gitignore
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/org.eclipse.gemini.web.extender/src/test/resources/.gitignore
diff --git a/org.eclipse.gemini.web.extender/src/test/resources/META-INF/TEST.MF b/org.eclipse.gemini.web.extender/src/test/resources/META-INF/TEST.MF
new file mode 100644
index 0000000..38ab879
--- /dev/null
+++ b/org.eclipse.gemini.web.extender/src/test/resources/META-INF/TEST.MF
@@ -0,0 +1,5 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Import-Package: javax.servlet;version="2.5",junit.framework,org.eclips
+ e.virgo.teststubs.osgi.framework,org.junit
+
diff --git a/org.eclipse.gemini.web.extender/template.mf b/org.eclipse.gemini.web.extender/template.mf
new file mode 100644
index 0000000..2da2d41
--- /dev/null
+++ b/org.eclipse.gemini.web.extender/template.mf
@@ -0,0 +1,11 @@
+Manifest-Version: 1
+Bundle-ManifestVersion: 2
+Bundle-SymbolicName: org.eclipse.gemini.web.extender
+Bundle-Version: 1
+Bundle-Name: SpringSource OSGi Web Container Extender
+Bundle-Activator: org.eclipse.gemini.web.extender.ExtenderActivator
+Import-Template: org.eclipse.gemini.web.*;version="1.0",
+ org.osgi.framework.*;version="0",
+ org.osgi.util.tracker.*;version="1.4.2",
+ javax.servlet;version="2.5"
+
diff --git a/org.eclipse.gemini.web.test/.classpath b/org.eclipse.gemini.web.test/.classpath
new file mode 100644
index 0000000..0652aca
--- /dev/null
+++ b/org.eclipse.gemini.web.test/.classpath
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="src" path="src/main/java"/>
+ <classpathentry kind="src" output="target/test-classes" path="src/test/java"/>
+ <classpathentry kind="src" output="target/test-classes" path="src/test/resources"/>
+ <classpathentry excluding=".gitignore" kind="src" path="src/main/resources"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+ <classpathentry kind="var" path="WEB_CONTAINER_IVY_CACHE/org.junit/com.springsource.org.junit/4.7.0/com.springsource.org.junit-4.7.0.jar" sourcepath="/WEB_CONTAINER_IVY_CACHE/org.junit/com.springsource.org.junit/4.5.0/com.springsource.org.junit-sources-4.5.0.jar"/>
+ <classpathentry kind="var" path="WEB_CONTAINER_IVY_CACHE/org.eclipse.osgi/org.eclipse.osgi/3.5.1.R35x_v20091005/org.eclipse.osgi-3.5.1.R35x_v20091005.jar" sourcepath="/WEB_CONTAINER_IVY_CACHE/org.eclipse.osgi/org.eclipse.osgi/3.5.1.R35x_v20091005/org.eclipse.osgi-sources-3.5.1.R35x_v20091005.jar"/>
+ <classpathentry kind="var" path="WEB_CONTAINER_IVY_CACHE/org.eclipse.virgo.util/org.eclipse.virgo.util.osgi/2.1.0.D-20100420091708/org.eclipse.virgo.util.osgi-2.1.0.D-20100420091708.jar" sourcepath="/WEB_CONTAINER_IVY_CACHE/org.eclispe.virgo.util/org.eclipse.virgo.util.osgi/2.0.0.RELEASE/org.eclipse.virgo.util.osgi-sources-2.0.0.RELEASE.jar"/>
+ <classpathentry kind="var" path="WEB_CONTAINER_IVY_CACHE/org.eclipse.virgo.util/org.eclipse.virgo.util.io/2.1.0.D-20100420091708/org.eclipse.virgo.util.io-2.1.0.D-20100420091708.jar" sourcepath="/WEB_CONTAINER_IVY_CACHE/org.eclispe.virgo.util/org.eclipse.virgo.util.io/2.0.0.RELEASE/org.eclipse.virgo.util.io-sources-2.0.0.RELEASE.jar"/>
+ <classpathentry kind="var" path="WEB_CONTAINER_IVY_CACHE/org.eclipse.virgo.util/org.eclipse.virgo.util.common/2.1.0.D-20100420091708/org.eclipse.virgo.util.common-2.1.0.D-20100420091708.jar" sourcepath="/WEB_CONTAINER_IVY_CACHE/org.eclispe.virgo.util/org.eclipse.virgo.util.common/2.0.0.RELEASE/org.eclipse.virgo.util.common-sources-2.0.0.RELEASE.jar"/>
+ <classpathentry kind="var" path="WEB_CONTAINER_IVY_CACHE/org.slf4j/com.springsource.slf4j.api/1.5.10/com.springsource.slf4j.api-1.5.10.jar" sourcepath="/WEB_CONTAINER_IVY_CACHE/org.slf4j/com.springsource.slf4j.api/1.5.10/com.springsource.slf4j.api-sources-1.5.10.jar"/>
+ <classpathentry kind="var" path="WEB_CONTAINER_IVY_CACHE/org.apache.catalina.springsource/com.springsource.org.apache.catalina.springsource/6.0.20.S2-r5956/com.springsource.org.apache.catalina.springsource-6.0.20.S2-r5956.jar" sourcepath="/WEB_CONTAINER_IVY_CACHE/org.apache.catalina.springsource/com.springsource.org.apache.catalina.springsource/6.0.20.S2-r5956/com.springsource.org.apache.catalina.springsource-sources-6.0.20.S2-r5956.jar"/>
+ <classpathentry kind="var" path="WEB_CONTAINER_IVY_CACHE/org.apache.juli.springsource/com.springsource.org.apache.juli.extras.springsource/6.0.20.S2-r5956/com.springsource.org.apache.juli.extras.springsource-6.0.20.S2-r5956.jar"/>
+ <classpathentry kind="var" path="WEB_CONTAINER_IVY_CACHE/org.apache.jasper.springsource/com.springsource.org.apache.jasper.springsource/6.0.20.S2-r5956/com.springsource.org.apache.jasper.springsource-6.0.20.S2-r5956.jar"/>
+ <classpathentry kind="var" path="WEB_CONTAINER_IVY_CACHE/org.apache.jasper.springsource/com.springsource.org.apache.jasper.org.eclipse.jdt.springsource/6.0.20.S2-r5956/com.springsource.org.apache.jasper.org.eclipse.jdt.springsource-6.0.20.S2-r5956.jar"/>
+ <classpathentry kind="var" path="WEB_CONTAINER_IVY_CACHE/org.apache.catalina.springsource/com.springsource.org.apache.catalina.ha.springsource/6.0.20.S2-r5956/com.springsource.org.apache.catalina.ha.springsource-6.0.20.S2-r5956.jar"/>
+ <classpathentry kind="var" path="WEB_CONTAINER_IVY_CACHE/org.apache.catalina.springsource/com.springsource.org.apache.catalina.tribes.springsource/6.0.20.S2-r5956/com.springsource.org.apache.catalina.tribes.springsource-6.0.20.S2-r5956.jar"/>
+ <classpathentry kind="var" path="WEB_CONTAINER_IVY_CACHE/org.apache.coyote.springsource/com.springsource.org.apache.coyote.springsource/6.0.20.S2-r5956/com.springsource.org.apache.coyote.springsource-6.0.20.S2-r5956.jar" sourcepath="/WEB_CONTAINER_IVY_CACHE/org.apache.coyote.springsource/com.springsource.org.apache.coyote.springsource/6.0.20.S2-r5956/com.springsource.org.apache.coyote.springsource-sources-6.0.20.S2-r5956.jar"/>
+ <classpathentry kind="var" path="WEB_CONTAINER_IVY_CACHE/javax.servlet/com.springsource.javax.servlet/2.5.0/com.springsource.javax.servlet-2.5.0.jar" sourcepath="/WEB_CONTAINER_IVY_CACHE/javax.servlet/com.springsource.javax.servlet/2.5.0/com.springsource.javax.servlet-sources-2.5.0.jar"/>
+ <classpathentry kind="var" path="WEB_CONTAINER_IVY_CACHE/org.easymock/com.springsource.org.easymock/2.3.0/com.springsource.org.easymock-2.3.0.jar" sourcepath="/WEB_CONTAINER_IVY_CACHE/org.easymock/com.springsource.org.easymock/2.3.0/com.springsource.org.easymock-sources-2.3.0.jar"/>
+ <classpathentry combineaccessrules="false" kind="src" path="/org.eclipse.gemini.web.tomcat"/>
+ <classpathentry kind="var" path="WEB_CONTAINER_IVY_CACHE/org.eclipse.virgo.test/org.eclipse.virgo.test.framework/2.1.0.D-20100420091951/org.eclipse.virgo.test.framework-2.1.0.D-20100420091951.jar" sourcepath="/WEB_CONTAINER_IVY_CACHE/org.eclipse.virgo.test/org.eclipse.virgo.test.framework/2.1.0.D-20100420091951/org.eclipse.virgo.test.framework-sources-2.1.0.D-20100420091951.jar"/>
+ <classpathentry kind="var" path="WEB_CONTAINER_IVY_CACHE/org.eclipse.virgo.osgi/org.eclipse.virgo.osgi.launcher/2.1.0.D-20100420091535/org.eclipse.virgo.osgi.launcher-2.1.0.D-20100420091535.jar" sourcepath="/WEB_CONTAINER_IVY_CACHE/org.eclispe.virgo.osgi/org.eclipse.virgo.osgi.launcher/2.0.0.RELEASE/org.eclipse.virgo.osgi.launcher-sources-2.0.0.RELEASE.jar"/>
+ <classpathentry combineaccessrules="false" kind="src" path="/org.eclipse.gemini.web.core"/>
+ <classpathentry kind="output" path="target/classes"/>
+</classpath>
diff --git a/org.eclipse.gemini.web.test/.project b/org.eclipse.gemini.web.test/.project
new file mode 100644
index 0000000..4d08c0b
--- /dev/null
+++ b/org.eclipse.gemini.web.test/.project
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>org.eclipse.gemini.web.test</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.wst.common.project.facet.core.builder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.springframework.ide.eclipse.core.springbuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>com.springsource.server.ide.bundlor.core.builder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>com.springsource.server.ide.facet.core.bundlenature</nature>
+ <nature>org.springframework.ide.eclipse.core.springnature</nature>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ <nature>org.eclipse.wst.common.project.facet.core.nature</nature>
+ </natures>
+</projectDescription>
diff --git a/org.eclipse.gemini.web.test/.settings/org.eclipse.wst.common.project.facet.core.xml b/org.eclipse.gemini.web.test/.settings/org.eclipse.wst.common.project.facet.core.xml
new file mode 100644
index 0000000..801f856
--- /dev/null
+++ b/org.eclipse.gemini.web.test/.settings/org.eclipse.wst.common.project.facet.core.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<faceted-project>
+ <installed facet="com.springsource.server.bundle" version="1.0"/>
+</faceted-project>
diff --git a/org.eclipse.gemini.web.test/.springBeans b/org.eclipse.gemini.web.test/.springBeans
new file mode 100644
index 0000000..2257068
--- /dev/null
+++ b/org.eclipse.gemini.web.test/.springBeans
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<beansProjectDescription>
+ <version>1</version>
+ <pluginVersion><![CDATA[2.2.2.RELEASE]]></pluginVersion>
+ <configSuffixes>
+ <configSuffix><![CDATA[xml]]></configSuffix>
+ </configSuffixes>
+ <enableImports><![CDATA[false]]></enableImports>
+ <configs>
+ </configs>
+ <configSets>
+ </configSets>
+</beansProjectDescription>
diff --git a/org.eclipse.gemini.web.test/build.xml b/org.eclipse.gemini.web.test/build.xml
new file mode 100644
index 0000000..3962cc0
--- /dev/null
+++ b/org.eclipse.gemini.web.test/build.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project name="org.eclipse.gemini.web.test">
+
+ <property name="findbugs.exclude.file" value="${basedir}/findbugs-exclude.xml"/>
+ <property file="${basedir}/../build.properties"/>
+ <property file="${basedir}/../build.versions"/>
+ <import file="${basedir}/../virgo-build/standard/default.xml"/>
+
+ <target name="collect">
+ </target>
+</project>
diff --git a/org.eclipse.gemini.web.test/ivy.xml b/org.eclipse.gemini.web.test/ivy.xml
new file mode 100644
index 0000000..cf90d95
--- /dev/null
+++ b/org.eclipse.gemini.web.test/ivy.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?xml-stylesheet type="text/xsl" href="http://ivyrep.jayasoft.org/ivy-doc.xsl"?>
+<ivy-module
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:noNamespaceSchemaLocation="http://incubator.apache.org/ivy/schemas/ivy.xsd"
+ version="1.3">
+
+ <info organisation="org.eclipse.gemini.web" module="${ant.project.name}"/>
+
+ <configurations>
+ <include file="${virgo.build.dir}/common/default-ivy-configurations.xml"/>
+ </configurations>
+
+ <publications>
+ <artifact name="${ant.project.name}"/>
+ <artifact name="${ant.project.name}-sources" type="src" ext="jar"/>
+ </publications>
+
+ <dependencies>
+ <dependency org="org.eclipse.virgo.util" name="org.eclipse.virgo.util.osgi" rev="${org.eclipse.virgo.util}" conf="compile->runtime"/>
+ <dependency org="org.eclipse.virgo.util" name="org.eclipse.virgo.util.io" rev="${org.eclipse.virgo.util}" conf="compile->runtime"/>
+ <dependency org="org.eclipse.osgi" name="org.eclipse.osgi" rev="${org.eclipse.osgi}" conf="compile->compile"/>
+ <dependency org="javax.servlet" name="com.springsource.javax.servlet" rev="${javax.servlet}" conf="compile->runtime"/>
+ <dependency org="org.junit" name="com.springsource.org.junit" rev="${org.junit}" conf="test->runtime"/>
+ <dependency org="org.slf4j" name="com.springsource.slf4j.api" rev="${org.slf4j}" conf="compile->runtime"/>
+ <dependency org="org.slf4j" name="com.springsource.slf4j.nop" rev="${org.slf4j}" conf="test->runtime"/>
+ <dependency org="org.easymock" name="com.springsource.org.easymock" rev="${org.easymock}" conf="test->runtime"/>
+ <dependency org="org.eclipse.virgo.test" name="org.eclipse.virgo.test.framework" rev="${org.eclipse.virgo.test}"/>
+ <dependency org="org.eclipse.gemini.web" name="org.eclipse.gemini.web.core" rev="latest.integration"/>
+ <dependency org="org.eclipse.gemini.web" name="org.eclipse.gemini.web.tomcat" rev="latest.integration"/>
+
+ <dependency org="org.springframework" name="org.springframework.web" rev="${org.springframework}" conf="test->runtime"/>
+
+ <dependency org="javax.activation" name="com.springsource.javax.activation" rev="${javax.activation}" />
+
+ <dependency org="org.apache.taglibs" name="com.springsource.org.apache.taglibs.standard" rev="1.1.2" conf="test->runtime"/>
+ <dependency org="javax.servlet" name="com.springsource.javax.servlet.jsp.jstl" rev="1.2.0" conf="test->runtime"/>
+
+ </dependencies>
+
+</ivy-module>
diff --git a/org.eclipse.gemini.web.test/src/main/java/.gitignore b/org.eclipse.gemini.web.test/src/main/java/.gitignore
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/org.eclipse.gemini.web.test/src/main/java/.gitignore
diff --git a/org.eclipse.gemini.web.test/src/main/resources/.gitignore b/org.eclipse.gemini.web.test/src/main/resources/.gitignore
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/org.eclipse.gemini.web.test/src/main/resources/.gitignore
diff --git a/org.eclipse.gemini.web.test/src/test/java/org/eclipse/gemini/web/test/WarInstallationTests.java b/org.eclipse.gemini.web.test/src/test/java/org/eclipse/gemini/web/test/WarInstallationTests.java
new file mode 100644
index 0000000..ae34519
--- /dev/null
+++ b/org.eclipse.gemini.web.test/src/test/java/org/eclipse/gemini/web/test/WarInstallationTests.java
@@ -0,0 +1,50 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 VMware Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Apache License v2.0 which accompanies this distribution.
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Apache License v2.0 is available at
+ * http://www.opensource.org/licenses/apache2.0.php.
+ * You may elect to redistribute this code under either of these licenses.
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.gemini.web.test;
+
+import static org.junit.Assert.assertNotNull;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleException;
+
+import org.eclipse.virgo.test.framework.OsgiTestRunner;
+import org.eclipse.virgo.test.framework.TestFrameworkUtils;
+
+@RunWith(OsgiTestRunner.class)
+public class WarInstallationTests {
+
+ private BundleContext bundleContext;
+
+ @Before
+ public void before() {
+ this.bundleContext = TestFrameworkUtils.getBundleContextForTestClass(getClass());
+ }
+
+ @Test
+ public void testInstallSimpleWar() throws BundleException {
+ String location = "webbundle:file:../org.eclipse.gemini.web.core/src/test/resources/simple-war.war?Web-ContextPath=/";
+
+ Bundle bundle = this.bundleContext.installBundle(location);
+ assertNotNull(bundle);
+ assertNotNull(bundle.getSymbolicName());
+ bundle.uninstall();
+ }
+}
diff --git a/org.eclipse.gemini.web.test/src/test/java/org/eclipse/gemini/web/test/extender/ClassPathDependencyTests.java b/org.eclipse.gemini.web.test/src/test/java/org/eclipse/gemini/web/test/extender/ClassPathDependencyTests.java
new file mode 100644
index 0000000..a6075f4
--- /dev/null
+++ b/org.eclipse.gemini.web.test/src/test/java/org/eclipse/gemini/web/test/extender/ClassPathDependencyTests.java
@@ -0,0 +1,97 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 VMware Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Apache License v2.0 which accompanies this distribution.
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Apache License v2.0 is available at
+ * http://www.opensource.org/licenses/apache2.0.php.
+ * You may elect to redistribute this code under either of these licenses.
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.gemini.web.test.extender;
+
+import junit.framework.Assert;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleException;
+
+import org.eclipse.virgo.test.framework.OsgiTestRunner;
+import org.eclipse.virgo.test.framework.TestFrameworkUtils;
+
+@RunWith(OsgiTestRunner.class)
+public class ClassPathDependencyTests {
+
+ private BundleContext context;
+
+ private Bundle extender;
+
+ private Bundle war;
+
+ @Before
+ public void before() throws BundleException {
+ this.context = TestFrameworkUtils.getBundleContextForTestClass(getClass());
+ this.extender = installExtender();
+ this.extender.start();
+ this.war = null;
+ }
+
+ @After
+ public void after() throws BundleException {
+ if (this.war != null) {
+ uninstallWar();
+ }
+ if (this.extender != null) {
+ this.extender.uninstall();
+ this.extender = null;
+ }
+ }
+
+ @Test
+ public void testWarWithClassPathDependencies() throws BundleException, Exception {
+ deployWar();
+ checkWarBundleClassPath();
+ }
+
+ private void deployWar() throws BundleException {
+ this.war = this.context.installBundle("webbundle:file:src/test/resources/classpathdeps.war?Web-ContextPath=/classpathdeps");
+ this.war.start();
+ }
+
+ private void checkWarBundleClassPath() {
+ String bundleClassPath = (String) this.war.getHeaders().get("Bundle-ClassPath");
+ Assert.assertEquals("WEB-INF/classes,WEB-INF/lib/jar1.jar,j2/jar2.jar,j3/jar3.jar,j4/jar4.jar", bundleClassPath);
+ }
+
+ @Test
+ public void testWarWithCyclicClassPathDependencies() throws BundleException, Exception {
+ deployCyclicWar();
+ checkWarBundleClassPath();
+ }
+
+ private void deployCyclicWar() throws BundleException {
+ this.war = this.context.installBundle("webbundle:file:src/test/resources/cyclicclasspathdeps.war?Web-ContextPath=/cyclicclasspathdeps");
+ this.war.start();
+ }
+
+
+ private void uninstallWar() throws BundleException {
+ this.war.uninstall();
+ this.war = null;
+ }
+
+ private Bundle installExtender() throws BundleException {
+ return this.context.installBundle("file:../org.eclipse.gemini.web.extender/target/classes");
+ }
+
+}
diff --git a/org.eclipse.gemini.web.test/src/test/java/org/eclipse/gemini/web/test/extender/OverlappingWebContextPathsTests.java b/org.eclipse.gemini.web.test/src/test/java/org/eclipse/gemini/web/test/extender/OverlappingWebContextPathsTests.java
new file mode 100644
index 0000000..5c8af28
--- /dev/null
+++ b/org.eclipse.gemini.web.test/src/test/java/org/eclipse/gemini/web/test/extender/OverlappingWebContextPathsTests.java
@@ -0,0 +1,147 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 VMware Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Apache License v2.0 which accompanies this distribution.
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Apache License v2.0 is available at
+ * http://www.opensource.org/licenses/apache2.0.php.
+ * You may elect to redistribute this code under either of these licenses.
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.gemini.web.test.extender;
+
+import static org.junit.Assert.assertNotNull;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.MalformedURLException;
+import java.net.URL;
+
+import junit.framework.Assert;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleException;
+
+import org.eclipse.virgo.test.framework.OsgiTestRunner;
+import org.eclipse.virgo.test.framework.TestFrameworkUtils;
+
+@RunWith(OsgiTestRunner.class)
+public class OverlappingWebContextPathsTests {
+
+ private BundleContext context;
+
+ private Bundle extender;
+
+ private Bundle war1;
+
+ private Bundle war2;
+
+ @Before
+ public void before() throws BundleException {
+ this.context = TestFrameworkUtils.getBundleContextForTestClass(getClass());
+ this.extender = installExtender();
+ this.extender.start();
+ this.war1 = null;
+ this.war2 = null;
+ }
+
+ @After
+ public void after() throws BundleException {
+ if (this.war1 != null) {
+ uninstallWar1();
+ }
+ if (this.war2 != null) {
+ uninstallWar2();
+ }
+ if (this.extender != null) {
+ this.extender.uninstall();
+ this.extender = null;
+ }
+ }
+
+ @Test
+ public void testWarWithAGivenWebContextPathOverridesLaterWarWithTheSamePath() throws BundleException, Exception {
+ deployWar1();
+ checkWar1Responding();
+ deployWar2();
+ checkWar1Responding();
+ }
+
+ @Test
+ public void testLaterWarWithOverlappingWebContextPathRespondsWhenEarlierWarIsStoppedAndLaterWarIsUpdated() throws BundleException, Exception {
+ deployWar1();
+ checkWar1Responding();
+ deployWar2();
+ this.war1.stop();
+ checkWar2Responding();
+ }
+
+ private void deployWar1() throws BundleException {
+ this.war1 = this.context.installBundle("webbundle:file:src/test/resources/war-with-servlet.war?Web-ContextPath=/overlap");
+ this.war1.start();
+ }
+
+ private void deployWar2() throws BundleException {
+ this.war2 = this.context.installBundle("webbundle:file:src/test/resources/war-with-another-servlet.war?Web-ContextPath=/overlap");
+ this.war2.start();
+ }
+
+ private void uninstallWar1() throws BundleException {
+ this.war1.uninstall();
+ this.war1 = null;
+ }
+
+ private void uninstallWar2() throws BundleException {
+ this.war2.uninstall();
+ this.war2 = null;
+ }
+
+ private void checkWar1Responding() throws MalformedURLException, IOException, InterruptedException {
+ validateURL("http://localhost:8080/overlap/test", "Hello World!");
+ }
+
+ private void checkWar2Responding() throws MalformedURLException, IOException, InterruptedException {
+ validateURL("http://localhost:8080/overlap/test", "Another");
+ }
+
+ private Bundle installExtender() throws BundleException {
+ return this.context.installBundle("file:../org.eclipse.gemini.web.extender/target/classes");
+ }
+
+ private void validateURL(String path, String expectedResponse) throws MalformedURLException, IOException, InterruptedException {
+ BufferedReader reader = new BufferedReader(new InputStreamReader(openInputStream(path)));
+ try {
+ Assert.assertEquals(expectedResponse, reader.readLine());
+ } finally {
+ reader.close();
+ }
+ }
+
+ private InputStream openInputStream(String path) throws MalformedURLException, InterruptedException {
+ URL url = new URL(path);
+ InputStream stream = null;
+ for (int i = 0; i < 5; i++) {
+ try {
+ stream = url.openConnection().getInputStream();
+ } catch (IOException e) {
+ Thread.sleep(1000);
+ }
+ }
+ assertNotNull(stream);
+ return stream;
+ }
+
+}
diff --git a/org.eclipse.gemini.web.test/src/test/java/org/eclipse/gemini/web/test/extender/StreamBasedExtenderTests.java b/org.eclipse.gemini.web.test/src/test/java/org/eclipse/gemini/web/test/extender/StreamBasedExtenderTests.java
new file mode 100644
index 0000000..45f0bd8
--- /dev/null
+++ b/org.eclipse.gemini.web.test/src/test/java/org/eclipse/gemini/web/test/extender/StreamBasedExtenderTests.java
@@ -0,0 +1,379 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 VMware Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Apache License v2.0 which accompanies this distribution.
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Apache License v2.0 is available at
+ * http://www.opensource.org/licenses/apache2.0.php.
+ * You may elect to redistribute this code under either of these licenses.
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.gemini.web.test.extender;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.fail;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.MalformedURLException;
+import java.net.URL;
+
+import javax.servlet.ServletContext;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.ServiceReference;
+
+import org.eclipse.virgo.test.framework.OsgiTestRunner;
+import org.eclipse.virgo.test.framework.TestFrameworkUtils;
+
+@RunWith(OsgiTestRunner.class)
+public class StreamBasedExtenderTests {
+
+ private BundleContext context;
+
+ @Before
+ public void before() {
+ this.context = TestFrameworkUtils.getBundleContextForTestClass(getClass());
+ }
+
+ @Test
+ public void testInstallAfterExtender() throws BundleException, Exception {
+ Bundle extender = installExtender();
+ Bundle war = null;
+ try {
+ extender.start();
+
+ war = installWarBundle();
+ war.start();
+
+ validateURL("http://localhost:8080/simple-war/index.html");
+
+ war.stop();
+
+ validateNotFound("http://localhost:8080/simple-war/index.html");
+ } finally {
+ extender.uninstall();
+ if (war != null) {
+ war.uninstall();
+ }
+ }
+ }
+
+ @Test
+ public void testInstallBeforeExtender() throws BundleException, Exception {
+ Bundle war = installWarBundle();
+ war.start();
+
+ Bundle extender = installExtender();
+ try {
+ extender.start();
+
+ validateURL("http://localhost:8080/simple-war/index.html");
+
+ war.stop();
+
+ validateNotFound("http://localhost:8080/simple-war/index.html");
+ } finally {
+ extender.uninstall();
+ war.uninstall();
+ }
+ }
+
+ @Test
+ public void installWithCustomContextPath() throws BundleException, Exception {
+ Bundle war = null;
+ Bundle extender = installExtender();
+ try {
+ extender.start();
+
+ war = installWarBundle("?Web-ContextPath=/custom");
+ war.start();
+
+ validateURL("http://localhost:8080/custom/index.html");
+
+ war.stop();
+
+ validateNotFound("http://localhost:8080/custom/index.html");
+ } finally {
+ extender.uninstall();
+ if (war != null) {
+ war.uninstall();
+ }
+ }
+ }
+
+ @Test
+ public void installWithContextPathSpecifiedInManifest() throws BundleException, Exception {
+ Bundle war = null;
+ Bundle extender = installExtender();
+ try {
+ extender.start();
+
+ war = this.context.installBundle("webbundle:file:src/test/resources/specified-context-path-1.war");
+ war.start();
+
+ validateURL("http://localhost:8080/specified/test");
+
+ war.stop();
+
+ validateNotFound("http://localhost:8080/specified/test");
+ } finally {
+ extender.uninstall();
+ if (war != null) {
+ war.uninstall();
+ }
+ }
+ }
+
+ @Test
+ public void installWithDuplicateContextRoots() throws BundleException, Exception {
+ Bundle war1 = null;
+ Bundle war2 = null;
+
+ Bundle extender = installExtender();
+
+ try {
+ extender.start();
+
+ war1 = this.context.installBundle("webbundle:file:src/test/resources/specified-context-path-1.war");
+ war1.start();
+
+ validateURL("http://localhost:8080/specified/test");
+
+ ServiceReference[] serviceReferences = this.context.getServiceReferences(ServletContext.class.getName(), null);
+ assertNotNull(serviceReferences);
+ assertEquals(1, serviceReferences.length);
+
+ war2 = this.context.installBundle("webbundle:file:src/test/resources/specified-context-path-2.war");
+ war2.start();
+
+ serviceReferences = this.context.getServiceReferences(ServletContext.class.getName(), null);
+ assertNotNull(serviceReferences);
+ assertEquals(1, serviceReferences.length);
+
+ war2.stop();
+ war2.uninstall();
+
+ validateURL("http://localhost:8080/specified/test");
+
+ serviceReferences = this.context.getServiceReferences(ServletContext.class.getName(), null);
+ assertNotNull(serviceReferences);
+ assertEquals(1, serviceReferences.length);
+
+ war1.stop();
+
+ validateNotFound("http://localhost:8080/specified/test");
+
+ serviceReferences = this.context.getServiceReferences(ServletContext.class.getName(), null);
+ assertNull(serviceReferences);
+ } finally {
+ extender.uninstall();
+ if (war1 != null) {
+ war1.uninstall();
+ }
+ }
+ }
+
+ @Test
+ public void installWarWithNoManifest() throws BundleException, Exception {
+ Bundle war = null;
+ Bundle extender = installExtender();
+ try {
+ extender.start();
+
+ war = this.context.installBundle("webbundle:file:src/test/resources/no-manifest.war?Web-ContextPath=/no-manifest");
+ war.start();
+
+ validateURL("http://localhost:8080/no-manifest/test");
+
+ war.stop();
+
+ validateNotFound("http://localhost:8080/no-manifest/test");
+ } finally {
+ extender.uninstall();
+ if (war != null) {
+ war.uninstall();
+ }
+ }
+ }
+
+ /**
+ * This test expects IllegalArgumentException rather than BundleException because for stream based installation the exception is thrown
+ * when the stream is opened and BundleException is not a valid exception for URL.openStream.
+ * @throws BundleException but shouldn't
+ * @throws Exception possibly
+ */
+ @Test(expected = IllegalArgumentException.class)
+ public void installWithInvalidBundleVersion() throws BundleException, Exception {
+ Bundle extender = installExtender();
+ try {
+ extender.start();
+
+ installWarBundle("?Bundle-Version=1.2.3.a - b");
+ } finally {
+ extender.uninstall();
+ }
+ }
+
+ /**
+ * This test expects IllegalArgumentException rather than BundleException because for stream based installation the exception is thrown
+ * when the stream is opened and BundleException is not a valid exception for URL.openStream.
+ * @throws BundleException but shouldn't
+ * @throws Exception possibly
+ */
+ @Test(expected = IllegalArgumentException.class)
+ public void installWithInvalidBundleManifestVersionx() throws BundleException, Exception {
+ Bundle extender = installExtender();
+ try {
+ extender.start();
+
+ installWarBundle("?Bundle-ManifestVersion=x");
+ } finally {
+ extender.uninstall();
+ }
+ }
+
+ /**
+ * This test expects IllegalArgumentException rather than BundleException because for stream based installation the exception is thrown
+ * when the stream is opened and BundleException is not a valid exception for URL.openStream.
+ * @throws BundleException but shouldn't
+ * @throws Exception possibly
+ */
+ @Test(expected = IllegalArgumentException.class)
+ public void installWithInvalidBundleManifestVersion0() throws BundleException, Exception {
+ Bundle extender = installExtender();
+ try {
+ extender.start();
+
+ installWarBundle("?Bundle-ManifestVersion=0");
+ } finally {
+ extender.uninstall();
+ }
+ }
+
+ /**
+ * This test expects IllegalArgumentException rather than BundleException because for stream based installation the exception is thrown
+ * when the stream is opened and BundleException is not a valid exception for URL.openStream.
+ * @throws BundleException but shouldn't
+ * @throws Exception possibly
+ */
+ @Test(expected = IllegalArgumentException.class)
+ public void installWithInvalidBundleManifestVersion1() throws BundleException, Exception {
+ Bundle extender = installExtender();
+ try {
+ extender.start();
+
+ installWarBundle("?Bundle-ManifestVersion=1");
+ } finally {
+ extender.uninstall();
+ }
+ }
+
+ @Test
+ public void installWithBundleManifestVersion2() throws BundleException, Exception {
+ Bundle war = null;
+ Bundle extender = installExtender();
+ try {
+ extender.start();
+
+ war = installWarBundle("?Bundle-ManifestVersion=2&Web-ContextPath=/simple-war");
+
+ war.start();
+
+ validateURL("http://localhost:8080/simple-war/index.html");
+
+ war.stop();
+
+ validateNotFound("http://localhost:8080/simple-war/index.html");
+ } finally {
+ extender.uninstall();
+ if (war != null) {
+ war.uninstall();
+ }
+ }
+ }
+
+
+ private Bundle installExtender() throws BundleException {
+ return this.context.installBundle("file:../org.eclipse.gemini.web.extender/target/classes");
+ }
+
+ private Bundle installWarBundle() throws BundleException {
+ return installWarBundle("?Web-ContextPath=/simple-war");
+ }
+
+ private Bundle installWarBundle(String suffix) throws BundleException {
+ InputStream in = null;
+ try {
+ URL url = new URL("webbundle:file:../org.eclipse.gemini.web.core/src/test/resources/simple-war.war" + suffix);
+ in = url.openStream();
+ } catch (MalformedURLException e) {
+ fail("Unexpected exception " + e.getMessage());
+ } catch (IOException e) {
+ fail("Unexpected exception " + e.getMessage());
+ }
+
+ try {
+ return this.context.installBundle("simple-war.war", in);
+ } finally {
+ if (in != null) {
+ try {
+ in.close();
+ } catch (IOException e) {
+ }
+ }
+ }
+ }
+
+ private void validateURL(String path) throws MalformedURLException, IOException, InterruptedException {
+ InputStream stream = openInputStream(path);
+ assertNotNull(stream);
+ BufferedReader reader = new BufferedReader(new InputStreamReader(stream));
+ try {
+ String line = null;
+ while ((line = reader.readLine()) != null) {
+ System.out.println(line);
+ }
+ } finally {
+ reader.close();
+ }
+ }
+
+ private void validateNotFound(String path) throws Exception {
+ URL url = new URL(path);
+ try {
+ InputStream stream = url.openConnection().getInputStream();
+ stream.close();
+ fail("URL '" + path + "' is still deployed");
+ } catch (IOException e) {
+ }
+ }
+
+ private InputStream openInputStream(String path) throws MalformedURLException, InterruptedException {
+ URL url = new URL(path);
+ InputStream stream = null;
+ for (int i = 0; i < 5; i++) {
+ try {
+ stream = url.openConnection().getInputStream();
+ } catch (IOException e) {
+ Thread.sleep(1000);
+ }
+ }
+ return stream;
+ }
+}
diff --git a/org.eclipse.gemini.web.test/src/test/java/org/eclipse/gemini/web/test/extender/UrlBasedExtenderTests.java b/org.eclipse.gemini.web.test/src/test/java/org/eclipse/gemini/web/test/extender/UrlBasedExtenderTests.java
new file mode 100644
index 0000000..68ccbce
--- /dev/null
+++ b/org.eclipse.gemini.web.test/src/test/java/org/eclipse/gemini/web/test/extender/UrlBasedExtenderTests.java
@@ -0,0 +1,396 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 VMware Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Apache License v2.0 which accompanies this distribution.
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Apache License v2.0 is available at
+ * http://www.opensource.org/licenses/apache2.0.php.
+ * You may elect to redistribute this code under either of these licenses.
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.gemini.web.test.extender;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.MalformedURLException;
+import java.net.URL;
+
+import javax.servlet.ServletContext;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.ServiceReference;
+
+import org.eclipse.virgo.test.framework.OsgiTestRunner;
+import org.eclipse.virgo.test.framework.TestFrameworkUtils;
+
+@RunWith(OsgiTestRunner.class)
+public class UrlBasedExtenderTests {
+
+ private static final String SIMPLE_WAR_WEB_BUNDLE_URL = "webbundle:file:../org.eclipse.gemini.web.core/src/test/resources/simple-war.war";
+
+ private static final String EMPTY_WAR_WEB_BUNDLE_URL = "webbundle:file:src/test/resources/empty.war";
+
+ private BundleContext context;
+
+ @Before
+ public void before() {
+ this.context = TestFrameworkUtils.getBundleContextForTestClass(getClass());
+ }
+
+ @Test
+ public void testInstallAfterExtender() throws BundleException, Exception {
+ Bundle extender = installExtender();
+ Bundle war = null;
+ try {
+ extender.start();
+
+ war = installWarBundle();
+ war.start();
+
+ validateURL("http://localhost:8080/simple-war/index.html");
+
+ war.stop();
+
+ validateNotFound("http://localhost:8080/simple-war/index.html");
+ } finally {
+ extender.uninstall();
+ if (war != null) {
+ war.uninstall();
+ }
+ }
+ }
+
+ @Test
+ public void testInstallBeforeExtender() throws BundleException, Exception {
+ Bundle war = installWarBundle();
+ war.start();
+
+ Bundle extender = installExtender();
+ try {
+ extender.start();
+
+ validateURL("http://localhost:8080/simple-war/index.html");
+
+ war.stop();
+
+ validateNotFound("http://localhost:8080/simple-war/index.html");
+ } finally {
+ extender.uninstall();
+ war.uninstall();
+ }
+ }
+
+ @Test
+ public void installWithCustomContextPath() throws BundleException, Exception {
+ Bundle war = null;
+ Bundle extender = installExtender();
+ try {
+ extender.start();
+
+ war = installWarBundle("?Web-ContextPath=/custom");
+ war.start();
+
+ validateURL("http://localhost:8080/custom/index.html");
+
+ war.stop();
+
+ validateNotFound("http://localhost:8080/custom/index.html");
+ } finally {
+ extender.uninstall();
+ if (war != null) {
+ war.uninstall();
+ }
+ }
+ }
+
+ @Test
+ public void installWithContextPathSpecifiedInManifest() throws BundleException, Exception {
+ Bundle war = null;
+ Bundle extender = installExtender();
+ try {
+ extender.start();
+
+ war = this.context.installBundle("webbundle:file:src/test/resources/specified-context-path-1.war");
+ war.start();
+
+ validateURL("http://localhost:8080/specified/test");
+
+ war.stop();
+
+ validateNotFound("http://localhost:8080/specified/test");
+ } finally {
+ extender.uninstall();
+ if (war != null) {
+ war.uninstall();
+ }
+ }
+ }
+
+ @Test
+ public void installWithDuplicateContextRoots() throws BundleException, Exception {
+ Bundle war1 = null;
+ Bundle war2 = null;
+
+ Bundle extender = installExtender();
+
+ try {
+ extender.start();
+
+ war1 = this.context.installBundle("webbundle:file:src/test/resources/specified-context-path-1.war");
+ war1.start();
+
+ validateURL("http://localhost:8080/specified/test");
+
+ ServiceReference[] serviceReferences = this.context.getServiceReferences(ServletContext.class.getName(), null);
+ assertNotNull(serviceReferences);
+ assertEquals(1, serviceReferences.length);
+
+ war2 = this.context.installBundle("webbundle:file:src/test/resources/specified-context-path-2.war");
+ war2.start();
+
+ serviceReferences = this.context.getServiceReferences(ServletContext.class.getName(), null);
+ assertNotNull(serviceReferences);
+ assertEquals(1, serviceReferences.length);
+
+ war2.stop();
+ war2.uninstall();
+
+ validateURL("http://localhost:8080/specified/test");
+
+ serviceReferences = this.context.getServiceReferences(ServletContext.class.getName(), null);
+ assertNotNull(serviceReferences);
+ assertEquals(1, serviceReferences.length);
+
+ war1.stop();
+
+ validateNotFound("http://localhost:8080/specified/test");
+
+ serviceReferences = this.context.getServiceReferences(ServletContext.class.getName(), null);
+ assertNull(serviceReferences);
+ } finally {
+ extender.uninstall();
+ if (war1 != null) {
+ war1.uninstall();
+ }
+ }
+ }
+
+ @Test
+ public void installWarWithNoManifest() throws BundleException, Exception {
+ Bundle war = null;
+ Bundle extender = installExtender();
+ try {
+ extender.start();
+
+ war = this.context.installBundle("webbundle:file:src/test/resources/no-manifest.war?Web-ContextPath=/no-manifest");
+ war.start();
+
+ validateURL("http://localhost:8080/no-manifest/test");
+
+ war.stop();
+
+ validateNotFound("http://localhost:8080/no-manifest/test");
+ } finally {
+ extender.uninstall();
+ if (war != null) {
+ war.uninstall();
+ }
+ }
+ }
+
+ @Test
+ public void installWithBundleClassPath() throws BundleException, Exception {
+ Bundle war = null;
+ Bundle extender = installExtender();
+ try {
+ extender.start();
+
+ war = installBundle(EMPTY_WAR_WEB_BUNDLE_URL, "?Bundle-ClassPath=WEB-INF/classes/&Web-ContextPath=/");
+
+ String bundleClassPath = (String) war.getHeaders().get("Bundle-ClassPath");
+ assertEquals("WEB-INF/classes", bundleClassPath);
+ } finally {
+ extender.uninstall();
+ if (war != null) {
+ war.uninstall();
+ }
+ }
+ }
+
+ @Test
+ public void installWithImportPackage() throws BundleException, Exception {
+ Bundle war = null;
+ Bundle extender = installExtender();
+ try {
+ extender.start();
+
+ war = installBundle(EMPTY_WAR_WEB_BUNDLE_URL,
+ "?Import-Package=javax.servlet;version=2.5,javax.servlet.http;version=2.5&Web-ContextPath=/");
+
+ String importPackage = (String) war.getHeaders().get("Import-Package");
+ assertTrue(importPackage.startsWith("javax.servlet;version=\"2.5\",javax.servlet.http;version=\"2.5\""));
+ } finally {
+ extender.uninstall();
+ if (war != null) {
+ war.uninstall();
+ }
+ }
+ }
+
+ @Test(expected = BundleException.class)
+ public void installWithInvalidBundleVersion() throws BundleException, Exception {
+ Bundle extender = installExtender();
+ try {
+ extender.start();
+
+ installWarBundle("?Bundle-Version=1.2.3.a - b");
+ } finally {
+ extender.uninstall();
+ }
+ }
+
+ @Test(expected = BundleException.class)
+ public void installWithInvalidBundleManifestVersionx() throws BundleException, Exception {
+ Bundle extender = installExtender();
+ try {
+ extender.start();
+
+ installWarBundle("?Bundle-ManifestVersion=x");
+ } finally {
+ extender.uninstall();
+ }
+ }
+
+ @Test(expected = BundleException.class)
+ public void installWithInvalidBundleManifestVersion0() throws BundleException, Exception {
+ Bundle extender = installExtender();
+ try {
+ extender.start();
+
+ installWarBundle("?Bundle-ManifestVersion=0");
+ } finally {
+ extender.uninstall();
+ }
+ }
+
+ @Test(expected = BundleException.class)
+ public void installWithBadParameter() throws BundleException, Exception {
+ Bundle extender = installExtender();
+ try {
+ extender.start();
+
+ installWarBundle("?Bundle-ManifestVersion");
+ } finally {
+ extender.uninstall();
+ }
+ }
+
+ @Test(expected = BundleException.class)
+ public void installWithInvalidBundleManifestVersion1() throws BundleException, Exception {
+ Bundle extender = installExtender();
+ try {
+ extender.start();
+
+ installWarBundle("?Bundle-ManifestVersion=1");
+ } finally {
+ extender.uninstall();
+ }
+ }
+
+ @Test
+ public void installWithBundleManifestVersion2() throws BundleException, Exception {
+ Bundle war = null;
+ Bundle extender = installExtender();
+ try {
+ extender.start();
+
+ war = installWarBundle("?Bundle-ManifestVersion=2&Web-ContextPath=/simple-war");
+
+ war.start();
+
+ validateURL("http://localhost:8080/simple-war/index.html");
+
+ war.stop();
+
+ validateNotFound("http://localhost:8080/simple-war/index.html");
+ } finally {
+ extender.uninstall();
+ if (war != null) {
+ war.uninstall();
+ }
+ }
+ }
+
+ private Bundle installExtender() throws BundleException {
+ return this.context.installBundle("file:../org.eclipse.gemini.web.extender/target/classes");
+ }
+
+ private Bundle installWarBundle() throws BundleException {
+ return installWarBundle("?Web-ContextPath=/simple-war");
+ }
+
+ private Bundle installWarBundle(String suffix) throws BundleException {
+ return installBundle(SIMPLE_WAR_WEB_BUNDLE_URL, suffix);
+ }
+
+ private Bundle installBundle(String bundleUrl, String suffix) throws BundleException {
+ return this.context.installBundle(bundleUrl + suffix);
+ }
+
+ private void validateURL(String path) throws MalformedURLException, IOException, InterruptedException {
+ InputStream stream = openInputStream(path);
+ assertNotNull(stream);
+ BufferedReader reader = new BufferedReader(new InputStreamReader(stream));
+ try {
+ String line = null;
+ while ((line = reader.readLine()) != null) {
+ System.out.println(line);
+ }
+ } finally {
+ reader.close();
+ }
+ }
+
+ private void validateNotFound(String path) throws Exception {
+ URL url = new URL(path);
+ try {
+ InputStream stream = url.openConnection().getInputStream();
+ stream.close();
+ fail("URL '" + path + "' is still deployed");
+ } catch (IOException e) {
+ }
+ }
+
+ private InputStream openInputStream(String path) throws MalformedURLException, InterruptedException {
+ URL url = new URL(path);
+ InputStream stream = null;
+ for (int i = 0; i < 5; i++) {
+ try {
+ stream = url.openConnection().getInputStream();
+ } catch (IOException e) {
+ Thread.sleep(1000);
+ }
+ }
+ return stream;
+ }
+
+}
diff --git a/org.eclipse.gemini.web.test/src/test/java/org/eclipse/gemini/web/test/tomcat/TomcatServletContainerTests.java b/org.eclipse.gemini.web.test/src/test/java/org/eclipse/gemini/web/test/tomcat/TomcatServletContainerTests.java
new file mode 100644
index 0000000..e5d0c6e
--- /dev/null
+++ b/org.eclipse.gemini.web.test/src/test/java/org/eclipse/gemini/web/test/tomcat/TomcatServletContainerTests.java
@@ -0,0 +1,341 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 VMware Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Apache License v2.0 which accompanies this distribution.
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Apache License v2.0 is available at
+ * http://www.opensource.org/licenses/apache2.0.php.
+ * You may elect to redistribute this code under either of these licenses.
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.gemini.web.test.tomcat;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.MalformedURLException;
+import java.net.Socket;
+import java.net.URL;
+import java.net.URLConnection;
+import java.net.UnknownHostException;
+import java.util.Set;
+
+import javax.servlet.ServletContext;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.ServiceReference;
+
+import org.eclipse.gemini.web.core.spi.ServletContainer;
+import org.eclipse.gemini.web.core.spi.WebApplicationHandle;
+import org.eclipse.virgo.test.framework.OsgiTestRunner;
+import org.eclipse.virgo.test.framework.TestFrameworkUtils;
+import org.eclipse.virgo.util.io.PathReference;
+import org.eclipse.virgo.util.io.ZipUtils;
+
+@RunWith(OsgiTestRunner.class)
+public class TomcatServletContainerTests {
+
+ private static final String PATH_WAR_WITH_TLD_WAR = "../org.eclipse.gemini.web.test/src/test/resources/war-with-tld.war?Web-ContextPath=/war-with-tld";
+
+ private static final String PATH_WAR_WITH_SERVLET = "../org.eclipse.gemini.web.test/src/test/resources/war-with-servlet.war?Web-ContextPath=/war-with-servlet";
+
+ private static final String LOCATION_PREFIX = "webbundle:file:";
+
+ private static final String LOCATION_SIMPLE_WAR = LOCATION_PREFIX
+ + "../org.eclipse.gemini.web.core/src/test/resources/simple-war.war?Web-ContextPath=/simple-war";
+
+ private static final String LOCATION_WAR_WITH_SERVLET = "webbundle:file:" + PATH_WAR_WITH_SERVLET;
+
+ private static final String LOCATION_WAR_WITH_JSP = LOCATION_PREFIX
+ + "../org.eclipse.gemini.web.test/src/test/resources/war-with-jsp.war?Web-ContextPath=/war-with-jsp";
+
+ private static final String LOCATION_WAR_WITH_TLD = LOCATION_PREFIX + PATH_WAR_WITH_TLD_WAR;
+
+ private static final String LOCATION_WAR_WITH_TLD_FROM_DEPENDENCY = LOCATION_PREFIX
+ + "../org.eclipse.gemini.web.test/src/test/resources/war-with-tld-from-dependency.war?Web-ContextPath=/war-with-tld-from-dependency";
+
+ private static final String LOCATION_WAR_WITH_TLD_IMPORT_SYSTEM_PACKAGES = LOCATION_PREFIX
+ + "../org.eclipse.gemini.web.test/src/test/resources/war-with-tld-import-system-packages.war?Web-ContextPath=/war-with-tld-import-system-packages";
+
+ private BundleContext bundleContext;
+
+ private ServletContainer container;
+
+ @Before
+ public void before() throws Exception {
+ this.bundleContext = TestFrameworkUtils.getBundleContextForTestClass(getClass());
+ ServiceReference ref = bundleContext.getServiceReference(ServletContainer.class.getName());
+ this.container = (ServletContainer) bundleContext.getService(ref);
+ }
+
+ @Test
+ public void testServletContainerAvailable() {
+ assertNotNull(this.container);
+ try {
+ new Socket("localhost", 8080);
+ } catch (UnknownHostException e) {
+ fail("Unable to connect");
+ } catch (IOException e) {
+ fail("Unable to connect");
+ }
+ }
+
+ @Test
+ public void testInstallSimpleWar() throws Exception {
+ String location = LOCATION_SIMPLE_WAR;
+ Bundle bundle = this.bundleContext.installBundle(location);
+ bundle.start();
+
+ validateNotFound("http://localhost:8080/test/index.html");
+
+ WebApplicationHandle handle = this.container.createWebApplication("/test", bundle);
+ this.container.startWebApplication(handle);
+ assertNotNull(handle);
+
+ validateURL("http://localhost:8080/test/index.html");
+
+ this.container.stopWebApplication(handle);
+
+ validateNotFound("http://localhost:8080/test/index.html");
+
+ }
+
+ @Test
+ public void testWarWithServlet() throws Exception {
+ String location = LOCATION_WAR_WITH_SERVLET;
+ Bundle bundle = this.bundleContext.installBundle(location);
+ bundle.start();
+
+ WebApplicationHandle handle = this.container.createWebApplication("/war-with-servlet", bundle);
+ this.container.startWebApplication(handle);
+ try {
+ validateURL("http://localhost:8080/war-with-servlet/test");
+ } finally {
+ this.container.stopWebApplication(handle);
+ }
+ }
+
+ @Test
+ public void testWarWithBasicJSP() throws Exception {
+ String location = LOCATION_WAR_WITH_JSP;
+ Bundle bundle = this.bundleContext.installBundle(location);
+ bundle.start();
+
+ WebApplicationHandle handle = this.container.createWebApplication("/war-with-jsp", bundle);
+ this.container.startWebApplication(handle);
+ try {
+ validateURL("http://localhost:8080/war-with-jsp/index.jsp");
+ } finally {
+ this.container.stopWebApplication(handle);
+ }
+ }
+
+ @Test
+ public void testWarWithJSTL() throws Exception {
+ testWarWithJSTL("");
+ }
+
+ private void testWarWithJSTL(String addtionalUrlSuffix) throws MalformedURLException, IOException, BundleException {
+ String location = LOCATION_WAR_WITH_TLD + addtionalUrlSuffix;
+ Bundle bundle = this.bundleContext.installBundle(location);
+ bundle.start();
+
+ WebApplicationHandle handle = this.container.createWebApplication("/war-with-tld", bundle);
+ this.container.startWebApplication(handle);
+ try {
+ String realPath = handle.getServletContext().getRealPath("/");
+ System.out.println(realPath);
+ validateURL("http://localhost:8080/war-with-tld/test.jsp");
+ } finally {
+ this.container.stopWebApplication(handle);
+ bundle.uninstall();
+ }
+ }
+
+ @Test
+ public void testWarWithJSTLFromDependency() throws MalformedURLException, IOException, BundleException {
+ String jstlLocation = "file:../ivy-cache/repository/javax.servlet/com.springsource.javax.servlet.jsp.jstl/1.2.0/com.springsource.javax.servlet.jsp.jstl-1.2.0.jar";
+ Bundle jstlBundle = this.bundleContext.installBundle(jstlLocation);
+
+ Bundle bundle = this.bundleContext.installBundle(LOCATION_WAR_WITH_TLD_FROM_DEPENDENCY);
+ bundle.start();
+
+ WebApplicationHandle handle = this.container.createWebApplication("/war-with-tld-from-dependency", bundle);
+ this.container.startWebApplication(handle);
+ try {
+ String realPath = handle.getServletContext().getRealPath("/");
+ System.out.println(realPath);
+ validateURL("http://localhost:8080/war-with-tld-from-dependency/test.jsp");
+ } finally {
+ this.container.stopWebApplication(handle);
+ bundle.uninstall();
+ jstlBundle.uninstall();
+ }
+ }
+
+ @Test
+ public void testWarWithJSTLFromExplodedDependency() throws MalformedURLException, IOException, BundleException {
+ String jstlPath = "../ivy-cache/repository/javax.servlet/com.springsource.javax.servlet.jsp.jstl/1.2.0/com.springsource.javax.servlet.jsp.jstl-1.2.0.jar";
+ PathReference jstl = new PathReference(jstlPath);
+ PathReference unzippedJstl = explode(jstl);
+
+ String jstlLocation = "file:" + unzippedJstl.getAbsolutePath();
+ Bundle jstlBundle = this.bundleContext.installBundle(jstlLocation);
+
+ Bundle bundle = this.bundleContext.installBundle(LOCATION_WAR_WITH_TLD_FROM_DEPENDENCY);
+ bundle.start();
+
+ WebApplicationHandle handle = this.container.createWebApplication("/war-with-tld-from-dependency", bundle);
+ this.container.startWebApplication(handle);
+ try {
+ validateURL("http://localhost:8080/war-with-tld-from-dependency/test.jsp");
+ } finally {
+ this.container.stopWebApplication(handle);
+ bundle.uninstall();
+ jstlBundle.uninstall();
+ unzippedJstl.delete(true);
+ }
+ }
+
+ @Test
+ public void testWarWithJSTLThatImportsSystemPackages() throws MalformedURLException, IOException, BundleException {
+ String location = LOCATION_WAR_WITH_TLD_IMPORT_SYSTEM_PACKAGES;
+ Bundle bundle = this.bundleContext.installBundle(location);
+ bundle.start();
+
+ WebApplicationHandle handle = this.container.createWebApplication("/war-with-tld", bundle);
+ this.container.startWebApplication(handle);
+ try {
+ String realPath = handle.getServletContext().getRealPath("/");
+ System.out.println(realPath);
+ validateURL("http://localhost:8080/war-with-tld/test.jsp");
+ } finally {
+ this.container.stopWebApplication(handle);
+ bundle.uninstall();
+ }
+ }
+
+ @Test
+ public void testGetRealPathWithJarBundle() throws Exception {
+ String location = LOCATION_WAR_WITH_SERVLET;
+ Bundle bundle = this.bundleContext.installBundle(location);
+ bundle.start();
+
+ WebApplicationHandle handle = this.container.createWebApplication("/war-with-servlet", bundle);
+ this.container.startWebApplication(handle);
+ try {
+ ServletContext context = handle.getServletContext();
+ assertNotNull(context);
+
+ String path = context.getRealPath("/WEB-INF/web.xml");
+ assertNull(path);
+ } finally {
+ this.container.stopWebApplication(handle);
+ }
+ }
+
+ @Test
+ public void testServletContextResourceLookup() throws Exception {
+ String location = LOCATION_WAR_WITH_SERVLET;
+ Bundle bundle = this.bundleContext.installBundle(location);
+ bundle.start();
+
+ WebApplicationHandle handle = this.container.createWebApplication("/war-with-servlet", bundle);
+ this.container.startWebApplication(handle);
+ try {
+ ServletContext context = handle.getServletContext();
+ assertNotNull(context);
+
+ URL resource = context.getResource("/WEB-INF/web.xml");
+ assertNotNull(resource);
+
+ URLConnection connection = resource.openConnection();
+ assertNotNull(connection);
+
+ Set<?> paths = context.getResourcePaths("/WEB-INF");
+ assertNotNull(paths);
+ assertEquals(3, paths.size());
+
+ } finally {
+ this.container.stopWebApplication(handle);
+ }
+ }
+
+ @Test
+ public void rootContextPath() throws Exception {
+ String location = LOCATION_WAR_WITH_SERVLET;
+ Bundle bundle = this.bundleContext.installBundle(location);
+ bundle.start();
+
+ WebApplicationHandle handle = this.container.createWebApplication("", bundle);
+ this.container.startWebApplication(handle);
+ try {
+ ServletContext context = handle.getServletContext();
+ assertEquals("", context.getContextPath());
+ } finally {
+ this.container.stopWebApplication(handle);
+ }
+ }
+
+ private void validateURL(String path) throws MalformedURLException, IOException {
+ URL url = new URL(path);
+ InputStream stream = url.openConnection().getInputStream();
+ assertNotNull(stream);
+ BufferedReader reader = new BufferedReader(new InputStreamReader(stream));
+ String line = null;
+ while ((line = reader.readLine()) != null) {
+ System.out.println(line);
+ }
+ }
+
+ private void validateNotFound(String path) throws Exception {
+ URL url = new URL(path);
+ try {
+ url.openConnection().getInputStream();
+ } catch (IOException e) {
+ assertTrue("success case", true);
+ return;
+ }
+ fail("URL '" + path + "' is still deployed");
+ }
+
+ private PathReference explode(PathReference packed) throws IOException {
+ PathReference target = new PathReference("target");
+ return ZipUtils.unzipTo(packed, target);
+ }
+
+ @Test
+ public void testLastModified() throws Exception {
+ String location = LOCATION_WAR_WITH_SERVLET;
+ Bundle bundle = this.bundleContext.installBundle(location);
+ bundle.start();
+
+ WebApplicationHandle handle = this.container.createWebApplication("", bundle);
+ this.container.startWebApplication(handle);
+ try {
+ ServletContext context = handle.getServletContext();
+ long lm = context.getResource("/META-INF/").openConnection().getLastModified();
+ assertTrue(lm != 0);
+ } finally {
+ this.container.stopWebApplication(handle);
+ }
+ }
+}
diff --git a/org.eclipse.gemini.web.test/src/test/resources/META-INF/MANIFEST.MF b/org.eclipse.gemini.web.test/src/test/resources/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..72c89fd
--- /dev/null
+++ b/org.eclipse.gemini.web.test/src/test/resources/META-INF/MANIFEST.MF
@@ -0,0 +1,12 @@
+Manifest-Version: 1.0
+Bundle-Version: 1.0
+Tool: Bundlor 1.0.0.M6
+Bundle-Name: Web Container Tests
+Bundle-ManifestVersion: 2
+Import-Package: javax.servlet;version="0",junit.framework;version="0",
+ org.eclipse.gemini.web.core.spi;version="0",org.eclipse.virgo.test.fr
+ amework;version="0",org.eclipse.virgo.util.io;version="0",org.junit;v
+ ersion="0",org.junit.runner;version="0",org.osgi.framework;version="0
+ "
+Bundle-SymbolicName: org.eclipse.gemini.web.test
+
diff --git a/org.eclipse.gemini.web.test/src/test/resources/META-INF/test.config.properties b/org.eclipse.gemini.web.test/src/test/resources/META-INF/test.config.properties
new file mode 100644
index 0000000..eb1b2d0
--- /dev/null
+++ b/org.eclipse.gemini.web.test/src/test/resources/META-INF/test.config.properties
@@ -0,0 +1,51 @@
+launcher.bundles=file:../ivy-cache/repository/org.apache.felix/org.apache.felix.eventadmin/1.0.0/org.apache.felix.eventadmin-1.0.0.jar@start,\
+file:../ivy-cache/repository/org.eclipse.virgo.util/org.eclipse.virgo.util.common/${org.eclipse.virgo.util}/org.eclipse.virgo.util.common-${org.eclipse.virgo.util}.jar@start,\
+file:../ivy-cache/repository/org.eclipse.virgo.util/org.eclipse.virgo.util.io/${org.eclipse.virgo.util}/org.eclipse.virgo.util.io-${org.eclipse.virgo.util}.jar@start,\
+file:../ivy-cache/repository/org.eclipse.virgo.util/org.eclipse.virgo.util.math/${org.eclipse.virgo.util}/org.eclipse.virgo.util.math-${org.eclipse.virgo.util}.jar@start,\
+file:../ivy-cache/repository/org.eclipse.virgo.util/org.eclipse.virgo.util.osgi/${org.eclipse.virgo.util}/org.eclipse.virgo.util.osgi-${org.eclipse.virgo.util}.jar@start,\
+file:../ivy-cache/repository/org.eclipse.virgo.util/org.eclipse.virgo.util.parser.manifest/${org.eclipse.virgo.util}/org.eclipse.virgo.util.parser.manifest-${org.eclipse.virgo.util}.jar@start,\
+file:../ivy-cache/repository/javax.annotation/com.springsource.javax.annotation/1.0.0/com.springsource.javax.annotation-1.0.0.jar@start,\
+file:../ivy-cache/repository/javax.activation/com.springsource.javax.activation/1.1.1/com.springsource.javax.activation-1.1.1.jar@start,\
+file:../ivy-cache/repository/javax.ejb/com.springsource.javax.ejb/3.0.0/com.springsource.javax.ejb-3.0.0.jar@start,\
+file:../ivy-cache/repository/javax.el/com.springsource.javax.el/1.0.0/com.springsource.javax.el-1.0.0.jar@start,\
+file:../ivy-cache/repository/javax.mail/com.springsource.javax.mail/1.4.0/com.springsource.javax.mail-1.4.0.jar@start,\
+file:../ivy-cache/repository/javax.persistence/com.springsource.javax.persistence/1.0.0/com.springsource.javax.persistence-1.0.0.jar@start,\
+file:../ivy-cache/repository/javax.servlet/com.springsource.javax.servlet.jsp/2.1.0/com.springsource.javax.servlet.jsp-2.1.0.jar@start,\
+file:../ivy-cache/repository/javax.servlet/com.springsource.javax.servlet/2.5.0/com.springsource.javax.servlet-2.5.0.jar@start,\
+file:../ivy-cache/repository/javax.xml.rpc/com.springsource.javax.xml.rpc/1.1.0/com.springsource.javax.xml.rpc-1.1.0.jar@start,\
+file:../ivy-cache/repository/javax.xml.soap/com.springsource.javax.xml.soap/1.3.0/com.springsource.javax.xml.soap-1.3.0.jar@start,\
+file:../ivy-cache/repository/javax.xml.ws/com.springsource.javax.xml.ws/2.1.1/com.springsource.javax.xml.ws-2.1.1.jar@start,\
+file:../ivy-cache/repository/org.apache.catalina.springsource/com.springsource.org.apache.catalina.ha.springsource/6.0.20.S2-r5956/com.springsource.org.apache.catalina.ha.springsource-6.0.20.S2-r5956.jar@start,\
+file:../ivy-cache/repository/org.apache.catalina.springsource/com.springsource.org.apache.catalina.tribes.springsource/6.0.20.S2-r5956/com.springsource.org.apache.catalina.tribes.springsource-6.0.20.S2-r5956.jar@start,\
+file:../ivy-cache/repository/org.apache.catalina.springsource/com.springsource.org.apache.catalina.springsource/6.0.20.S2-r5956/com.springsource.org.apache.catalina.springsource-6.0.20.S2-r5956.jar@start,\
+file:../ivy-cache/repository/org.apache.coyote.springsource/com.springsource.org.apache.coyote.springsource/6.0.20.S2-r5956/com.springsource.org.apache.coyote.springsource-6.0.20.S2-r5956.jar,\
+file:../ivy-cache/repository/org.apache.el.springsource/com.springsource.org.apache.el.springsource/6.0.20.S2-r5956/com.springsource.org.apache.el.springsource-6.0.20.S2-r5956.jar@start,\
+file:../ivy-cache/repository/org.apache.jasper.springsource/com.springsource.org.apache.jasper.org.eclipse.jdt.springsource/6.0.20.S2-r5956/com.springsource.org.apache.jasper.org.eclipse.jdt.springsource-6.0.20.S2-r5956.jar@start,\
+file:../ivy-cache/repository/org.apache.jasper.springsource/com.springsource.org.apache.jasper.springsource/6.0.20.S2-r5956/com.springsource.org.apache.jasper.springsource-6.0.20.S2-r5956.jar,\
+file:../ivy-cache/repository/org.apache.juli.springsource/com.springsource.org.apache.juli.extras.springsource/6.0.20.S2-r5956/com.springsource.org.apache.juli.extras.springsource-6.0.20.S2-r5956.jar@start,\
+file:../ivy-cache/repository/org.slf4j/com.springsource.slf4j.api/1.5.10/com.springsource.slf4j.api-1.5.10.jar@start,\
+file:../ivy-cache/repository/org.slf4j/com.springsource.slf4j.nop/1.5.10/com.springsource.slf4j.nop-1.5.10.jar,\
+file:../ivy-cache/repository/org.aopalliance/com.springsource.org.aopalliance/1.0.0/com.springsource.org.aopalliance-1.0.0.jar,\
+file:../ivy-cache/repository/org.apache.commons/com.springsource.org.apache.commons.logging/1.1.1/com.springsource.org.apache.commons.logging-1.1.1.jar,\
+file:../ivy-cache/repository/org.springframework/org.springframework.aop/3.0.0.RELEASE/org.springframework.aop-3.0.0.RELEASE.jar@start,\
+file:../ivy-cache/repository/org.springframework/org.springframework.beans/3.0.0.RELEASE/org.springframework.beans-3.0.0.RELEASE.jar@start,\
+file:../ivy-cache/repository/org.springframework/org.springframework.context/3.0.0.RELEASE/org.springframework.context-3.0.0.RELEASE.jar,\
+file:../ivy-cache/repository/org.springframework/org.springframework.core/3.0.0.RELEASE/org.springframework.core-3.0.0.RELEASE.jar,\
+file:../ivy-cache/repository/org.springframework.osgi/org.springframework.osgi.core/${org.springframework.osgi}/org.springframework.osgi.core-${org.springframework.osgi}.jar@start,\
+file:../ivy-cache/repository/org.springframework.osgi/org.springframework.osgi.io/${org.springframework.osgi}/org.springframework.osgi.io-${org.springframework.osgi}.jar@start,\
+file:../org.eclipse.gemini.web.core/target/classes@start,\
+file:../org.eclipse.gemini.web.tomcat/target/classes@start
+
+org.eclipse.virgo.test.properties.include=file:../build.versions,file:../build.properties
+
+org.eclipse.virgo.test.ivy.settings=file:src/test/resources/ivysettings.xml
+
+osgi.java.profile=file:src/test/resources/java6-server.profile
+osgi.java.profile.bootdelegation=override
+osgi.parentClassloader=fwk
+osgi.context.bootdelegation=false
+osgi.compatibility.bootdelegation=false
+
+osgi.console=2401
+
+local.repo.dir=/tmp/local-repository
diff --git a/org.eclipse.gemini.web.test/src/test/resources/classpathdeps.war b/org.eclipse.gemini.web.test/src/test/resources/classpathdeps.war
new file mode 100644
index 0000000..e771f99
--- /dev/null
+++ b/org.eclipse.gemini.web.test/src/test/resources/classpathdeps.war
Binary files differ
diff --git a/org.eclipse.gemini.web.test/src/test/resources/cyclicclasspathdeps.war b/org.eclipse.gemini.web.test/src/test/resources/cyclicclasspathdeps.war
new file mode 100644
index 0000000..394b78a
--- /dev/null
+++ b/org.eclipse.gemini.web.test/src/test/resources/cyclicclasspathdeps.war
Binary files differ
diff --git a/org.eclipse.gemini.web.test/src/test/resources/empty.war b/org.eclipse.gemini.web.test/src/test/resources/empty.war
new file mode 100644
index 0000000..227fcdd
--- /dev/null
+++ b/org.eclipse.gemini.web.test/src/test/resources/empty.war
Binary files differ
diff --git a/org.eclipse.gemini.web.test/src/test/resources/ivysettings.xml b/org.eclipse.gemini.web.test/src/test/resources/ivysettings.xml
new file mode 100644
index 0000000..3b9ec90
--- /dev/null
+++ b/org.eclipse.gemini.web.test/src/test/resources/ivysettings.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ivysettings>
+
+ <settings defaultResolver="external-lookup" defaultLatestStrategy="latest-lexico"/>
+
+ <caches resolutionCacheDir="${ivy.cache.dir}/resolution" repositoryCacheDir="${ivy.cache.dir}/repository"
+ ivyPattern="[organisation]/[module]/[revision]/[artifact]-[revision].[ext]"
+ artifactPattern="[organisation]/[module]/[revision]/[artifact]-[revision].[ext]"
+ useOrigin="true" checkUpToDate="false"/>
+
+
+ <macrodef name="localrepo">
+ <filesystem descriptor="required" local="false">
+ <ivy pattern="${local.repo.dir}/[organisation]/[module]/[revision]/[artifact]-[revision].[ext]"/>
+ <artifact pattern="${local.repo.dir}/[organisation]/[module]/[revision]/[artifact]-[revision].[ext]"/>
+ </filesystem>
+ </macrodef>
+
+ <resolvers>
+<!-- Integration repositories -->
+ <filesystem name="integration" descriptor="required">
+ <ivy pattern="${integration.repo.dir}/[organisation]/[module]/[revision]/[artifact]-[revision].[ext]"/>
+ <artifact pattern="${integration.repo.dir}/[organisation]/[module]/[revision]/[artifact]-[revision].[ext]"/>
+ </filesystem>
+
+<!-- Lookup repositories -->
+ <chain name="external-lookup" returnFirst="true">
+ <localrepo name="local-external-repository"/>
+
+ <url name="bundle-external-repository">
+ <ivy pattern="http://repository.springsource.com/ivy/bundles/external/[organisation]/[module]/[revision]/[artifact]-[revision].[ext]" />
+ <artifact pattern="http://repository.springsource.com/ivy/bundles/external/[organisation]/[module]/[revision]/[artifact]-[revision].[ext]" />
+ </url>
+ </chain>
+
+ <chain name="spring-portfolio-lookup" returnFirst="true">
+ <resolver ref="integration"/>
+ <localrepo name="local"/>
+ <url name="bundle-release-repository">
+ <ivy pattern="http://repository.springsource.com/ivy/bundles/release/[organisation]/[module]/[revision]/[artifact]-[revision].[ext]" />
+ <artifact pattern="http://repository.springsource.com/ivy/bundles/release/[organisation]/[module]/[revision]/[artifact]-[revision].[ext]" />
+ </url>
+ </chain>
+ </resolvers>
+
+ <modules>
+ <module organisation="org.springframework.*" name="*" resolver="spring-portfolio-lookup"/>
+ <module organisation="com.springsource.*" name="*" resolver="spring-portfolio-lookup"/>
+ <module organisation="org.eclipse.virgo.kernel" name="*" resolver="integration"/>
+ </modules>
+
+</ivysettings>
diff --git a/org.eclipse.gemini.web.test/src/test/resources/java6-server.profile b/org.eclipse.gemini.web.test/src/test/resources/java6-server.profile
new file mode 100644
index 0000000..b429732
--- /dev/null
+++ b/org.eclipse.gemini.web.test/src/test/resources/java6-server.profile
@@ -0,0 +1,170 @@
+org.osgi.framework.system.packages = \
+ com.sun.org.apache.xalan.internal.res,\
+ com.sun.org.apache.xml.internal.utils,\
+ com.sun.org.apache.xpath.internal,\
+ com.sun.org.apache.xpath.internal.jaxp,\
+ com.sun.org.apache.xpath.internal.objects,\
+ javax.accessibility,\
+ javax.crypto,\
+ javax.crypto.interfaces,\
+ javax.crypto.spec,\
+ javax.imageio,\
+ javax.imageio.event,\
+ javax.imageio.metadata,\
+ javax.imageio.plugins.bmp,\
+ javax.imageio.plugins.jpeg,\
+ javax.imageio.spi,\
+ javax.imageio.stream,\
+ javax.jws,\
+ javax.jws;version="2.0",\
+ javax.jws.soap,\
+ javax.jws.soap;version="2.0",\
+ javax.management,\
+ javax.management.loading,\
+ javax.management.modelmbean,\
+ javax.management.monitor,\
+ javax.management.openmbean,\
+ javax.management.relation,\
+ javax.management.remote,\
+ javax.management.remote.rmi,\
+ javax.management.timer,\
+ javax.naming,\
+ javax.naming.directory,\
+ javax.naming.event,\
+ javax.naming.ldap,\
+ javax.naming.spi,\
+ javax.net,\
+ javax.net.ssl,\
+ javax.print,\
+ javax.print.attribute,\
+ javax.print.attribute.standard,\
+ javax.print.event,\
+ javax.rmi,\
+ javax.rmi.CORBA,\
+ javax.rmi.ssl,\
+ javax.security.auth,\
+ javax.security.auth.callback,\
+ javax.security.auth.kerberos,\
+ javax.security.auth.login,\
+ javax.security.auth.spi,\
+ javax.security.auth.x500,\
+ javax.security.cert,\
+ javax.security.sasl,\
+ javax.sound.midi,\
+ javax.sound.midi.spi,\
+ javax.sound.sampled,\
+ javax.sound.sampled.spi,\
+ javax.sql,\
+ javax.sql.rowset,\
+ javax.sql.rowset.serial,\
+ javax.sql.rowset.spi,\
+ javax.swing,\
+ javax.swing.border,\
+ javax.swing.colorchooser,\
+ javax.swing.event,\
+ javax.swing.filechooser,\
+ javax.swing.plaf,\
+ javax.swing.plaf.basic,\
+ javax.swing.plaf.metal,\
+ javax.swing.plaf.multi,\
+ javax.swing.plaf.synth,\
+ javax.swing.table,\
+ javax.swing.text,\
+ javax.swing.text.html,\
+ javax.swing.text.html.parser,\
+ javax.swing.text.rtf,\
+ javax.swing.tree,\
+ javax.swing.undo,\
+ javax.transaction,\
+ javax.transaction;version="1.0.1",\
+ javax.transaction;version="1.1.0",\
+ javax.transaction.xa,\
+ javax.transaction.xa;version="1.0.1",\
+ javax.transaction.xa;version="1.1.0",\
+ javax.xml,\
+ javax.xml;version="1.0.1",\
+ javax.xml.bind,\
+ javax.xml.bind;version="2.0",\
+ javax.xml.bind.annotation,\
+ javax.xml.bind.annotation;version="2.0",\
+ javax.xml.bind.annotation.adapters,\
+ javax.xml.bind.annotation.adapters;version="2.0",\
+ javax.xml.bind.attachment,\
+ javax.xml.bind.attachment;version="2.0",\
+ javax.xml.bind.helpers,\
+ javax.xml.bind.helpers;version="2.0",\
+ javax.xml.bind.util,\
+ javax.xml.bind.util;version="2.0",\
+ javax.xml.datatype,\
+ javax.xml.namespace,\
+ javax.xml.parsers,\
+ javax.xml.soap,\
+ javax.xml.soap;version="1.3.0",\
+ javax.xml.stream,\
+ javax.xml.stream;version="1.0.1",\
+ javax.xml.stream.events,\
+ javax.xml.stream.events;version="1.0.1",\
+ javax.xml.stream.util,\
+ javax.xml.stream.util;version="1.0.1",\
+ javax.xml.transform,\
+ javax.xml.transform.dom,\
+ javax.xml.transform.sax,\
+ javax.xml.transform.stream,\
+ javax.xml.validation,\
+ javax.xml.xpath,\
+ org.ietf.jgss,\
+ org.omg.CORBA,\
+ org.omg.CORBA_2_3,\
+ org.omg.CORBA_2_3.portable,\
+ org.omg.CORBA.DynAnyPackage,\
+ org.omg.CORBA.ORBPackage,\
+ org.omg.CORBA.portable,\
+ org.omg.CORBA.TypeCodePackage,\
+ org.omg.CosNaming,\
+ org.omg.CosNaming.NamingContextExtPackage,\
+ org.omg.CosNaming.NamingContextPackage,\
+ org.omg.Dynamic,\
+ org.omg.DynamicAny,\
+ org.omg.DynamicAny.DynAnyFactoryPackage,\
+ org.omg.DynamicAny.DynAnyPackage,\
+ org.omg.IOP,\
+ org.omg.IOP.CodecFactoryPackage,\
+ org.omg.IOP.CodecPackage,\
+ org.omg.Messaging,\
+ org.omg.PortableInterceptor,\
+ org.omg.PortableInterceptor.ORBInitInfoPackage,\
+ org.omg.PortableServer,\
+ org.omg.PortableServer.CurrentPackage,\
+ org.omg.PortableServer.POAManagerPackage,\
+ org.omg.PortableServer.POAPackage,\
+ org.omg.PortableServer.portable,\
+ org.omg.PortableServer.ServantLocatorPackage,\
+ org.omg.SendingContext,\
+ org.omg.stub.java.rmi,\
+ org.w3c.dom,\
+ org.w3c.dom.bootstrap,\
+ org.w3c.dom.css,\
+ org.w3c.dom.events,\
+ org.w3c.dom.html,\
+ org.w3c.dom.ls,\
+ org.w3c.dom.ranges,\
+ org.w3c.dom.stylesheets,\
+ org.w3c.dom.traversal,\
+ org.w3c.dom.views ,\
+ org.xml.sax,\
+ org.xml.sax.ext,\
+ org.xml.sax.helpers
+org.osgi.framework.bootdelegation = \
+ com_cenqua_clover,\
+ com.cenqua.*,\
+ com.yourkit.*,\
+ com.sun.*,\
+ org.apache.xerces.jaxp.*,\
+ sun.*
+org.osgi.framework.executionenvironment = \
+ OSGi/Minimum-1.0,\
+ OSGi/Minimum-1.1,\
+ J2SE-1.3,\
+ J2SE-1.4,\
+ J2SE-1.5
+osgi.java.profile.name = SpringSource-dm-Server-Java5
diff --git a/org.eclipse.gemini.web.test/src/test/resources/no-manifest.war b/org.eclipse.gemini.web.test/src/test/resources/no-manifest.war
new file mode 100644
index 0000000..c5de6d5
--- /dev/null
+++ b/org.eclipse.gemini.web.test/src/test/resources/no-manifest.war
Binary files differ
diff --git a/org.eclipse.gemini.web.test/src/test/resources/specified-context-path-1.war b/org.eclipse.gemini.web.test/src/test/resources/specified-context-path-1.war
new file mode 100644
index 0000000..e03b23b
--- /dev/null
+++ b/org.eclipse.gemini.web.test/src/test/resources/specified-context-path-1.war
Binary files differ
diff --git a/org.eclipse.gemini.web.test/src/test/resources/specified-context-path-2.war b/org.eclipse.gemini.web.test/src/test/resources/specified-context-path-2.war
new file mode 100644
index 0000000..06172ea
--- /dev/null
+++ b/org.eclipse.gemini.web.test/src/test/resources/specified-context-path-2.war
Binary files differ
diff --git a/org.eclipse.gemini.web.test/src/test/resources/war-with-another-servlet.war b/org.eclipse.gemini.web.test/src/test/resources/war-with-another-servlet.war
new file mode 100644
index 0000000..3787f7f
--- /dev/null
+++ b/org.eclipse.gemini.web.test/src/test/resources/war-with-another-servlet.war
Binary files differ
diff --git a/org.eclipse.gemini.web.test/src/test/resources/war-with-jsp.war b/org.eclipse.gemini.web.test/src/test/resources/war-with-jsp.war
new file mode 100644
index 0000000..2fbd9a9
--- /dev/null
+++ b/org.eclipse.gemini.web.test/src/test/resources/war-with-jsp.war
Binary files differ
diff --git a/org.eclipse.gemini.web.test/src/test/resources/war-with-servlet.war b/org.eclipse.gemini.web.test/src/test/resources/war-with-servlet.war
new file mode 100644
index 0000000..c024d0a
--- /dev/null
+++ b/org.eclipse.gemini.web.test/src/test/resources/war-with-servlet.war
Binary files differ
diff --git a/org.eclipse.gemini.web.test/src/test/resources/war-with-tld-from-dependency.war b/org.eclipse.gemini.web.test/src/test/resources/war-with-tld-from-dependency.war
new file mode 100644
index 0000000..c3cbdfb
--- /dev/null
+++ b/org.eclipse.gemini.web.test/src/test/resources/war-with-tld-from-dependency.war
Binary files differ
diff --git a/org.eclipse.gemini.web.test/src/test/resources/war-with-tld-import-system-packages.war b/org.eclipse.gemini.web.test/src/test/resources/war-with-tld-import-system-packages.war
new file mode 100644
index 0000000..7a3916a
--- /dev/null
+++ b/org.eclipse.gemini.web.test/src/test/resources/war-with-tld-import-system-packages.war
Binary files differ
diff --git a/org.eclipse.gemini.web.test/src/test/resources/war-with-tld.war b/org.eclipse.gemini.web.test/src/test/resources/war-with-tld.war
new file mode 100644
index 0000000..969a430
--- /dev/null
+++ b/org.eclipse.gemini.web.test/src/test/resources/war-with-tld.war
Binary files differ
diff --git a/org.eclipse.gemini.web.test/template.mf b/org.eclipse.gemini.web.test/template.mf
new file mode 100644
index 0000000..4570730
--- /dev/null
+++ b/org.eclipse.gemini.web.test/template.mf
@@ -0,0 +1,7 @@
+Bundle-ManifestVersion: 2
+Bundle-SymbolicName: org.eclipse.gemini.web.test
+Bundle-Name: Web Container Tests
+Bundle-Version: 1.0
+Excluded-Exports: *
+Import-Template:
+ *;version="0"
diff --git a/org.eclipse.gemini.web.tomcat/.classpath b/org.eclipse.gemini.web.tomcat/.classpath
new file mode 100644
index 0000000..42ede82
--- /dev/null
+++ b/org.eclipse.gemini.web.tomcat/.classpath
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="src" path="src/main/java">
+ <attributes>
+ <attribute name="com.springsource.server.ide.jdt.core.test.classpathentry" value="false"/>
+ </attributes>
+ </classpathentry>
+ <classpathentry kind="src" output="target/test-classes" path="src/test/java">
+ <attributes>
+ <attribute name="com.springsource.server.ide.jdt.core.test.classpathentry" value="true"/>
+ </attributes>
+ </classpathentry>
+ <classpathentry kind="src" output="target/test-classes" path="src/test/resources">
+ <attributes>
+ <attribute name="com.springsource.server.ide.jdt.core.test.classpathentry" value="true"/>
+ </attributes>
+ </classpathentry>
+ <classpathentry kind="src" path="src/main/resources">
+ <attributes>
+ <attribute name="com.springsource.server.ide.jdt.core.test.classpathentry" value="false"/>
+ </attributes>
+ </classpathentry>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+ <classpathentry kind="var" path="WEB_CONTAINER_IVY_CACHE/org.junit/com.springsource.org.junit/4.7.0/com.springsource.org.junit-4.7.0.jar" sourcepath="/WEB_CONTAINER_IVY_CACHE/org.junit/com.springsource.org.junit/4.5.0/com.springsource.org.junit-sources-4.5.0.jar"/>
+ <classpathentry kind="var" path="WEB_CONTAINER_IVY_CACHE/org.eclipse.osgi/org.eclipse.osgi/3.5.1.R35x_v20091005/org.eclipse.osgi-3.5.1.R35x_v20091005.jar" sourcepath="/WEB_CONTAINER_IVY_CACHE/org.eclipse.osgi/org.eclipse.osgi/3.5.1.R35x_v20091005/org.eclipse.osgi-sources-3.5.1.R35x_v20091005.jar"/>
+ <classpathentry kind="var" path="WEB_CONTAINER_IVY_CACHE/org.eclipse.virgo.util/org.eclipse.virgo.util.osgi/2.1.0.D-20100420091708/org.eclipse.virgo.util.osgi-2.1.0.D-20100420091708.jar" sourcepath="/WEB_CONTAINER_IVY_CACHE/org.eclispe.virgo.util/org.eclipse.virgo.util.osgi/2.0.0.RELEASE/org.eclipse.virgo.util.osgi-sources-2.0.0.RELEASE.jar"/>
+ <classpathentry kind="var" path="WEB_CONTAINER_IVY_CACHE/org.eclipse.virgo.util/org.eclipse.virgo.util.io/2.1.0.D-20100420091708/org.eclipse.virgo.util.io-2.1.0.D-20100420091708.jar" sourcepath="/WEB_CONTAINER_IVY_CACHE/org.eclispe.virgo.util/org.eclipse.virgo.util.io/2.0.0.RELEASE/org.eclipse.virgo.util.io-sources-2.0.0.RELEASE.jar"/>
+ <classpathentry kind="var" path="WEB_CONTAINER_IVY_CACHE/org.eclipse.virgo.util/org.eclipse.virgo.util.common/2.1.0.D-20100420091708/org.eclipse.virgo.util.common-2.1.0.D-20100420091708.jar" sourcepath="/WEB_CONTAINER_IVY_CACHE/org.eclispe.virgo.util/org.eclipse.virgo.util.common/2.0.0.RELEASE/org.eclipse.virgo.util.common-sources-2.0.0.RELEASE.jar"/>
+ <classpathentry kind="var" path="WEB_CONTAINER_IVY_CACHE/org.slf4j/com.springsource.slf4j.api/1.5.10/com.springsource.slf4j.api-1.5.10.jar" sourcepath="/WEB_CONTAINER_IVY_CACHE/org.slf4j/com.springsource.slf4j.api/1.5.10/com.springsource.slf4j.api-sources-1.5.10.jar"/>
+ <classpathentry kind="var" path="WEB_CONTAINER_IVY_CACHE/org.apache.catalina.springsource/com.springsource.org.apache.catalina.springsource/6.0.20.S2-r5956/com.springsource.org.apache.catalina.springsource-6.0.20.S2-r5956.jar" sourcepath="/WEB_CONTAINER_IVY_CACHE/org.apache.catalina.springsource/com.springsource.org.apache.catalina.springsource/6.0.20.S2-r5956/com.springsource.org.apache.catalina.springsource-sources-6.0.20.S2-r5956.jar"/>
+ <classpathentry kind="var" path="WEB_CONTAINER_IVY_CACHE/org.apache.juli.springsource/com.springsource.org.apache.juli.extras.springsource/6.0.20.S2-r5956/com.springsource.org.apache.juli.extras.springsource-6.0.20.S2-r5956.jar"/>
+ <classpathentry kind="var" path="WEB_CONTAINER_IVY_CACHE/org.apache.jasper.springsource/com.springsource.org.apache.jasper.springsource/6.0.20.S2-r5956/com.springsource.org.apache.jasper.springsource-6.0.20.S2-r5956.jar" sourcepath="/WEB_CONTAINER_IVY_CACHE/org.apache.jasper.springsource/com.springsource.org.apache.jasper.springsource/6.0.20.S2-r5956/com.springsource.org.apache.jasper.springsource-sources-6.0.20.S2-r5956.jar"/>
+ <classpathentry kind="var" path="WEB_CONTAINER_IVY_CACHE/org.apache.jasper.springsource/com.springsource.org.apache.jasper.org.eclipse.jdt.springsource/6.0.20.S2-r5956/com.springsource.org.apache.jasper.org.eclipse.jdt.springsource-6.0.20.S2-r5956.jar"/>
+ <classpathentry kind="var" path="WEB_CONTAINER_IVY_CACHE/org.apache.catalina.springsource/com.springsource.org.apache.catalina.ha.springsource/6.0.20.S2-r5956/com.springsource.org.apache.catalina.ha.springsource-6.0.20.S2-r5956.jar"/>
+ <classpathentry kind="var" path="WEB_CONTAINER_IVY_CACHE/org.apache.catalina.springsource/com.springsource.org.apache.catalina.tribes.springsource/6.0.20.S2-r5956/com.springsource.org.apache.catalina.tribes.springsource-6.0.20.S2-r5956.jar"/>
+ <classpathentry kind="var" path="WEB_CONTAINER_IVY_CACHE/org.apache.coyote.springsource/com.springsource.org.apache.coyote.springsource/6.0.20.S2-r5956/com.springsource.org.apache.coyote.springsource-6.0.20.S2-r5956.jar" sourcepath="/WEB_CONTAINER_IVY_CACHE/org.apache.coyote.springsource/com.springsource.org.apache.coyote.springsource/6.0.20.S2-r5956/com.springsource.org.apache.coyote.springsource-sources-6.0.20.S2-r5956.jar"/>
+ <classpathentry kind="var" path="WEB_CONTAINER_IVY_CACHE/javax.servlet/com.springsource.javax.servlet/2.5.0/com.springsource.javax.servlet-2.5.0.jar" sourcepath="/WEB_CONTAINER_IVY_CACHE/javax.servlet/com.springsource.javax.servlet/2.5.0/com.springsource.javax.servlet-sources-2.5.0.jar"/>
+ <classpathentry kind="var" path="WEB_CONTAINER_IVY_CACHE/org.easymock/com.springsource.org.easymock/2.3.0/com.springsource.org.easymock-2.3.0.jar" sourcepath="/WEB_CONTAINER_IVY_CACHE/org.easymock/com.springsource.org.easymock/2.3.0/com.springsource.org.easymock-sources-2.3.0.jar"/>
+ <classpathentry combineaccessrules="false" kind="src" path="/org.eclipse.gemini.web.core"/>
+ <classpathentry kind="var" path="WEB_CONTAINER_IVY_CACHE/org.springframework.osgi/org.springframework.osgi.core/1.2.0/org.springframework.osgi.core-1.2.0.jar" sourcepath="/WEB_CONTAINER_IVY_CACHE/org.springframework.osgi/org.springframework.osgi.core/1.2.0/org.springframework.osgi.core-sources-1.2.0.jar"/>
+ <classpathentry kind="var" path="WEB_CONTAINER_IVY_CACHE/javax.el/com.springsource.javax.el/1.0.0/com.springsource.javax.el-1.0.0.jar"/>
+ <classpathentry kind="var" path="WEB_CONTAINER_IVY_CACHE/org.apache.commons/com.springsource.org.apache.commons.logging/1.1.1/com.springsource.org.apache.commons.logging-1.1.1.jar"/>
+ <classpathentry kind="var" path="WEB_CONTAINER_IVY_CACHE/org.springframework/org.springframework.core/3.0.0.RELEASE/org.springframework.core-3.0.0.RELEASE.jar"/>
+ <classpathentry kind="var" path="WEB_CONTAINER_IVY_CACHE/org.eclipse.virgo.teststubs/org.eclipse.virgo.teststubs.osgi/1.0.0.D-20100420091314/org.eclipse.virgo.teststubs.osgi-1.0.0.D-20100420091314.jar" sourcepath="/WEB_CONTAINER_IVY_CACHE/org.eclispe.virgo.osgi.test/org.eclipse.virgo.teststubs.osgi/1.0.0.CI-B40/org.eclipse.virgo.teststubs.osgi-sources-1.0.0.CI-B40.jar"/>
+ <classpathentry kind="output" path="target/classes"/>
+</classpath>
diff --git a/org.eclipse.gemini.web.tomcat/.project b/org.eclipse.gemini.web.tomcat/.project
new file mode 100644
index 0000000..fbc4b21
--- /dev/null
+++ b/org.eclipse.gemini.web.tomcat/.project
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>org.eclipse.gemini.web.tomcat</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.wst.common.project.facet.core.builder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.springframework.ide.eclipse.core.springbuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>com.springsource.server.ide.bundlor.core.builder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>com.springsource.server.ide.facet.core.bundlenature</nature>
+ <nature>org.springframework.ide.eclipse.core.springnature</nature>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ <nature>org.eclipse.wst.common.project.facet.core.nature</nature>
+ </natures>
+</projectDescription>
diff --git a/org.eclipse.gemini.web.tomcat/.settings/org.eclipse.wst.common.project.facet.core.xml b/org.eclipse.gemini.web.tomcat/.settings/org.eclipse.wst.common.project.facet.core.xml
new file mode 100644
index 0000000..801f856
--- /dev/null
+++ b/org.eclipse.gemini.web.tomcat/.settings/org.eclipse.wst.common.project.facet.core.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<faceted-project>
+ <installed facet="com.springsource.server.bundle" version="1.0"/>
+</faceted-project>
diff --git a/org.eclipse.gemini.web.tomcat/.springBeans b/org.eclipse.gemini.web.tomcat/.springBeans
new file mode 100644
index 0000000..2257068
--- /dev/null
+++ b/org.eclipse.gemini.web.tomcat/.springBeans
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<beansProjectDescription>
+ <version>1</version>
+ <pluginVersion><![CDATA[2.2.2.RELEASE]]></pluginVersion>
+ <configSuffixes>
+ <configSuffix><![CDATA[xml]]></configSuffix>
+ </configSuffixes>
+ <enableImports><![CDATA[false]]></enableImports>
+ <configs>
+ </configs>
+ <configSets>
+ </configSets>
+</beansProjectDescription>
diff --git a/org.eclipse.gemini.web.tomcat/build.xml b/org.eclipse.gemini.web.tomcat/build.xml
new file mode 100644
index 0000000..5b8399c
--- /dev/null
+++ b/org.eclipse.gemini.web.tomcat/build.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project name="org.eclipse.gemini.web.tomcat">
+
+ <property name="clover.coverage" value="15%"/>
+ <property name="findbugs.exclude.file" value="${basedir}/findbugs-exclude.xml"/>
+ <property file="${basedir}/../build.properties"/>
+ <property file="${basedir}/../build.versions"/>
+ <import file="${basedir}/../virgo-build/standard/default.xml"/>
+
+ <target name="collect">
+ <copy file="${jar.output.file}" tofile="${collect.output.dir}/${ant.project.name}.jar"/>
+ </target>
+</project>
diff --git a/org.eclipse.gemini.web.tomcat/findbugs-exclude.xml b/org.eclipse.gemini.web.tomcat/findbugs-exclude.xml
new file mode 100644
index 0000000..b98ad71
--- /dev/null
+++ b/org.eclipse.gemini.web.tomcat/findbugs-exclude.xml
@@ -0,0 +1,23 @@
+<FindBugsFilter>
+ <!-- Exclusions -->
+ <Match>
+ <Bug pattern="EI_EXPOSE_REP"/>
+ <Or>
+ <Class name="org.eclipse.virgo.kernel.config.internal.validate.KernelConfigPointSchemaWalker"/>
+ <Class name="org.eclipse.virgo.kernel.config.internal.validate.KernelConfigPointSchemaParser"/>
+ </Or>
+ <Method name="getTokenNames"/>
+ </Match>
+ <Match>
+ <Bug pattern="SIC_INNER_SHOULD_BE_STATIC"/>
+ <Class name="org.eclipse.virgo.kernel.config.internal.validate.KernelConfigPointSchemaParser$DFA15"/>
+ </Match>
+ <Match>
+ <Bug pattern="MS_PKGPROTECT"/>
+ <Or>
+ <Class name="org.eclipse.virgo.kernel.config.internal.validate.KernelConfigPointSchemaParser"/>
+ <Class name="org.eclipse.virgo.kernel.config.internal.validate.KernelConfigPointSchemaWalker"/>
+ </Or>
+ <Field name="tokenNames"/>
+ </Match>
+</FindBugsFilter>
diff --git a/org.eclipse.gemini.web.tomcat/ivy.xml b/org.eclipse.gemini.web.tomcat/ivy.xml
new file mode 100644
index 0000000..d3cd515
--- /dev/null
+++ b/org.eclipse.gemini.web.tomcat/ivy.xml
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?xml-stylesheet type="text/xsl" href="http://ivyrep.jayasoft.org/ivy-doc.xsl"?>
+<ivy-module
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:noNamespaceSchemaLocation="http://incubator.apache.org/ivy/schemas/ivy.xsd"
+ version="1.3">
+
+ <info organisation="org.eclipse.gemini.web" module="${ant.project.name}"/>
+
+ <configurations>
+ <include file="${virgo.build.dir}/common/default-ivy-configurations.xml"/>
+ </configurations>
+
+ <publications>
+ <artifact name="${ant.project.name}"/>
+ <artifact name="${ant.project.name}-sources" type="src" ext="jar"/>
+ </publications>
+
+ <dependencies>
+ <dependency org="org.eclipse.virgo.util" name="org.eclipse.virgo.util.osgi" rev="${org.eclipse.virgo.util}" conf="compile->runtime"/>
+ <dependency org="org.eclipse.virgo.util" name="org.eclipse.virgo.util.io" rev="${org.eclipse.virgo.util}" conf="compile->runtime"/>
+ <dependency org="org.eclipse.osgi" name="org.eclipse.osgi" rev="${org.eclipse.osgi}" conf="compile->compile"/>
+ <dependency org="javax.servlet" name="com.springsource.javax.servlet" rev="${javax.servlet}" conf="compile->runtime"/>
+
+
+ <dependency org="org.slf4j" name="com.springsource.slf4j.api" rev="${org.slf4j}" conf="compile->runtime"/>
+ <dependency org="org.slf4j" name="com.springsource.slf4j.nop" rev="${org.slf4j}" conf="test->runtime"/>
+ <dependency org="org.eclipse.gemini.web" name="org.eclipse.gemini.web.core" rev="latest.integration" conf="compile->compile"/>
+ <dependency org="javax.annotation" name="com.springsource.javax.annotation" rev="${javax.annotation}" conf="compile->runtime"/>
+ <dependency org="javax.ejb" name="com.springsource.javax.ejb" rev="${javax.ejb}" conf="compile->runtime,provided"/>
+ <dependency org="javax.el" name="com.springsource.javax.el" rev="${javax.el}" conf="compile->runtime"/>
+ <dependency org="javax.persistence" name="com.springsource.javax.persistence" rev="${javax.persistence}" conf="compile->runtime"/>
+ <dependency org="javax.servlet" name="com.springsource.javax.servlet.jsp" rev="${javax.servlet.jsp}" conf="compile->runtime"/>
+ <dependency org="javax.servlet" name="com.springsource.javax.servlet" rev="${javax.servlet}" conf="compile->runtime"/>
+ <dependency org="javax.xml.ws" name="com.springsource.javax.xml.ws" rev="${javax.xml.ws}" conf="compile->runtime"/>
+ <dependency org="javax.xml.soap" name="com.springsource.javax.xml.soap" rev="${javax.xml.soap}" />
+ <dependency org="javax.xml.bind" name="com.springsource.javax.xml.bind" rev="${javax.xml.bind}" />
+ <dependency org="javax.xml.stream" name="com.springsource.javax.xml.stream" rev="${javax.xml.stream}" />
+ <dependency org="org.apache.jasper.springsource" name="com.springsource.org.apache.jasper.springsource" rev="${org.apache.tomcat}" conf="compile->runtime"/>
+ <dependency org="org.apache.el.springsource" name="com.springsource.org.apache.el.springsource" rev="${org.apache.tomcat}" conf="compile->runtime"/>
+ <dependency org="org.apache.jasper.springsource" name="com.springsource.org.apache.jasper.org.eclipse.jdt.springsource" rev="${org.apache.tomcat}" conf="compile->runtime"/>
+ <dependency org="org.apache.catalina.springsource" name="com.springsource.org.apache.catalina.springsource" rev="${org.apache.tomcat}" conf="compile->runtime"/>
+ <dependency org="org.apache.catalina.springsource" name="com.springsource.org.apache.catalina.ha.springsource" rev="${org.apache.tomcat}" conf="compile->runtime"/>
+ <dependency org="org.apache.catalina.springsource" name="com.springsource.org.apache.catalina.tribes.springsource" rev="${org.apache.tomcat}" conf="compile->runtime"/>
+ <dependency org="org.apache.coyote.springsource" name="com.springsource.org.apache.coyote.springsource" rev="${org.apache.tomcat}" conf="compile->runtime"/>
+ <dependency org="org.apache.juli.springsource" name="com.springsource.org.apache.juli.extras.springsource" rev="${org.apache.tomcat}" conf="compile->runtime"/>
+
+ <dependency org="org.springframework.osgi" name="org.springframework.osgi.core" rev="${org.springframework.osgi}" conf="compile->runtime"/>
+
+ <dependency org="org.junit" name="com.springsource.org.junit" rev="${org.junit}" conf="test->runtime"/>
+ <dependency org="org.easymock" name="com.springsource.org.easymock" rev="${org.easymock}" conf="test->runtime"/>
+ <dependency org="org.eclipse.virgo.teststubs" name="org.eclipse.virgo.teststubs.osgi" rev="${org.eclipse.virgo.teststubs}" conf="test->runtime"/>
+ </dependencies>
+
+</ivy-module>
diff --git a/org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/Activator.java b/org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/Activator.java
new file mode 100644
index 0000000..c75f0bd
--- /dev/null
+++ b/org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/Activator.java
@@ -0,0 +1,113 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 VMware Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Apache License v2.0 which accompanies this distribution.
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Apache License v2.0 is available at
+ * http://www.opensource.org/licenses/apache2.0.php.
+ * You may elect to redistribute this code under either of these licenses.
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.gemini.web.tomcat.internal;
+
+import java.io.InputStream;
+import java.util.Properties;
+
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.service.packageadmin.PackageAdmin;
+import org.osgi.service.url.URLConstants;
+import org.osgi.service.url.URLStreamHandlerService;
+
+
+import org.eclipse.gemini.web.core.WebContainerProperties;
+import org.eclipse.gemini.web.core.spi.ServletContainer;
+import org.eclipse.gemini.web.tomcat.internal.loading.DirContextURLStreamHandlerService;
+import org.eclipse.virgo.util.io.IOUtils;
+import org.eclipse.virgo.util.osgi.ServiceRegistrationTracker;
+
+public class Activator implements BundleActivator {
+
+ private final Object monitor = new Object();
+
+ private final ServiceRegistrationTracker tracker = new ServiceRegistrationTracker();
+
+ private TomcatServletContainer container;
+
+ public void start(BundleContext context) throws Exception {
+ registerURLStreamHandler(context);
+ registerConnectorDescriptors(context);
+
+ TomcatServletContainer container = createContainer(context);
+ container.start();
+
+ ServiceRegistration sr = context.registerService(ServletContainer.class.getName(), container, null);
+ this.tracker.track(sr);
+
+ synchronized (this.monitor) {
+ this.container = container;
+ }
+ }
+
+ private void registerConnectorDescriptors(BundleContext context) {
+ TomcatWebContainerProperties tomcatWebContainerProperties = new TomcatWebContainerProperties();
+ ServiceRegistration registration = context.registerService(WebContainerProperties.class.getName(), tomcatWebContainerProperties, null);
+ this.tracker.track(registration);
+ }
+
+ private void registerURLStreamHandler(BundleContext context) {
+ Properties properties = new Properties();
+ properties.put(URLConstants.URL_HANDLER_PROTOCOL, "jndi");
+
+ DirContextURLStreamHandlerService handler = new DirContextURLStreamHandlerService();
+ ServiceRegistration reg = context.registerService(URLStreamHandlerService.class.getName(), handler, properties);
+ this.tracker.track(reg);
+ }
+
+ public void stop(BundleContext context) throws Exception {
+ TomcatServletContainer container;
+ synchronized (this.monitor) {
+ container = this.container;
+ this.container = null;
+ }
+ if (container != null) {
+ container.stop();
+ }
+ this.tracker.unregisterAll();
+ }
+
+ private TomcatServletContainer createContainer(BundleContext context) throws BundleException {
+ TomcatServletContainerFactory factory = new TomcatServletContainerFactory();
+ InputStream configFile = resolveConfigFile(context);
+ try {
+ return factory.createContainer(configFile, context, getPackageAdmin(context));
+ } finally {
+ IOUtils.closeQuietly(configFile);
+ }
+ }
+
+ private InputStream resolveConfigFile(BundleContext context) throws BundleException {
+ return TomcatConfigLocator.resolveConfigFile(context);
+ }
+
+ private PackageAdmin getPackageAdmin(BundleContext bundleContext) {
+ ServiceReference serviceReference = bundleContext.getServiceReference(PackageAdmin.class.getName());
+ if (serviceReference != null) {
+ PackageAdmin packageAdmin = (PackageAdmin) bundleContext.getService(serviceReference);
+ if (packageAdmin != null) {
+ return packageAdmin;
+ }
+ }
+
+ throw new IllegalStateException("PackageAdmin not available in the service registry");
+ }
+}
diff --git a/org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/BundleDependenciesJarScanner.java b/org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/BundleDependenciesJarScanner.java
new file mode 100644
index 0000000..c0c1f43
--- /dev/null
+++ b/org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/BundleDependenciesJarScanner.java
@@ -0,0 +1,133 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 VMware Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Apache License v2.0 which accompanies this distribution.
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Apache License v2.0 is available at
+ * http://www.opensource.org/licenses/apache2.0.php.
+ * You may elect to redistribute this code under either of these licenses.
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.gemini.web.tomcat.internal;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.JarURLConnection;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.Set;
+
+import javax.servlet.ServletContext;
+
+import org.apache.tomcat.JarScanner;
+import org.apache.tomcat.JarScannerCallback;
+import org.eclipse.gemini.web.tomcat.internal.loading.BundleWebappClassLoader;
+import org.eclipse.gemini.web.tomcat.internal.support.BundleDependencyDeterminer;
+import org.eclipse.gemini.web.tomcat.internal.support.BundleFileResolver;
+import org.osgi.framework.Bundle;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * A <code>JarScanner</code> implementation that passes each of the
+ * {@link Bundle}'s dependencies to the {@link JarScannerCallback}.
+ *
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * Thread-safe.
+ *
+ */
+final class BundleDependenciesJarScanner implements JarScanner {
+
+ private static final String JAR_URL_SUFFIX = "!/";
+
+ private static final String JAR_URL_PREFIX = "jar:";
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(BundleDependenciesJarScanner.class);
+
+ private final BundleDependencyDeterminer bundleDependencyDeterminer;
+
+ private final BundleFileResolver bundleFileResolver;
+
+ public BundleDependenciesJarScanner(BundleDependencyDeterminer bundleDependencyDeterminer, BundleFileResolver bundleFileResolver) {
+ this.bundleDependencyDeterminer = bundleDependencyDeterminer;
+ this.bundleFileResolver = bundleFileResolver;
+ }
+
+ public void scan(ServletContext context, ClassLoader classLoader, JarScannerCallback callback, Set<String> jarsToSkip) {
+ if (classLoader instanceof BundleWebappClassLoader) {
+ Bundle bundle = ((BundleWebappClassLoader)classLoader).getBundle();
+ scanDependentBundles(bundle, callback);
+ }
+ }
+
+ private void scanDependentBundles(Bundle rootBundle, JarScannerCallback callback) {
+ Set<Bundle> dependencies = this.bundleDependencyDeterminer.getDependencies(rootBundle);
+
+ for (Bundle bundle : dependencies) {
+ scanBundle(bundle, callback);
+ }
+ }
+
+ private void scanBundle(Bundle bundle, JarScannerCallback callback) {
+ File bundleFile = this.bundleFileResolver.resolve(bundle);
+ if (bundleFile != null) {
+ scanBundleFile(bundleFile, callback);
+ } else {
+ scanJarUrlConnection(bundle, callback);
+ }
+ }
+
+ private void scanJarUrlConnection(Bundle bundle, JarScannerCallback callback) {
+ URL bundleUrl;
+ try {
+ bundleUrl = new URL(JAR_URL_PREFIX + bundle.getLocation() + JAR_URL_SUFFIX);
+ } catch (MalformedURLException e) {
+ LOGGER.warn("Failed to create jar: url for bundle location " + bundle.getLocation());
+ return;
+ }
+
+ scanBundleUrl(bundleUrl, callback);
+ }
+
+ private void scanBundleFile(File bundleFile, JarScannerCallback callback) {
+ if (bundleFile.isDirectory()) {
+ try {
+ callback.scan(bundleFile);
+ } catch (IOException e) {
+ LOGGER.warn("Failure when attempting to scan bundle file '" + bundleFile + "'.", e);
+ }
+ } else {
+ URL bundleUrl;
+ try {
+ bundleUrl = new URL(JAR_URL_PREFIX + bundleFile.toURI().toURL() + JAR_URL_SUFFIX);
+ } catch (MalformedURLException e) {
+ LOGGER.warn("Failed to create jar: url for bundle file " + bundleFile);
+ return;
+ }
+ scanBundleUrl(bundleUrl, callback);
+ }
+ }
+
+ private void scanBundleUrl(URL url, JarScannerCallback callback) {
+ try {
+ URLConnection connection = url.openConnection();
+
+ if (connection instanceof JarURLConnection) {
+ callback.scan((JarURLConnection)connection);
+ }
+ } catch (IOException e) {
+ LOGGER.warn("Failure when attempting to scan bundle via jar URL '" + url + "'.", e);
+ }
+ }
+}
diff --git a/org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/ChainingJarScanner.java b/org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/ChainingJarScanner.java
new file mode 100644
index 0000000..a7a6316
--- /dev/null
+++ b/org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/ChainingJarScanner.java
@@ -0,0 +1,49 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 VMware Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Apache License v2.0 which accompanies this distribution.
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Apache License v2.0 is available at
+ * http://www.opensource.org/licenses/apache2.0.php.
+ * You may elect to redistribute this code under either of these licenses.
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.gemini.web.tomcat.internal;
+
+import java.util.Set;
+
+import javax.servlet.ServletContext;
+
+import org.apache.tomcat.JarScanner;
+import org.apache.tomcat.JarScannerCallback;
+
+/**
+ * A <code>JarScanner</code> implementation that delegates to a chain of <code>JarScanner</code>s.
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * Thread-safe.
+ *
+ */
+final class ChainingJarScanner implements JarScanner {
+
+ private final JarScanner[] jarScanners;
+
+ public ChainingJarScanner(JarScanner... jarScanners) {
+ this.jarScanners = jarScanners;
+ }
+
+ public void scan(ServletContext context, ClassLoader classloader, JarScannerCallback callback, Set<String> jarsToSkip) {
+ for (JarScanner jarScanner : jarScanners) {
+ jarScanner.scan(context, classloader, callback, jarsToSkip);
+ }
+ }
+
+}
diff --git a/org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/DelegatingClassLoaderCustomizer.java b/org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/DelegatingClassLoaderCustomizer.java
new file mode 100644
index 0000000..cb2be14
--- /dev/null
+++ b/org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/DelegatingClassLoaderCustomizer.java
@@ -0,0 +1,94 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 VMware Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Apache License v2.0 which accompanies this distribution.
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Apache License v2.0 is available at
+ * http://www.opensource.org/licenses/apache2.0.php.
+ * You may elect to redistribute this code under either of these licenses.
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.gemini.web.tomcat.internal;
+
+import java.lang.instrument.ClassFileTransformer;
+
+import org.eclipse.gemini.web.tomcat.spi.ClassLoaderCustomizer;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.util.tracker.ServiceTracker;
+import org.osgi.util.tracker.ServiceTrackerCustomizer;
+
+
+final class DelegatingClassLoaderCustomizer implements ClassLoaderCustomizer {
+
+ private final BundleContext context;
+
+ private final ServiceTracker tracker;
+
+ private volatile ClassLoaderCustomizer delegate;
+
+ public DelegatingClassLoaderCustomizer(BundleContext context) {
+ this.context = context;
+ this.tracker = new ServiceTracker(context, ClassLoaderCustomizer.class.getName(), new Customizer());
+ }
+
+ public void open() {
+ this.tracker.open();
+ }
+
+ public void close() {
+ this.tracker.close();
+ }
+
+ public void addClassFileTransformer(ClassFileTransformer transformer, Bundle bundle) {
+ if (this.delegate != null) {
+ this.delegate.addClassFileTransformer(transformer, bundle);
+ }
+ }
+
+ public ClassLoader createThrowawayClassLoader(Bundle bundle) {
+ if (this.delegate != null) {
+ return this.delegate.createThrowawayClassLoader(bundle);
+ } else {
+ return null;
+ }
+ }
+
+ public ClassLoader[] extendClassLoaderChain(Bundle bundle) {
+ if (this.delegate != null) {
+ return this.delegate.extendClassLoaderChain(bundle);
+ } else {
+ return new ClassLoader[0];
+ }
+ }
+
+ private class Customizer implements ServiceTrackerCustomizer {
+
+ public Object addingService(ServiceReference reference) {
+ ClassLoaderCustomizer newDelegate = (ClassLoaderCustomizer) context.getService(reference);
+
+ if (delegate == null) {
+ delegate = newDelegate;
+ }
+
+ return newDelegate;
+ }
+
+ public void modifiedService(ServiceReference reference, Object service) {
+ // no-op
+ }
+
+ public void removedService(ServiceReference reference, Object service) {
+ context.ungetService(reference);
+ delegate = null;
+ }
+
+ }
+}
diff --git a/org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/Tomcat.java b/org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/Tomcat.java
new file mode 100644
index 0000000..e1c5b94
--- /dev/null
+++ b/org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/Tomcat.java
@@ -0,0 +1,214 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 VMware Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Apache License v2.0 which accompanies this distribution.
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Apache License v2.0 is available at
+ * http://www.opensource.org/licenses/apache2.0.php.
+ * You may elect to redistribute this code under either of these licenses.
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.gemini.web.tomcat.internal;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.apache.catalina.Container;
+import org.apache.catalina.Context;
+import org.apache.catalina.Engine;
+import org.apache.catalina.Host;
+import org.apache.catalina.Lifecycle;
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.Server;
+import org.apache.catalina.Service;
+import org.apache.catalina.connector.Connector;
+import org.apache.catalina.core.StandardContext;
+import org.apache.catalina.startup.Catalina;
+import org.apache.catalina.startup.ContextConfig;
+import org.apache.catalina.startup.DefaultJarScanner;
+import org.apache.catalina.startup.Embedded;
+import org.apache.tomcat.JarScanner;
+import org.apache.tomcat.util.digester.Digester;
+import org.eclipse.gemini.web.core.spi.ServletContainerException;
+import org.eclipse.gemini.web.tomcat.internal.loading.ChainedClassLoader;
+import org.eclipse.gemini.web.tomcat.internal.support.BundleFileResolverFactory;
+import org.eclipse.gemini.web.tomcat.internal.support.PackageAdminBundleDependencyDeterminer;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.packageadmin.PackageAdmin;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.xml.sax.SAXException;
+
+
+final class Tomcat extends Embedded {
+
+ private static final String ROOT_CONTEXT_PATH = "";
+
+ private static final String ROOT_PATH = "/";
+
+ private final static Logger LOGGER = LoggerFactory.getLogger(Tomcat.class);
+
+ private final ExtendCatalina catalina = new ExtendCatalina();
+
+ private final JarScanner jarScanner;
+
+ Tomcat(BundleContext context, PackageAdmin packageAdmin) {
+ JarScanner bundleDependenciesJarScanner = new BundleDependenciesJarScanner(new PackageAdminBundleDependencyDeterminer(context, packageAdmin),
+ BundleFileResolverFactory.createBundleFileResolver());
+ JarScanner defaultJarScanner = new DefaultJarScanner();
+
+ this.jarScanner = new ChainingJarScanner(bundleDependenciesJarScanner, defaultJarScanner);
+ }
+
+ public void start() throws LifecycleException {
+ super.start();
+
+ Server server = getServer();
+ if (server instanceof Lifecycle) {
+ ((Lifecycle) server).start();
+ }
+ }
+
+ public void stop() throws LifecycleException {
+ Server server = getServer();
+
+ stopConnectorsAndContainers(server);
+
+ super.stop();
+ }
+
+ /**
+ *
+ */
+ private void stopConnectorsAndContainers(Server server) throws LifecycleException {
+ Service[] services = server.findServices();
+ if (services != null) {
+ for (Service service : services) {
+ Connector[] connectors = service.findConnectors();
+ for (Connector connector : connectors) {
+ connector.stop();
+ }
+ Container container = service.getContainer();
+ if (container instanceof Lifecycle) {
+ ((Lifecycle)container).stop();
+ }
+ }
+ }
+ }
+
+ public Engine findEngine() {
+ Server server = getServer();
+ Service[] findServices = server.findServices();
+ for (Service service : findServices) {
+ Container container = service.getContainer();
+ if (container instanceof Engine) {
+ return (Engine) container;
+ }
+ }
+ throw new IllegalStateException("Unable to locate Engine.");
+ }
+
+ public Host findHost() {
+ Engine engine = findEngine();
+ Container[] children = engine.findChildren();
+ for (Container container : children) {
+ if (container instanceof Host) {
+ return (Host) container;
+ }
+ }
+ throw new IllegalStateException("Unable to locate Host.");
+ }
+
+ @Override
+ public void initialize() throws LifecycleException {
+ getServer().initialize();
+ }
+
+ @Override
+ public Context createContext(String path, String docBase) {
+ if (LOGGER.isDebugEnabled()) {
+ LOGGER.debug("Creating context '" + path + "' with docBase '" + docBase + "'");
+ }
+
+ StandardContext context = new ExtendedStandardContext();
+
+ context.setDocBase(docBase);
+ context.setPath(path.equals(ROOT_PATH) ? ROOT_CONTEXT_PATH : path);
+
+ context.setJarScanner(this.jarScanner);
+
+ ContextConfig config = new ExtendedContextConfig();
+
+ config.setCustomAuthenticators(this.authenticators);
+ ((Lifecycle) context).addLifecycleListener(config);
+
+ return (context);
+ }
+
+ public void configure(InputStream configuration) {
+ Digester digester = this.catalina.createStartDigester();
+ digester.push(this);
+
+ ClassLoader[] loaders = new ClassLoader[] { Catalina.class.getClassLoader(), getClass().getClassLoader() };
+ digester.setClassLoader(ChainedClassLoader.create(loaders));
+
+ try {
+ digester.parse(configuration);
+ } catch (IOException e) {
+ throw new ServletContainerException("Error reading Tomcat configuration file.", e);
+ } catch (SAXException e) {
+ throw new ServletContainerException("Error parsing Tomcat XML configuration.", e);
+ }
+ }
+
+ /**
+ * Overrides {@link Catalina} to provide public access to {@link Catalina#createStartDigester}.
+ *
+ *
+ */
+ private static class ExtendCatalina extends Catalina {
+
+ @Override
+ public Digester createStartDigester() {
+ return super.createStartDigester();
+ }
+
+ }
+
+ /**
+ * Extends the Tomcat {@link StandardContext} to add custom functionality.
+ *
+ *
+ */
+ private static class ExtendedStandardContext extends StandardContext {
+
+ private static final long serialVersionUID = 6914580440115519171L;
+
+ /**
+ * Returns <code>true</code> for exploded bundles.
+ */
+ @Override
+ public boolean isFilesystemBased() {
+ String docBase = getDocBase();
+ File f = new File(docBase);
+ return f.isDirectory();
+ }
+
+ }
+
+ /**
+ * Override {@link ContextConfig}. This changes the {@link ClassLoader} used to load the web-embed.xml to the
+ * <code>ClassLoader</code> of this bundle.
+ *
+ *
+ */
+ private static class ExtendedContextConfig extends ContextConfig {
+ }
+}
diff --git a/org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/TomcatConfigLocator.java b/org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/TomcatConfigLocator.java
new file mode 100644
index 0000000..837a099
--- /dev/null
+++ b/org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/TomcatConfigLocator.java
@@ -0,0 +1,125 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 VMware Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Apache License v2.0 which accompanies this distribution.
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Apache License v2.0 is available at
+ * http://www.opensource.org/licenses/apache2.0.php.
+ * You may elect to redistribute this code under either of these licenses.
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.gemini.web.tomcat.internal;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.Enumeration;
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Utility class for loading the master Tomcat configuration.
+ * <p/>
+ * The location algorithm is as follows:
+ * <ol>
+ * <li>Check for <code>config/tomcat-server.xml</code> in the current working directory, use if found</code></li>
+ * <li>Check this bundle and attached fragments for <code>/META-INF/tomcat/server.xml, use if found</code></li>
+ * <li>Check this bundle for <code>/META-INF/tomcat/default-server.xml, use if found</code></li>
+ * <li>Throw {@link IllegalStateException} if no configuration is found</li>
+ * </ol>
+ *
+ *
+ */
+final class TomcatConfigLocator {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(TomcatConfigLocator.class);
+
+ static final String CONFIG_PATH_FRAMEWORK_PROPERTY = "org.eclipse.gemini.web.tomcat.config.path";
+
+ static final String DEFAULT_CONFIG_FILE_PATH = "config" + File.separator + "tomcat-server.xml";
+
+ static final String CONFIG_PATH = "META-INF/tomcat";
+
+ static final String DEFAULT_CONFIG_PATH = CONFIG_PATH + "/default-server.xml";
+
+ static final String USER_CONFIG_PATH = "server.xml";
+
+ public static InputStream resolveConfigFile(BundleContext context) throws BundleException {
+ Bundle bundle = context.getBundle();
+
+ InputStream is = lookupConfigInFileSystem(context);
+
+ if (is == null) {
+ is = lookupConfigInBundle(bundle);
+ }
+ return is;
+ }
+
+ private static InputStream lookupConfigInFileSystem(BundleContext context) {
+ InputStream result = null;
+
+ String path = context.getProperty(CONFIG_PATH_FRAMEWORK_PROPERTY);
+ if(path != null) {
+ result = tryGetStreamForFilePath(path);
+ }
+
+ if(result == null) {
+ result = tryGetStreamForFilePath(DEFAULT_CONFIG_FILE_PATH);
+ }
+ return result;
+ }
+
+ private static InputStream tryGetStreamForFilePath(String filePath) {
+ File configFile = new File(filePath);
+ if (configFile.exists()) {
+
+ try {
+ FileInputStream fis = new FileInputStream(configFile);
+ if (LOGGER.isInfoEnabled()) {
+ LOGGER.info("Configuring Tomcat from file '" + configFile + "'");
+ }
+ return fis;
+ } catch (FileNotFoundException e) {
+ LOGGER.warn("Found config file on disk but then received FileNotFoundException when trying to access", e);
+ }
+ }
+ return null;
+ }
+
+ private static InputStream lookupConfigInBundle(Bundle bundle) throws BundleException {
+ URL entry = null;
+ Enumeration<?> entries = bundle.findEntries(CONFIG_PATH, USER_CONFIG_PATH, false);
+ if (entries != null && entries.hasMoreElements()) {
+ entry = (URL) entries.nextElement();
+ if (LOGGER.isInfoEnabled()) {
+ LOGGER.info("Configuring Tomcat from fragment entry '" + entry + "'");
+ }
+ } else {
+ entry = bundle.getEntry(DEFAULT_CONFIG_PATH);
+ if (entry == null) {
+ throw new IllegalStateException("Unable to locate default Tomcat configuration. Is the '" + bundle + "' bundle corrupt?");
+ } else if(LOGGER.isInfoEnabled()) {
+ LOGGER.info("Configuring Tomcat from default config file");
+ }
+ }
+
+ try {
+ return entry.openStream();
+ } catch (IOException e) {
+ throw new BundleException("Unable to open Tomcat configuration at '" + entry + "'");
+ }
+ }
+}
diff --git a/org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/TomcatConnectorDescriptor.java b/org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/TomcatConnectorDescriptor.java
new file mode 100644
index 0000000..e592c41
--- /dev/null
+++ b/org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/TomcatConnectorDescriptor.java
@@ -0,0 +1,77 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 VMware Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Apache License v2.0 which accompanies this distribution.
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Apache License v2.0 is available at
+ * http://www.opensource.org/licenses/apache2.0.php.
+ * You may elect to redistribute this code under either of these licenses.
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.gemini.web.tomcat.internal;
+
+import org.eclipse.gemini.web.core.ConnectorDescriptor;
+
+
+/**
+ * <p>
+ * TomcatConnectorDescriptor is the Tomcat specific implementation of {@link ConnectorDescriptor}.
+ * </p>
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * StandardConnectorDescriptor is threadsafe
+ *
+ */
+public class TomcatConnectorDescriptor implements ConnectorDescriptor {
+
+ private final int port;
+
+ private final String scheme;
+
+ private final String protocol;
+
+ private final boolean sslEnabled;
+
+ public TomcatConnectorDescriptor(String protocol, String scheme, int port, boolean sslEnabled) {
+ this.protocol = protocol;
+ this.scheme = scheme;
+ this.port = port;
+ this.sslEnabled = sslEnabled;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public int getPort() {
+ return this.port;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String getProtocol() {
+ return this.protocol;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String getScheme() {
+ return this.scheme;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean sslEnabled() {
+ return this.sslEnabled;
+ }
+
+}
diff --git a/org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/TomcatMBeanManager.java b/org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/TomcatMBeanManager.java
new file mode 100644
index 0000000..b5068c8
--- /dev/null
+++ b/org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/TomcatMBeanManager.java
@@ -0,0 +1,85 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 VMware Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Apache License v2.0 which accompanies this distribution.
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Apache License v2.0 is available at
+ * http://www.opensource.org/licenses/apache2.0.php.
+ * You may elect to redistribute this code under either of these licenses.
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.gemini.web.tomcat.internal;
+
+import java.lang.management.ManagementFactory;
+import java.util.Set;
+
+import javax.management.InstanceNotFoundException;
+import javax.management.MBeanRegistrationException;
+import javax.management.MBeanServer;
+import javax.management.MalformedObjectNameException;
+import javax.management.ObjectName;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+final class TomcatMBeanManager {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(TomcatMBeanManager.class);
+
+ private final String domain;
+
+ public TomcatMBeanManager(String domain) {
+ this.domain = domain;
+ }
+
+ public void start() {
+ cleanMBeans();
+ }
+
+ public void stop() {
+ cleanMBeans();
+ }
+
+ private void cleanMBeans() {
+ Set<ObjectName> mbeans = findTomcatObjectNames();
+ for (ObjectName objectName : mbeans) {
+ tryUnregister(objectName);
+ }
+
+ }
+
+ private void tryUnregister(ObjectName objectName) {
+ try {
+ getMBeanServer().unregisterMBean(objectName);
+ } catch (InstanceNotFoundException e) {
+ if (LOGGER.isDebugEnabled()) {
+ LOGGER.debug("Attempted to unregister MBean that was not registered '" + objectName + "'");
+ }
+ } catch (MBeanRegistrationException e) {
+ if (LOGGER.isWarnEnabled()) {
+ LOGGER.warn("Error unregistering MBean '" + objectName + "'", e);
+ }
+ }
+ }
+
+ private Set<ObjectName> findTomcatObjectNames() {
+ ObjectName tomcatPattern;
+ try {
+ tomcatPattern = new ObjectName(this.domain + ":*");
+ } catch (MalformedObjectNameException e) {
+ throw new IllegalStateException("Unable to create query pattern.", e);
+ }
+ return getMBeanServer().queryNames(tomcatPattern, null);
+ }
+
+ private MBeanServer getMBeanServer() {
+ return ManagementFactory.getPlatformMBeanServer();
+ }
+
+}
diff --git a/org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/TomcatServletContainer.java b/org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/TomcatServletContainer.java
new file mode 100644
index 0000000..e9493f1
--- /dev/null
+++ b/org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/TomcatServletContainer.java
@@ -0,0 +1,255 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 VMware Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Apache License v2.0 which accompanies this distribution.
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Apache License v2.0 is available at
+ * http://www.opensource.org/licenses/apache2.0.php.
+ * You may elect to redistribute this code under either of these licenses.
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.gemini.web.tomcat.internal;
+
+import java.io.File;
+
+import javax.servlet.ServletContext;
+
+import org.apache.catalina.Container;
+import org.apache.catalina.Context;
+import org.apache.catalina.Host;
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.core.StandardContext;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
+
+
+import org.eclipse.gemini.web.core.spi.ContextPathExistsException;
+import org.eclipse.gemini.web.core.spi.ServletContainer;
+import org.eclipse.gemini.web.core.spi.ServletContainerException;
+import org.eclipse.gemini.web.core.spi.WebApplicationHandle;
+import org.eclipse.gemini.web.tomcat.internal.loading.BundleDirContext;
+import org.eclipse.gemini.web.tomcat.internal.loading.BundleWebappLoader;
+import org.eclipse.gemini.web.tomcat.internal.loading.ChainedClassLoader;
+import org.eclipse.gemini.web.tomcat.internal.loading.StandardWebBundleClassLoaderFactory;
+import org.eclipse.gemini.web.tomcat.internal.support.BundleFileResolver;
+import org.eclipse.gemini.web.tomcat.internal.support.BundleFileResolverFactory;
+import org.eclipse.gemini.web.tomcat.spi.WebBundleClassLoaderFactory;
+import org.eclipse.virgo.util.osgi.ServiceRegistrationTracker;
+
+final class TomcatServletContainer implements ServletContainer {
+
+ private final TomcatMBeanManager mbeanManager;
+
+ private final Tomcat tomcat;
+
+ private final DelegatingClassLoaderCustomizer classLoaderCustomizer;
+
+ private final ServiceRegistrationTracker registrationTracker = new ServiceRegistrationTracker();
+
+ private final BundleContext context;
+
+ public TomcatServletContainer(Tomcat tomcat, BundleContext context) {
+ this.classLoaderCustomizer = new DelegatingClassLoaderCustomizer(context);
+ this.tomcat = tomcat;
+ this.tomcat.init();
+ this.mbeanManager = new TomcatMBeanManager(tomcat.findEngine().getName());
+ this.context = context;
+ }
+
+ public void start() {
+ try {
+ this.classLoaderCustomizer.open();
+
+ WebBundleClassLoaderFactory classLoaderFactory = new StandardWebBundleClassLoaderFactory(this.classLoaderCustomizer);
+ ServiceRegistration registration = this.context.registerService(WebBundleClassLoaderFactory.class.getName(), classLoaderFactory, null);
+ this.registrationTracker.track(registration);
+
+ this.mbeanManager.start();
+ doStart();
+ } catch (LifecycleException e) {
+ throw new ServletContainerException("Unable to start Tomcat.", e);
+ }
+ }
+
+ public void stop() {
+ try {
+ doStop();
+ this.mbeanManager.stop();
+ this.registrationTracker.unregisterAll();
+ this.classLoaderCustomizer.close();
+ } catch (LifecycleException e) {
+ throw new ServletContainerException("Error stopping Tomcat", e);
+ }
+ }
+
+ public WebApplicationHandle createWebApplication(String contextPath, Bundle bundle) {
+ contextPath = formatContextPath(contextPath);
+
+ try {
+ String docBase = determineDocBase(bundle);
+
+ StandardContext context = (StandardContext) this.tomcat.createContext(contextPath, docBase);
+
+ BundleWebappLoader loader = new BundleWebappLoader(bundle, this.classLoaderCustomizer);
+ context.setLoader(loader);
+ context.setResources(new BundleDirContext(bundle));
+
+ ServletContext servletContext = context.getServletContext();
+
+ return new TomcatWebApplicationHandle(servletContext, context, loader);
+ } catch (Exception ex) {
+ throw new ServletContainerException("Unablo te create web application for context path '" + contextPath + "'", ex);
+ }
+ }
+
+ public void startWebApplication(WebApplicationHandle handle) {
+ String contextPath = handle.getServletContext().getContextPath();
+ Host host = this.tomcat.findHost();
+
+ checkContextPathIsFree(contextPath, host);
+
+ StandardContext context = extractTomcatContext(handle);
+
+ host.addChild(context);
+ if (!context.getAvailable()) {
+ host.removeChild(context);
+ throw new ServletContainerException("Web application at '" + contextPath + "' failed to start. Check the logs for more details.");
+ }
+
+ }
+
+ public void stopWebApplication(WebApplicationHandle handle) {
+ StandardContext context = extractTomcatContext(handle);
+ try {
+ removeContext(context);
+ } finally {
+ try {
+ stopContext(context);
+ } finally {
+ destroyContext(context);
+ }
+ }
+ }
+
+ private void doStart() throws LifecycleException {
+ ClassLoader current = Thread.currentThread().getContextClassLoader();
+ try {
+ Thread.currentThread().setContextClassLoader(createThreadContextClassLoader());
+ this.tomcat.start();
+ } finally {
+ Thread.currentThread().setContextClassLoader(current);
+ }
+ }
+
+ private void doStop() throws LifecycleException {
+ ClassLoader current = Thread.currentThread().getContextClassLoader();
+ try {
+ Thread.currentThread().setContextClassLoader(createThreadContextClassLoader());
+ this.tomcat.stop();
+ } finally {
+ Thread.currentThread().setContextClassLoader(current);
+ }
+ }
+
+ private ClassLoader createThreadContextClassLoader() {
+ return ChainedClassLoader.create(getClass().getClassLoader(), Context.class.getClassLoader());
+ }
+
+ private void removeContext(StandardContext context) {
+ try {
+ Host host = this.tomcat.findHost();
+ host.removeChild(context);
+ } catch (Exception e) {
+ throw new ServletContainerException("Unable to remove web application with context path '" + context.getServletContext().getContextPath()
+ + "'");
+ }
+ }
+
+ private void stopContext(StandardContext context) {
+ try {
+ context.stop();
+ } catch (Exception e) {
+ throw new ServletContainerException("Error stopping web application with context path '" + context.getServletContext().getContextPath()
+ + "'");
+ }
+ }
+
+ private void destroyContext(StandardContext context) {
+ try {
+ context.destroy();
+ } catch (Exception e) {
+ throw new ServletContainerException("Error destroying web application with context path '" + context.getServletContext().getContextPath()
+ + "'");
+ }
+ }
+
+ /**
+ * A context path can only be bound to one application. This method checks to see if a given context path is free,
+ * throwing {@link ContextPathExistsException} if not.
+ *
+ * @param contextPath the context path
+ * @param host the {@link Host} to check for duplicate context paths.
+ * @throws ContextPathExistsException if the context path is already used.
+ */
+ private void checkContextPathIsFree(String contextPath, Host host) {
+ Container existingContext = host.findChild(contextPath);
+ if (existingContext != null) {
+ throw new ContextPathExistsException(contextPath);
+ }
+ }
+
+ private String determineDocBase(Bundle bundle) {
+ BundleFileResolver resolver = BundleFileResolverFactory.createBundleFileResolver();
+ File root = resolver.resolve(bundle);
+ return root != null ? root.getAbsolutePath() : "";
+ }
+
+ private StandardContext extractTomcatContext(WebApplicationHandle handle) {
+ if (!(handle instanceof TomcatWebApplicationHandle)) {
+ throw new IllegalStateException("Unrecognized handle type '" + handle.getClass() + "'.");
+ }
+ return ((TomcatWebApplicationHandle) handle).getContext();
+ }
+
+ private String formatContextPath(String contextPath) {
+ if (!contextPath.startsWith("/")) {
+ contextPath = "/" + contextPath;
+ }
+ return contextPath;
+ }
+
+ private static class TomcatWebApplicationHandle implements WebApplicationHandle {
+
+ private final ServletContext servletContext;
+
+ private final StandardContext context;
+
+ private final BundleWebappLoader webappLoader;
+
+ private TomcatWebApplicationHandle(ServletContext servletContext, StandardContext context, BundleWebappLoader webappLoader) {
+ this.servletContext = servletContext;
+ this.context = context;
+ this.webappLoader = webappLoader;
+ }
+
+ public ServletContext getServletContext() {
+ return this.servletContext;
+ }
+
+ public StandardContext getContext() {
+ return context;
+ }
+
+ public ClassLoader getClassLoader() {
+ return this.webappLoader.getClassLoader();
+ }
+
+ }
+}
diff --git a/org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/TomcatServletContainerFactory.java b/org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/TomcatServletContainerFactory.java
new file mode 100644
index 0000000..78e6943
--- /dev/null
+++ b/org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/TomcatServletContainerFactory.java
@@ -0,0 +1,32 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 VMware Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Apache License v2.0 which accompanies this distribution.
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Apache License v2.0 is available at
+ * http://www.opensource.org/licenses/apache2.0.php.
+ * You may elect to redistribute this code under either of these licenses.
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.gemini.web.tomcat.internal;
+
+import java.io.InputStream;
+
+import org.osgi.framework.BundleContext;
+import org.osgi.service.packageadmin.PackageAdmin;
+
+final class TomcatServletContainerFactory {
+
+ public TomcatServletContainer createContainer(InputStream configuration, BundleContext context, PackageAdmin packageAdmin) {
+ Tomcat catalina = new Tomcat(context, packageAdmin);
+ catalina.configure(configuration);
+
+ return new TomcatServletContainer(catalina, context);
+ }
+}
diff --git a/org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/TomcatWebContainerProperties.java b/org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/TomcatWebContainerProperties.java
new file mode 100644
index 0000000..464928d
--- /dev/null
+++ b/org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/TomcatWebContainerProperties.java
@@ -0,0 +1,101 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 VMware Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Apache License v2.0 which accompanies this distribution.
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Apache License v2.0 is available at
+ * http://www.opensource.org/licenses/apache2.0.php.
+ * You may elect to redistribute this code under either of these licenses.
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.gemini.web.tomcat.internal;
+
+import java.lang.management.ManagementFactory;
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+
+import org.eclipse.gemini.web.core.ConnectorDescriptor;
+import org.eclipse.gemini.web.core.WebContainerProperties;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+
+
+/**
+ * <p>
+ * TODO Document TomcatWebContainerProperties
+ * </p>
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * TODO Document concurrent semantics of TomcatWebContainerProperties
+ *
+ */
+final class TomcatWebContainerProperties implements WebContainerProperties {
+
+ private static final String CATALINA_TYPE_PROTOCOL_HANDLER = "Catalina:type=ProtocolHandler,*";
+
+ private final static Logger LOGGER = LoggerFactory.getLogger(TomcatWebContainerProperties.class);
+
+ private static final String ATTRIBUTE_MODELER_TYPE = "modelerType";
+
+ private static final String ATTRIBUTE_SSL_ENABLED = "sSLEnabled";
+
+ private static final String ATTRIBUTE_NAME = "name";
+
+ private static final String ATTRIBUTE_PORT = "port";
+
+ /**
+ * {@inheritDoc}
+ */
+ public Set<ConnectorDescriptor> getConnectorDescriptors() {
+ Set<ConnectorDescriptor> connectorDescriptors = new HashSet<ConnectorDescriptor>();
+ MBeanServer mBeanServer = this.getMBeanServer();
+ try {
+ ObjectName portNamesQuery = new ObjectName(CATALINA_TYPE_PROTOCOL_HANDLER);
+ Set<ObjectName> portMBeanNames = mBeanServer.queryNames(portNamesQuery, null);
+ for(ObjectName objectName : portMBeanNames) {
+
+ Object attribute = this.getAttribute(mBeanServer, objectName, ATTRIBUTE_MODELER_TYPE);
+ Object modler = attribute == null ? "" : attribute;
+
+ attribute = this.getAttribute(mBeanServer, objectName, ATTRIBUTE_SSL_ENABLED);
+ Object sslEnabled = attribute == null ? false : attribute;
+
+ attribute = this.getAttribute(mBeanServer, objectName, ATTRIBUTE_NAME);
+ Object name = attribute == null ? "" : attribute;
+
+ attribute = objectName.getKeyProperty(ATTRIBUTE_PORT);
+ Object port = attribute == null ? -1 : attribute;
+
+ connectorDescriptors.add(new TomcatConnectorDescriptor(modler.toString(), name.toString(), Integer.valueOf(port.toString()), Boolean.valueOf(sslEnabled.toString())));
+ }
+ } catch (Exception e) {
+ LOGGER.warn("Unable to obtain the Tomcat port number from its MBeans", e);
+ }
+ return connectorDescriptors;
+ }
+
+ private MBeanServer getMBeanServer() {
+ return ManagementFactory.getPlatformMBeanServer();
+ }
+
+ private Object getAttribute(MBeanServer mBeanServer, ObjectName objectName, String attributeName) {
+ try {
+ return mBeanServer.getAttribute(objectName, attributeName);
+ } catch (Exception e) {
+ return null;
+ }
+ }
+
+}
diff --git a/org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/loading/AbstractReadOnlyDirContext.java b/org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/loading/AbstractReadOnlyDirContext.java
new file mode 100644
index 0000000..998170d
--- /dev/null
+++ b/org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/loading/AbstractReadOnlyDirContext.java
@@ -0,0 +1,138 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 VMware Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Apache License v2.0 which accompanies this distribution.
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Apache License v2.0 is available at
+ * http://www.opensource.org/licenses/apache2.0.php.
+ * You may elect to redistribute this code under either of these licenses.
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.gemini.web.tomcat.internal.loading;
+
+import java.util.Hashtable;
+
+import javax.naming.Context;
+import javax.naming.Name;
+import javax.naming.NameParser;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.ModificationItem;
+import javax.naming.directory.SearchControls;
+import javax.naming.directory.SearchResult;
+
+import org.apache.naming.resources.BaseDirContext;
+
+
+@SuppressWarnings("unchecked")
+abstract class AbstractReadOnlyDirContext extends BaseDirContext {
+
+ public void bind(String name, Object obj, Attributes attrs) throws NamingException {
+ throw new UnsupportedOperationException();
+ }
+
+ public void modifyAttributes(String name, ModificationItem[] mods) throws NamingException {
+ throw new UnsupportedOperationException();
+ }
+
+ public void modifyAttributes(String name, int mod_op, Attributes attrs) throws NamingException {
+ throw new UnsupportedOperationException();
+ }
+
+ public void rebind(String name, Object obj, Attributes attrs) throws NamingException {
+ throw new UnsupportedOperationException();
+ }
+
+ public Object addToEnvironment(String propName, Object propVal) throws NamingException {
+ throw new UnsupportedOperationException();
+ }
+
+ public void bind(String name, Object obj) throws NamingException {
+ throw new UnsupportedOperationException();
+ }
+
+ public void rebind(String name, Object obj) throws NamingException {
+ throw new UnsupportedOperationException();
+ }
+
+ public Object removeFromEnvironment(String propName) throws NamingException {
+ throw new UnsupportedOperationException();
+ }
+
+ public void rename(String oldName, String newName) throws NamingException {
+ throw new UnsupportedOperationException();
+ }
+
+ public void unbind(String name) throws NamingException {
+ throw new UnsupportedOperationException();
+ }
+
+ public DirContext createSubcontext(String name, Attributes attrs) throws NamingException {
+ throw new UnsupportedOperationException();
+ }
+
+ public DirContext getSchema(String name) throws NamingException {
+ throw new UnsupportedOperationException();
+ }
+
+ public DirContext getSchemaClassDefinition(String name) throws NamingException {
+ throw new UnsupportedOperationException();
+ }
+
+ public NamingEnumeration<SearchResult> search(String name, Attributes matchingAttributes) throws NamingException {
+ throw new UnsupportedOperationException();
+ }
+
+ public NamingEnumeration<SearchResult> search(String name, Attributes matchingAttributes, String[] attributesToReturn) throws NamingException {
+ throw new UnsupportedOperationException();
+ }
+
+ public NamingEnumeration<SearchResult> search(String name, String filter, SearchControls cons) throws NamingException {
+ throw new UnsupportedOperationException();
+ }
+
+ public NamingEnumeration<SearchResult> search(String name, String filterExpr, Object[] filterArgs, SearchControls cons) throws NamingException {
+ throw new UnsupportedOperationException();
+ }
+
+ public Name composeName(Name name, Name prefix) throws NamingException {
+ throw new UnsupportedOperationException();
+ }
+
+ public String composeName(String name, String prefix) throws NamingException {
+ throw new UnsupportedOperationException();
+ }
+
+ public Context createSubcontext(String name) throws NamingException {
+ throw new UnsupportedOperationException();
+ }
+
+ public void destroySubcontext(String name) throws NamingException {
+ throw new UnsupportedOperationException();
+ }
+
+ public Hashtable<?, ?> getEnvironment() throws NamingException {
+ throw new UnsupportedOperationException();
+ }
+
+ public String getNameInNamespace() throws NamingException {
+ throw new UnsupportedOperationException();
+ }
+
+ public NameParser getNameParser(String name) throws NamingException {
+ throw new UnsupportedOperationException();
+ }
+
+ public Object lookupLink(String name) throws NamingException {
+ throw new UnsupportedOperationException();
+ }
+
+}
diff --git a/org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/loading/BaseWebappLoader.java b/org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/loading/BaseWebappLoader.java
new file mode 100644
index 0000000..9b9787c
--- /dev/null
+++ b/org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/loading/BaseWebappLoader.java
@@ -0,0 +1,327 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 VMware Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Apache License v2.0 which accompanies this distribution.
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Apache License v2.0 is available at
+ * http://www.opensource.org/licenses/apache2.0.php.
+ * You may elect to redistribute this code under either of these licenses.
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.gemini.web.tomcat.internal.loading;
+
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyChangeSupport;
+
+import javax.management.MBeanRegistration;
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+
+import org.apache.catalina.Container;
+import org.apache.catalina.Context;
+import org.apache.catalina.Engine;
+import org.apache.catalina.Lifecycle;
+import org.apache.catalina.LifecycleListener;
+import org.apache.catalina.Loader;
+import org.apache.catalina.core.StandardContext;
+import org.apache.catalina.loader.Constants;
+import org.apache.catalina.util.LifecycleSupport;
+import org.apache.catalina.util.StringManager;
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
+import org.apache.tomcat.util.modeler.Registry;
+
+abstract class BaseWebappLoader implements Loader, Lifecycle, PropertyChangeListener, MBeanRegistration {
+
+ /**
+ * The string manager for this package.
+ */
+ protected static final StringManager sm = StringManager.getManager(Constants.Package);
+
+ protected final Log log = LogFactory.getLog(getClass());
+
+ /**
+ * The lifecycle event support for this Loader.
+ */
+ protected final LifecycleSupport lifecycle = new LifecycleSupport(this);
+
+ /**
+ * The property change support for this Loader.
+ */
+ private final PropertyChangeSupport support = new PropertyChangeSupport(this);
+
+ /**
+ * The "follow standard delegation model" flag that will be used to configure our ClassLoader.
+ */
+ private boolean delegate = false;
+
+ /**
+ * The Container with which this Loader has been associated.
+ */
+ private Container container = null;
+
+ private boolean initialized = false;
+
+ private ObjectName controller;
+
+ private ObjectName objectName;
+
+ /**
+ * The reloadable flag for this Loader.
+ */
+ private boolean reloadable = false;
+
+
+ /**
+ * Ensures that the grandparent {@link Container} of the supplied {@link StandardContext context} is an
+ * {@link Engine}.
+ *
+ * @throws IllegalStateException if the grandparent is not an <code>Engine</code>
+ */
+ protected final void ensureGrandparentIsAnEngine(StandardContext standardContext) {
+ if (!(standardContext.getParent().getParent() instanceof Engine)) {
+ throw new IllegalStateException("Grandparent of [" + standardContext + "] is not an instanceof [" + Engine.class.getName() + "].");
+ }
+ }
+
+ protected String getCatalinaContextPath(StandardContext context) {
+ String contextPath = context.getPath();
+ if (contextPath.equals("")) {
+ contextPath = "/";
+ }
+ return contextPath;
+ }
+
+ protected void init() {
+ if (this.initialized) {
+ return;
+ }
+
+ this.initialized = true;
+
+ if (this.objectName == null) {
+ // not registered yet - stand-alone or API
+ if (this.container instanceof StandardContext) {
+ // Register ourself. The container must be a webapp
+ try {
+ StandardContext ctx = (StandardContext) this.container;
+ ensureGrandparentIsAnEngine(ctx);
+ this.objectName = new ObjectName(ctx.getEngineName() + ":type=Loader,path=" + getCatalinaContextPath(ctx) + ",host="
+ + ctx.getParent().getName());
+ Registry.getRegistry(null, null).registerComponent(this, this.objectName, null);
+ this.controller = this.objectName;
+ } catch (Exception e) {
+ log.error("Error registering loader", e);
+ }
+ }
+ }
+ }
+
+ protected void destroy() {
+ if (this.controller == this.objectName) {
+ // Self-registration, undo it
+ Registry.getRegistry(null, null).unregisterComponent(this.objectName);
+ this.objectName = null;
+ }
+ this.initialized = false;
+ }
+
+ protected final ObjectName getObjectName() {
+ return this.objectName;
+ }
+
+ // -------------------------------------------------------------------------
+ // --- Loader
+ // -------------------------------------------------------------------------
+
+ /**
+ * Execute a periodic task, such as reloading, etc. This method will be invoked inside the class loading context of
+ * this container. Unexpected throwables will be caught and logged.
+ */
+ public void backgroundProcess() {
+ if (this.reloadable && modified()) {
+ try {
+ Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
+ if (getContainer() instanceof StandardContext) {
+ ((StandardContext) getContainer()).reload();
+ }
+ } finally {
+ if (getContainer().getLoader() != null) {
+ Thread.currentThread().setContextClassLoader(getContainer().getLoader().getClassLoader());
+ }
+ }
+ }
+ }
+
+ /**
+ * Add a property change listener to this component.
+ *
+ * @param listener The listener to add
+ */
+ public void addPropertyChangeListener(PropertyChangeListener listener) {
+ this.support.addPropertyChangeListener(listener);
+ }
+
+ /**
+ * Set the Container with which this Logger has been associated.
+ *
+ * @param container The associated Container
+ */
+ public void setContainer(Container container) {
+
+ // unregister from the old Container (if any)
+ if (this.container != null && this.container instanceof Context) {
+ ((Context) this.container).removePropertyChangeListener(this);
+ }
+
+ // Process this property change
+ Container oldContainer = this.container;
+ this.container = container;
+ this.support.firePropertyChange("container", oldContainer, this.container);
+
+ // Register with the new Container (if any)
+ if (this.container != null && this.container instanceof Context) {
+ setReloadable(((Context) this.container).getReloadable());
+ ((Context) this.container).addPropertyChangeListener(this);
+ }
+ }
+
+ public final Container getContainer() {
+ return this.container;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void removePropertyChangeListener(PropertyChangeListener listener) {
+ this.support.removePropertyChangeListener(listener);
+ }
+
+ /**
+ * Return the reloadable flag for this Loader.
+ */
+ public boolean getReloadable() {
+ return this.reloadable;
+ }
+
+ /**
+ * Set the reloadable flag for this Loader.
+ *
+ * @param reloadable The new reloadable flag
+ */
+ public void setReloadable(boolean reloadable) {
+ // Process this property change
+ boolean oldReloadable = this.reloadable;
+ this.reloadable = reloadable;
+ this.support.firePropertyChange("reloadable", Boolean.valueOf(oldReloadable), Boolean.valueOf(this.reloadable));
+ }
+
+ /**
+ * Return the "follow standard delegation model" flag used to configure our ClassLoader.
+ */
+ public boolean getDelegate() {
+ return this.delegate;
+ }
+
+ /**
+ * Set the "follow standard delegation model" flag used to configure our ClassLoader.
+ *
+ * @param delegate The new flag
+ */
+ public void setDelegate(boolean delegate) {
+ boolean oldDelegate = this.delegate;
+ this.delegate = delegate;
+ this.support.firePropertyChange("delegate", Boolean.valueOf(oldDelegate), Boolean.valueOf(this.delegate));
+ }
+
+ // -------------------------------------------------------------------------
+ // --- PropertyChangeListener
+ // -------------------------------------------------------------------------
+
+ /**
+ * {@inheritDoc}
+ */
+ public void propertyChange(PropertyChangeEvent event) {
+
+ // Validate the source of this event
+ if (!(event.getSource() instanceof Context)) {
+ return;
+ }
+
+ // Process a relevant property change
+ if (event.getPropertyName().equals("reloadable")) {
+ try {
+ setReloadable(((Boolean) event.getNewValue()).booleanValue());
+ } catch (Exception e) {
+ log.error(sm.getString("webappLoader.reloadable", event.getNewValue().toString()));
+ }
+ }
+ }
+
+ // -------------------------------------------------------------------------
+ // --- Lifecycle
+ // -------------------------------------------------------------------------
+
+ /**
+ * {@inheritDoc}
+ */
+ public void addLifecycleListener(LifecycleListener listener) {
+ this.lifecycle.addLifecycleListener(listener);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public LifecycleListener[] findLifecycleListeners() {
+ return this.lifecycle.findLifecycleListeners();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void removeLifecycleListener(LifecycleListener listener) {
+ this.lifecycle.removeLifecycleListener(listener);
+ }
+
+ // -------------------------------------------------------------------------
+ // --- MBeanRegistration
+ // -------------------------------------------------------------------------
+
+ /**
+ * {@inheritDoc}
+ */
+ public void postDeregister() {
+ /* no-op */
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void postRegister(Boolean registrationDone) {
+ /* no-op */
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void preDeregister() throws Exception {
+ /* no-op */
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public ObjectName preRegister(MBeanServer mBeanServer, ObjectName objectName) throws Exception {
+ this.objectName = objectName;
+ return objectName;
+ }
+
+ // -------------------------------------------------------------------------
+}
diff --git a/org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/loading/BundleClassPathURLExtractor.java b/org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/loading/BundleClassPathURLExtractor.java
new file mode 100644
index 0000000..86338dd
--- /dev/null
+++ b/org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/loading/BundleClassPathURLExtractor.java
@@ -0,0 +1,67 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 VMware Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Apache License v2.0 which accompanies this distribution.
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Apache License v2.0 is available at
+ * http://www.opensource.org/licenses/apache2.0.php.
+ * You may elect to redistribute this code under either of these licenses.
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.gemini.web.tomcat.internal.loading;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.osgi.framework.Bundle;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+final class BundleClassPathURLExtractor {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(BundleClassPathURLExtractor.class);
+
+ private static final String JAR_EXTENSION = ".jar";
+
+ public static Set<URI> extractBundleClassPathURLs(Bundle bundle) {
+ Set<URI> results = new HashSet<URI>();
+ String bcp = (String) bundle.getHeaders().get(org.osgi.framework.Constants.BUNDLE_CLASSPATH);
+ if (bcp != null) {
+ String[] entries = bcp.split(",");
+ for (String entry : entries) {
+ if (isJarEntry(entry)) {
+ URL entryUrl = bundle.getEntry(entry);
+ if (entryUrl != null) {
+ try {
+ URI entryAsJarUrl = new URI("jar", entryUrl.toString() + "!/", null);
+ results.add(entryAsJarUrl);
+ } catch (URISyntaxException e) {
+ if (LOGGER.isWarnEnabled()) {
+ LOGGER.warn("Skipping: " + entryUrl, e);
+ }
+ }
+ } else {
+ if (LOGGER.isWarnEnabled()) {
+ LOGGER.warn("Bundle-ClassPath entry '" + entry + "' is not present in bundle " + bundle.getSymbolicName() + " " + bundle.getVersion() + " and has been skipped");
+ }
+ }
+ }
+ }
+ }
+ return results;
+ }
+
+ private static boolean isJarEntry(String entry) {
+ return entry.endsWith(JAR_EXTENSION);
+ }
+
+}
diff --git a/org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/loading/BundleDirContext.java b/org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/loading/BundleDirContext.java
new file mode 100644
index 0000000..13ab865
--- /dev/null
+++ b/org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/loading/BundleDirContext.java
@@ -0,0 +1,152 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 VMware Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Apache License v2.0 which accompanies this distribution.
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Apache License v2.0 is available at
+ * http://www.opensource.org/licenses/apache2.0.php.
+ * You may elect to redistribute this code under either of these licenses.
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.gemini.web.tomcat.internal.loading;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.naming.Binding;
+import javax.naming.NameClassPair;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.Attributes;
+
+import org.apache.naming.NamingContextBindingsEnumeration;
+import org.apache.naming.NamingContextEnumeration;
+import org.apache.naming.NamingEntry;
+import org.eclipse.gemini.web.tomcat.internal.support.BundleFileResolver;
+import org.eclipse.gemini.web.tomcat.internal.support.BundleFileResolverFactory;
+import org.osgi.framework.Bundle;
+
+
+@SuppressWarnings("unchecked")
+public final class BundleDirContext extends AbstractReadOnlyDirContext {
+
+ private volatile BundleEntry bundleEntry;
+
+ private final BundleFileResolver bundleFileResolver = BundleFileResolverFactory.createBundleFileResolver();
+
+ public BundleDirContext(Bundle bundle) {
+ this(new BundleEntry(bundle));
+ }
+
+ private BundleDirContext(BundleEntry bundleEntry) {
+ this.bundleEntry = bundleEntry;
+ }
+
+ public NamingEnumeration<NameClassPair> list(String name) throws NamingException {
+ List<NamingEntry> resources = doSafeList(name);
+ return new NamingContextEnumeration(resources.iterator());
+ }
+
+ public NamingEnumeration<Binding> listBindings(String name) throws NamingException {
+ List<NamingEntry> resources = doSafeList(name);
+ return new NamingContextBindingsEnumeration(resources.iterator(), this);
+ }
+
+ public Object doLookup(String name) throws NamingException {
+ return entryToResult(getNamedEntry(name));
+ }
+
+ private List<NamingEntry> doSafeList(String name) throws NamingException {
+ return doList(getNamedEntry(name));
+ }
+
+ private BundleEntry getNamedEntry(String name) throws NamingException {
+ checkCanLookup(name);
+ BundleEntry bundleEntry = this.bundleEntry.getEntry(name);
+ if (bundleEntry == null) {
+ throw new NamingException("Name '" + name + "' does not exist.");
+ }
+ return bundleEntry;
+ }
+
+ private List<NamingEntry> doList(BundleEntry bundleEntry) {
+ List<BundleEntry> list = bundleEntry.list();
+ List<NamingEntry> resources = new ArrayList<NamingEntry>();
+ for (BundleEntry entry : list) {
+ Object object;
+ object = entryToResult(entry);
+ resources.add(new NamingEntry(entry.getName(), object, NamingEntry.ENTRY));
+ }
+ return resources;
+ }
+
+ private Object entryToResult(BundleEntry entry) {
+ Object result;
+ if (entry.isDirectory()) {
+ result = new BundleDirContext(entry);
+ } else {
+ result = new URLResource(entry.getURL());
+ }
+ return result;
+ }
+
+ private void checkCanLookup(String name) throws NamingException {
+ BundleEntry entry = this.bundleEntry;
+ if (entry == null || entry.getBundle().getState() == Bundle.UNINSTALLED) {
+ throw new NamingException("Resource not found '" + name + "'");
+ }
+ checkNotAttemptingToLookupFromProtectedLocation(name);
+ }
+
+ private void checkNotAttemptingToLookupFromProtectedLocation(String name) throws NamingException {
+ checkNotAttemptingToLookupFrom(name, "/OSGI-INF/");
+ checkNotAttemptingToLookupFrom(name, "/OSGI-OPT/");
+ }
+
+ private void checkNotAttemptingToLookupFrom(String name, String prefix) throws NamingException {
+ if (name.startsWith(prefix)) {
+ throw new NamingException("Resource cannot be obtained from " + prefix);
+ }
+ }
+
+ @Override
+ public void close() throws NamingException {
+ super.close();
+ this.bundleEntry = null;
+ }
+
+ /**
+ * Retrieves selected attributes associated with a named object.
+ *
+ * @return the requested attributes; never null
+ * @param name the name of the object from which to retrieve attributes
+ * @param attrIds the identifiers of the attributes to retrieve. null indicates that all attributes should be
+ * retrieved; an empty array indicates that none should be retrieved
+ * @exception NamingException if a naming exception is encountered
+ */
+ @Override
+ protected Attributes doGetAttributes(String name, String[] attrIds) throws NamingException {
+ return new BundleEntryAttributes(getNamedEntry(name), attrIds);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected String doGetRealPath(String path) {
+ if (this.bundleEntry.getEntry(path) != null) {
+ File bundleLocation = this.bundleFileResolver.resolve(this.bundleEntry.getBundle());
+ if (bundleLocation != null && bundleLocation.isDirectory()) {
+ return new File(bundleLocation, path).getAbsolutePath();
+ }
+ }
+ return null;
+ }
+}
diff --git a/org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/loading/BundleEntry.java b/org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/loading/BundleEntry.java
new file mode 100644
index 0000000..8ab852b
--- /dev/null
+++ b/org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/loading/BundleEntry.java
@@ -0,0 +1,179 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 VMware Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Apache License v2.0 which accompanies this distribution.
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Apache License v2.0 is available at
+ * http://www.opensource.org/licenses/apache2.0.php.
+ * You may elect to redistribute this code under either of these licenses.
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.gemini.web.tomcat.internal.loading;
+
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.osgi.framework.Bundle;
+
+public final class BundleEntry {
+
+ private static final String PATH_SEPARATOR = "/";
+
+ private final String path;
+
+ private final Bundle bundle;
+
+ public BundleEntry(Bundle bundle) {
+ this(bundle, "");
+ }
+
+ private BundleEntry(Bundle bundle, String path) {
+ this.path = path;
+ this.bundle = bundle;
+ }
+
+ public Bundle getBundle() {
+ return this.bundle;
+ }
+
+ public List<BundleEntry> list() {
+ List<BundleEntry> entries = new ArrayList<BundleEntry>();
+ Enumeration<?> paths = getEntryPathsFromBundle();
+ if (paths != null) {
+ while (paths.hasMoreElements()) {
+ String subPath = (String) paths.nextElement();
+ entries.add(createBundleEntry(subPath));
+ }
+ }
+ return entries;
+ }
+
+ private BundleEntry createBundleEntry(String path) {
+ return new BundleEntry(this.bundle, path);
+ }
+
+ private Enumeration<?> getEntryPathsFromBundle() {
+ final Enumeration<?> ep = this.bundle.getEntryPaths(this.path);
+
+ Set<String> paths = new HashSet<String>();
+ if (ep != null) {
+ while (ep.hasMoreElements()) {
+ paths.add((String) ep.nextElement());
+ }
+ }
+
+ // Ensure web.xml appears even though it may be supplied by a fragment.
+ if ("WEB-INF".equals(this.path) && getEntry("web.xml") != null) {
+ paths.add("WEB-INF/web.xml");
+ }
+
+ if (paths.isEmpty()) {
+ return null;
+ }
+
+ final String[] pathArray = paths.toArray(new String[0]);
+
+ return new Enumeration<String>() {
+
+ private int pos = 0;
+
+ public boolean hasMoreElements() {
+ return pos < pathArray.length;
+ }
+
+ public String nextElement() {
+ if (hasMoreElements()) {
+ return pathArray[pos++];
+ }
+ return null;
+ }
+
+ };
+ }
+
+ public BundleEntry getEntry(String subPath) {
+ String finalPath = this.path + subPath;
+ if (getEntryFromBundle(finalPath) != null) {
+ return createBundleEntry(finalPath);
+ } else {
+ return null;
+ }
+ }
+
+ private URL getEntryFromBundle(String path) {
+ /*
+ * This method has been generalised from this.bundle.getEntry(path) to
+ * allow web.xml to be supplied by a fragment.
+ */
+ if (path.endsWith(PATH_SEPARATOR) || path.length() == 0) {
+ return this.bundle.getEntry(path);
+ }
+ String searchPath;
+ String searchFile;
+ int lastSlashIndex = path.lastIndexOf(PATH_SEPARATOR);
+ if (lastSlashIndex == -1) {
+ searchPath = PATH_SEPARATOR;
+ searchFile = path;
+ } else {
+ searchPath = path.substring(0, lastSlashIndex);
+ searchFile = path.substring(lastSlashIndex + 1);
+ }
+ Enumeration<?> entries = this.bundle.findEntries(searchPath,
+ searchFile, false);
+
+ if (entries != null) {
+ if (entries.hasMoreElements()) {
+ return (URL) entries.nextElement();
+ }
+ }
+
+ return null;
+ }
+
+ public String getName() {
+ String name = this.path;
+
+ if (name.endsWith(PATH_SEPARATOR)) {
+ name = name.substring(0, this.path.length() - 1);
+ }
+
+ int index = name.lastIndexOf(PATH_SEPARATOR);
+ if (index > -1) {
+ name = name.substring(index + 1);
+ }
+
+ if (name.length() == 0) {
+ return PATH_SEPARATOR;
+ } else {
+ return name;
+ }
+ }
+
+ public URL getURL() {
+ return getEntryFromBundle(this.path);
+ }
+
+ public String getPath() {
+ return this.path;
+ }
+
+ public boolean isDirectory() {
+ return this.path.endsWith(PATH_SEPARATOR);
+ }
+
+ @Override
+ public String toString() {
+ return String.format("BundleEntry [bundle=%s,path=%s]", this.bundle,
+ this.path);
+ }
+}
diff --git a/org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/loading/BundleEntryAttributes.java b/org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/loading/BundleEntryAttributes.java
new file mode 100644
index 0000000..f237881
--- /dev/null
+++ b/org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/loading/BundleEntryAttributes.java
@@ -0,0 +1,148 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 VMware Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Apache License v2.0 which accompanies this distribution.
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Apache License v2.0 is available at
+ * http://www.opensource.org/licenses/apache2.0.php.
+ * You may elect to redistribute this code under either of these licenses.
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.gemini.web.tomcat.internal.loading;
+
+import java.io.IOException;
+import java.net.URLConnection;
+
+import javax.naming.directory.Attributes;
+
+import org.apache.naming.resources.ResourceAttributes;
+
+/**
+ * {@link BundleEntryAttributes} provides the default {@link Attributes} for a {@link BundleEntry}.
+ */
+final class BundleEntryAttributes extends ResourceAttributes {
+
+ private static final int CREATION_DATE_UNKNOWN = 0;
+
+ private static final long TIME_NOT_SET = -1L;
+
+ private static final long serialVersionUID = 7799793247259935763L;
+
+ private final transient BundleEntry bundleEntry;
+
+ private final String[] attrIds;
+
+ private long lastModified = TIME_NOT_SET;
+
+ /**
+ * Creates a {@link BundleEntryAttributes} for the given {@link BundleEntry} and attribute identifier array.
+ *
+ * @param bundleEntry the <code>BundleEntry</code> from which to retrieve attributes
+ * @param attrIds the identifiers of the attributes to retrieve, or <code>null</code> if all attributes should be
+ * retrieved
+ */
+ BundleEntryAttributes(BundleEntry bundleEntry, String[] attrIds) {
+ this.bundleEntry = bundleEntry;
+ this.attrIds = attrIds;
+ setCollection(this.bundleEntry.isDirectory());
+ getName();
+ getLastModified();
+ getCreation();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public long getCreation() {
+ long creation = TIME_NOT_SET;
+
+ if (attrPresent(CREATION_DATE) || attrPresent(ALTERNATE_CREATION_DATE)) {
+ creation = super.getCreation();
+
+ if (creation == TIME_NOT_SET) {
+ try {
+ URLConnection urlConnection = this.bundleEntry.getURL().openConnection();
+ creation = urlConnection.getDate();
+ if (creation == CREATION_DATE_UNKNOWN) {
+ creation = determineLastModified();
+ }
+ setCreation(creation);
+ } catch (IOException _) {
+ }
+ }
+ }
+
+ return creation;
+ }
+
+ private boolean attrPresent(String attrId) {
+ if (this.attrIds == null) {
+ return true;
+ }
+
+ for (String ai : this.attrIds) {
+ if (ai.equals(attrId)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public long getLastModified() {
+ long lastModified = TIME_NOT_SET;
+
+ if (attrPresent(LAST_MODIFIED) || attrPresent(ALTERNATE_LAST_MODIFIED)) {
+ lastModified = super.getLastModified();
+
+ if (lastModified == TIME_NOT_SET) {
+ lastModified = determineLastModified();
+
+ if (lastModified != TIME_NOT_SET) {
+ setLastModified(lastModified);
+ }
+ }
+ }
+
+ return lastModified;
+ }
+
+ private long determineLastModified() {
+ if (this.lastModified == TIME_NOT_SET) {
+ try {
+ URLConnection urlConnection = this.bundleEntry.getURL().openConnection();
+ this.lastModified = urlConnection.getLastModified();
+ } catch (IOException _) {
+ }
+ }
+ return this.lastModified;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getName() {
+ if (!attrPresent(NAME)) {
+ return null;
+ }
+
+ String name = super.getName();
+ if (name == null) {
+ name = this.bundleEntry.getName();
+ }
+ return name;
+ }
+
+}
diff --git a/org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/loading/BundleWebappClassLoader.java b/org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/loading/BundleWebappClassLoader.java
new file mode 100644
index 0000000..aa90ae0
--- /dev/null
+++ b/org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/loading/BundleWebappClassLoader.java
@@ -0,0 +1,326 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 VMware Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Apache License v2.0 which accompanies this distribution.
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Apache License v2.0 is available at
+ * http://www.opensource.org/licenses/apache2.0.php.
+ * You may elect to redistribute this code under either of these licenses.
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.gemini.web.tomcat.internal.loading;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.instrument.ClassFileTransformer;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.sql.Driver;
+import java.sql.DriverManager;
+import java.sql.SQLException;
+import java.util.Enumeration;
+import java.util.Set;
+
+import org.apache.catalina.Context;
+import org.apache.catalina.Lifecycle;
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.LifecycleListener;
+import org.apache.catalina.loader.Constants;
+import org.apache.catalina.util.StringManager;
+import org.apache.tomcat.util.IntrospectionUtils;
+import org.eclipse.gemini.web.tomcat.spi.ClassLoaderCustomizer;
+import org.osgi.framework.Bundle;
+import org.springframework.osgi.util.BundleDelegatingClassLoader;
+
+
+public class BundleWebappClassLoader extends URLClassLoader implements Lifecycle {
+
+ /**
+ * The string manager for this package.
+ */
+ protected static final StringManager sm = StringManager.getManager(Constants.Package);
+
+ protected static final org.apache.juli.logging.Log log = org.apache.juli.logging.LogFactory.getLog(BundleWebappClassLoader.class);
+
+ /**
+ * Has this class loader been started?
+ */
+ private volatile boolean started = false;
+
+ /**
+ * The delegating class loader for the web application's bundle(s).
+ */
+ private final ClassLoader bundleDelegatingClassLoader;
+
+ private final ClassLoaderCustomizer classLoaderCustomizer;
+
+ private final Bundle bundle;
+
+ // ------------------------------------------------------------------------
+ // --- Constructors
+ // ------------------------------------------------------------------------
+
+ public BundleWebappClassLoader(final Bundle bundle, ClassLoaderCustomizer classLoaderCustomizer) {
+ super(new URL[0]);
+ this.bundle = bundle;
+ this.classLoaderCustomizer = classLoaderCustomizer;
+
+ addBundleClassPathURLs(bundle);
+
+ this.bundleDelegatingClassLoader = createClassLoaderChain(bundle);
+ }
+
+ private ChainedClassLoader createClassLoaderChain(Bundle bundle) {
+ ClassLoader[] loaders = {
+ BundleDelegatingClassLoader.createBundleClassLoaderFor(bundle),
+ Context.class.getClassLoader() // catalina classloader
+ };
+
+ ClassLoader[] chainExtensions = this.classLoaderCustomizer.extendClassLoaderChain(bundle);
+
+ ClassLoader[] finalLoaders;
+ if(chainExtensions != null && chainExtensions.length > 0) {
+ finalLoaders = new ClassLoader[loaders.length + chainExtensions.length];
+ System.arraycopy(loaders, 0, finalLoaders, 0, loaders.length);
+ System.arraycopy(chainExtensions, 0, finalLoaders, loaders.length, chainExtensions.length);
+ } else {
+ finalLoaders = loaders;
+ }
+ return ChainedClassLoader.create(finalLoaders);
+ }
+
+ private void addBundleClassPathURLs(Bundle bundle) {
+ Set<URI> uris = BundleClassPathURLExtractor.extractBundleClassPathURLs(bundle);
+ for (URI uri : uris) {
+ try {
+ addURL(uri.toURL());
+ } catch (MalformedURLException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ // -------------------------------------------------------------------------
+ // --- Instrumentation
+ // -------------------------------------------------------------------------
+
+ public void addTransformer(ClassFileTransformer transformer) {
+ this.classLoaderCustomizer.addClassFileTransformer(transformer, this.bundle);
+ }
+
+ public ClassLoader getThrowawayClassLoader() {
+ return this.classLoaderCustomizer.createThrowawayClassLoader(this.bundle);
+ }
+
+ // -------------------------------------------------------------------------
+ // --- Lifecycle
+ // -------------------------------------------------------------------------
+
+ /**
+ * {@inheritDoc}
+ */
+ public void addLifecycleListener(LifecycleListener listener) {
+ /* no-op */
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public LifecycleListener[] findLifecycleListeners() {
+ return new LifecycleListener[0];
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void removeLifecycleListener(LifecycleListener listener) {
+ /* no-op */
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void start() throws LifecycleException {
+ this.started = true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void stop() throws LifecycleException {
+ // Clearing references should be done before setting started to
+ // false, due to possible side effects
+ clearReferences();
+
+ this.started = false;
+ }
+
+ // -------------------------------------------------------------------------
+ // --- ClassLoader
+ // -------------------------------------------------------------------------
+
+ /**
+ * Find the resource with the given name. A resource is some data (images, audio, text, etc.) that can be accessed
+ * by class code in a way that is independent of the location of the code. The name of a resource is a "/"-separated
+ * path name that identifies the resource. If the resource cannot be found, return <code>null</code>.
+ * <p>
+ * This method searches according to the following algorithm, returning as soon as it finds the appropriate URL. If
+ * the resource cannot be found, returns <code>null</code>.
+ * <ul>
+ * <li>???</li>
+ * </ul>
+ *
+ * @param name Name of the resource to return a URL for
+ */
+ @Override
+ public URL getResource(String name) {
+ if (log.isDebugEnabled()) {
+ log.debug("getResource(" + name + ")");
+ }
+
+ URL url = null;
+
+ url = this.bundleDelegatingClassLoader.getResource(name);
+ if (url != null) {
+ return url;
+ }
+
+ // Resource was not found
+ if (log.isDebugEnabled()) {
+ log.debug(" --> Resource not found, returning null");
+ }
+
+ return null;
+ }
+
+ public InputStream getResourceAsStream(String name) {
+ return super.getResourceAsStream(name);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Enumeration<URL> getResources(String name) throws IOException {
+ if (log.isDebugEnabled()) {
+ log.debug("getResources(" + name + ")");
+ }
+
+ return this.bundleDelegatingClassLoader.getResources(name);
+ }
+
+ /**
+ * Load the class with the specified name, searching using the following algorithm until it finds and returns the
+ * class. If the class cannot be found, throws <code>ClassNotFoundException</code>.
+ * <ul>
+ * <li>Check cache of previously loaded classes</li>
+ * <li>If not found, attempt to load the class from the Application's synthetic context bundle (and indirectly from
+ * the Web module's bundle)</li>
+ * <li>If not found, attempt to load the class from Tomcat's bundles</li>
+ * <li>If not found, throw a <code>ClassNotFoundException</code></li>
+ * </ul>
+ * If the class was found using the above steps, and the <code>resolve</code> flag is <code>true</code>, this method
+ * will then call <code>resolveClass(Class)</code> on the resulting Class object.
+ *
+ * @param name Name of the class to be loaded
+ * @param resolve If <code>true</code> then resolve the class
+ *
+ * @exception ClassNotFoundException if the class was not found
+ */
+ @Override
+ public Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
+ if (log.isDebugEnabled()) {
+ log.debug("loadClass(" + name + ", " + resolve + ")");
+ }
+
+ Class<?> clazz = null;
+
+ // Log access to stopped classloader
+ if (!this.started) {
+ try {
+ throw new IllegalStateException();
+ } catch (IllegalStateException e) {
+ log.info(sm.getString("webappClassLoader.stopped", name), e);
+ }
+ }
+
+ // Check our previously loaded class cache
+ clazz = findLoadedClass(name);
+ if (clazz != null) {
+ if (log.isDebugEnabled()) {
+ log.debug(" Returning class from cache");
+ }
+ if (resolve) {
+ resolveClass(clazz);
+ }
+ return clazz;
+ }
+
+ // Search the application's bundle
+ if (log.isDebugEnabled()) {
+ log.debug(" Searching the application's bundle");
+ }
+ try {
+ clazz = this.bundleDelegatingClassLoader.loadClass(name);
+ if (clazz != null) {
+ if (log.isDebugEnabled()) {
+ log.debug(" Loading class from the delegating classloader");
+ }
+ if (resolve) {
+ resolveClass(clazz);
+ }
+ return clazz;
+ }
+ } catch (ClassNotFoundException e) {
+ /* no-op */
+ }
+
+ throw new ClassNotFoundException(name);
+ }
+
+ // -------------------------------------------------------------------------
+ // --- Protected Methods
+ // -------------------------------------------------------------------------
+
+ /**
+ * Clear references.
+ */
+ protected void clearReferences() {
+
+ // Unregister any JDBC drivers loaded by this classloader
+ Enumeration<Driver> drivers = DriverManager.getDrivers();
+ while (drivers.hasMoreElements()) {
+ Driver driver = (Driver) drivers.nextElement();
+ if (driver.getClass().getClassLoader() == this) {
+ try {
+ DriverManager.deregisterDriver(driver);
+ } catch (SQLException e) {
+ log.warn("SQL driver deregistration failed", e);
+ }
+ }
+ }
+
+ // XXX Determine if we need to null out static or final fields from loaded classes as in WebappClassLoader
+
+ // Clear the IntrospectionUtils cache.
+ IntrospectionUtils.clear();
+
+ // Clear the classloader reference in common-logging
+ org.apache.juli.logging.LogFactory.release(this);
+
+ // Clear the classloader reference in the VM's bean introspector
+ java.beans.Introspector.flushCaches();
+ }
+
+ public Bundle getBundle() {
+ return this.bundle;
+ }
+}
diff --git a/org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/loading/BundleWebappLoader.java b/org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/loading/BundleWebappLoader.java
new file mode 100644
index 0000000..3625a81
--- /dev/null
+++ b/org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/loading/BundleWebappLoader.java
@@ -0,0 +1,248 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 VMware Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Apache License v2.0 which accompanies this distribution.
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Apache License v2.0 is available at
+ * http://www.opensource.org/licenses/apache2.0.php.
+ * You may elect to redistribute this code under either of these licenses.
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.gemini.web.tomcat.internal.loading;
+
+import java.beans.PropertyChangeListener;
+
+import javax.management.MBeanRegistration;
+import javax.management.MalformedObjectNameException;
+import javax.management.ObjectName;
+import javax.servlet.ServletContext;
+
+import org.apache.catalina.Context;
+import org.apache.catalina.Globals;
+import org.apache.catalina.Lifecycle;
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.Loader;
+import org.apache.catalina.core.StandardContext;
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
+import org.apache.naming.resources.DirContextURLStreamHandler;
+import org.apache.tomcat.util.modeler.Registry;
+import org.eclipse.gemini.web.tomcat.spi.ClassLoaderCustomizer;
+import org.osgi.framework.Bundle;
+
+
+public class BundleWebappLoader extends BaseWebappLoader implements Loader, Lifecycle, PropertyChangeListener, MBeanRegistration {
+
+ private static Log log = LogFactory.getLog(BundleWebappLoader.class);
+
+ /**
+ * The OSGi {@link Bundle bundle} which will back the {@link ClassLoader} we will create.
+ */
+ private volatile Bundle bundle;
+
+ private volatile ClassLoaderCustomizer classLoaderCustomizer;
+
+ /**
+ * The time stamp which is used to track when the bundle's {@link Bundle#getLastModified() last modified} time stamp
+ * was last checked.
+ */
+ private long bundleModificationCheckTimestamp;
+
+ private Object bundleModificationLock = new Object();
+
+ /**
+ * The class loader being managed by this Loader.
+ */
+ private ClassLoader classLoader = null;
+
+ /**
+ * The descriptive information about this Loader implementation.
+ */
+ private static final String INFO = BaseWebappLoader.class.getName() + "/1.0";
+
+ /**
+ * Has this Loader been started?
+ */
+ private boolean started = false;
+
+ // -------------------------------------------------------------------------
+ // --- Constructors
+ // -------------------------------------------------------------------------
+
+
+ public BundleWebappLoader(Bundle bundle, ClassLoaderCustomizer classLoaderCustomizer) {
+ this.bundle = bundle;
+ this.bundleModificationCheckTimestamp = this.bundle.getLastModified();
+ this.classLoaderCustomizer = classLoaderCustomizer;
+ }
+
+ // -------------------------------------------------------------------------
+ // --- OsgiWebappLoader-specific implementation
+ // -------------------------------------------------------------------------
+
+ /**
+ * @return
+ * @see #getClassLoaderName()
+ */
+ private ClassLoader createClassLoader() {
+ return new BundleWebappClassLoader(this.bundle, this.classLoaderCustomizer);
+ }
+
+ // -------------------------------------------------------------------------
+ // --- Loader
+ // -------------------------------------------------------------------------
+
+ /**
+ * @throws UnsupportedOperationException always
+ */
+ public void addRepository(String repository) {
+ throw new UnsupportedOperationException(getClass().getSimpleName() + " does not support addRepository(String)");
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String[] findRepositories() {
+ return new String[0];
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public ClassLoader getClassLoader() {
+ return this.classLoader;
+ }
+
+ /**
+ * Return descriptive information about this Loader implementation and the corresponding version number, in the
+ * format <code>&lt;description&gt;/&lt;version&gt;</code>.
+ */
+ public String getInfo() {
+ return INFO;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean modified() {
+
+ synchronized (bundleModificationLock) {
+ // If the current last modification time stamp is newer than the time stamp from the last time we checked,
+ // there's been change!
+ final long lastModified = this.bundle.getLastModified();
+ if (lastModified > this.bundleModificationCheckTimestamp) {
+ this.bundleModificationCheckTimestamp = lastModified;
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ // -------------------------------------------------------------------------
+ // --- Lifecycle
+ // -------------------------------------------------------------------------
+
+ /**
+ * {@inheritDoc}
+ */
+ public void start() throws LifecycleException {
+ init();
+ if (this.started) {
+ throw new LifecycleException(sm.getString("webappLoader.alreadyStarted"));
+ }
+ if (log.isDebugEnabled()) {
+ log.debug(sm.getString("webappLoader.starting"));
+ }
+ this.lifecycle.fireLifecycleEvent(START_EVENT, null);
+ this.started = true;
+
+ if (getContainer().getResources() == null) {
+ log.info("No resources for " + getContainer());
+ return;
+ }
+
+ // Construct a class loader based on our current repositories list
+ try {
+
+ this.classLoader = createClassLoader();
+ if (this.classLoader instanceof Lifecycle) {
+ ((Lifecycle) this.classLoader).start();
+ }
+
+ DirContextURLStreamHandler.bind((ClassLoader) classLoader, getContainer().getResources());
+
+ registerClassLoaderMBean();
+
+ } catch (Throwable t) {
+ log.error("LifecycleException ", t);
+ throw new LifecycleException("start: ", t);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void stop() throws LifecycleException {
+
+ // Validate and update our current component state
+ if (!this.started) {
+ throw new LifecycleException(sm.getString("webappLoader.notStarted"));
+ }
+ if (log.isDebugEnabled()) {
+ log.debug(sm.getString("webappLoader.stopping"));
+ }
+ this.lifecycle.fireLifecycleEvent(STOP_EVENT, null);
+ this.started = false;
+
+ // Remove context attributes as appropriate
+ if (getContainer() instanceof Context) {
+ ServletContext servletContext = ((Context) getContainer()).getServletContext();
+ servletContext.removeAttribute(Globals.CLASS_PATH_ATTR);
+ }
+
+ // Throw away our current class loader
+ if (this.classLoader instanceof Lifecycle) {
+ ((Lifecycle) this.classLoader).stop();
+ }
+
+ DirContextURLStreamHandler.unbind((ClassLoader) classLoader);
+
+ unregisterClassLoaderMBean();
+
+ this.classLoader = null;
+ this.bundle = null;
+ this.classLoaderCustomizer = null;
+ destroy();
+ }
+
+ private void registerClassLoaderMBean() throws MalformedObjectNameException, Exception {
+ StandardContext ctx = (StandardContext) getContainer();
+ ensureGrandparentIsAnEngine(ctx);
+ ObjectName classLoaderObjectName = createClassLoaderObjectName(ctx);
+ Registry.getRegistry(null, null).registerComponent(this.classLoader, classLoaderObjectName, null);
+ }
+
+ private void unregisterClassLoaderMBean() {
+ try {
+ StandardContext ctx = (StandardContext) getContainer();
+ ensureGrandparentIsAnEngine(ctx);
+ ObjectName classLoaderObjectName = createClassLoaderObjectName(ctx);
+ Registry.getRegistry(null, null).unregisterComponent(classLoaderObjectName);
+ } catch (Throwable t) {
+ log.error("LifecycleException ", t);
+ }
+ }
+
+ private ObjectName createClassLoaderObjectName(StandardContext ctx) throws MalformedObjectNameException {
+ return new ObjectName(ctx.getEngineName() + ":type=OsgiWebappClassLoader,path=" + getCatalinaContextPath(ctx) + ",host="
+ + ctx.getParent().getName());
+ }
+
+}
diff --git a/org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/loading/ChainedClassLoader.java b/org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/loading/ChainedClassLoader.java
new file mode 100644
index 0000000..35a8ece
--- /dev/null
+++ b/org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/loading/ChainedClassLoader.java
@@ -0,0 +1,173 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 VMware Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Apache License v2.0 which accompanies this distribution.
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Apache License v2.0 is available at
+ * http://www.opensource.org/licenses/apache2.0.php.
+ * You may elect to redistribute this code under either of these licenses.
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.gemini.web.tomcat.internal.loading;
+
+import java.io.IOException;
+import java.net.URL;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.Vector;
+
+public final class ChainedClassLoader extends ClassLoader {
+
+ /** list of loaders */
+ private final List<ClassLoader> loaders = new ArrayList<ClassLoader>();
+
+ /**
+ * Constructs a new <code>ChainedClassLoader</code> instance.
+ *
+ * @param loaders array of non-null class loaders
+ * @param parent parent class loader (can be null)
+ */
+ ChainedClassLoader(ClassLoader... loaders) {
+
+ synchronized (this.loaders) {
+ for (int i = 0; i < loaders.length; i++) {
+ ClassLoader classLoader = loaders[i];
+ addLoader(classLoader);
+ }
+ }
+ }
+
+ public static ChainedClassLoader create(final ClassLoader... loaders) {
+ return AccessController.doPrivileged(new PrivilegedAction<ChainedClassLoader>() {
+
+ public ChainedClassLoader run() {
+ return new ChainedClassLoader(loaders);
+ }
+
+ });
+ }
+
+ @Override
+ public URL getResource(final String name) {
+ if (System.getSecurityManager() != null) {
+ return AccessController.doPrivileged(new PrivilegedAction<URL>() {
+
+ public URL run() {
+ return doGetResource(name);
+ }
+ });
+ } else {
+ return doGetResource(name);
+ }
+ }
+
+ @Override
+ public Enumeration<URL> getResources(final String name) throws IOException {
+ if (System.getSecurityManager() != null) {
+ try {
+ return AccessController.doPrivileged(new PrivilegedExceptionAction<Enumeration<URL>>() {
+
+ public Enumeration<URL> run() throws Exception {
+ return doGetResources(name);
+ }
+
+ });
+ } catch (PrivilegedActionException e) {
+ Exception exception = e.getException();
+ if (exception instanceof RuntimeException) {
+ throw (RuntimeException) exception;
+ } else if (exception instanceof IOException) {
+ throw (IOException) exception;
+ } else {
+ throw new IllegalStateException("Unexpected Exception from privileged action.", exception);
+ }
+ }
+ } else {
+ return doGetResources(name);
+ }
+ }
+
+ private Enumeration<URL> doGetResources(String name) throws IOException {
+ Vector<URL> urls = new Vector<URL>();
+ synchronized (this.loaders) {
+ for (ClassLoader loader : this.loaders) {
+ Enumeration<URL> resources = loader.getResources(name);
+ if (resources != null) {
+ while (resources.hasMoreElements()) {
+ URL url = (URL) resources.nextElement();
+ urls.add(url);
+ }
+ }
+ }
+ }
+ return urls.elements();
+ }
+
+ private URL doGetResource(String name) {
+ URL url = null;
+ synchronized (loaders) {
+ for (int i = 0; i < loaders.size(); i++) {
+ ClassLoader loader = (ClassLoader) loaders.get(i);
+ url = loader.getResource(name);
+ if (url != null)
+ return url;
+ }
+ }
+ return url;
+ }
+
+ public Class<?> loadClass(final String name) throws ClassNotFoundException {
+
+ if (System.getSecurityManager() != null) {
+ try {
+ return AccessController.doPrivileged(new PrivilegedExceptionAction<Class<?>>() {
+
+ public Class<?> run() throws Exception {
+ return doLoadClass(name);
+ }
+ });
+ } catch (PrivilegedActionException pae) {
+ throw (ClassNotFoundException) pae.getException();
+ }
+ } else {
+ return doLoadClass(name);
+ }
+ }
+
+ private Class<?> doLoadClass(String name) throws ClassNotFoundException {
+ Class<?> clazz = null;
+
+ synchronized (loaders) {
+ for (int i = 0; i < loaders.size(); i++) {
+ ClassLoader loader = (ClassLoader) loaders.get(i);
+ try {
+ clazz = loader.loadClass(name);
+ return clazz;
+ } catch (ClassNotFoundException e) {
+ // keep moving through the class loaders
+ }
+ }
+ }
+
+ throw new ClassNotFoundException(name);
+ }
+
+ private void addLoader(ClassLoader classLoader) {
+ synchronized (loaders) {
+ if (!loaders.contains(classLoader)) {
+ loaders.add(classLoader);
+ }
+ }
+ }
+}
diff --git a/org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/loading/DirContextURLStreamHandlerService.java b/org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/loading/DirContextURLStreamHandlerService.java
new file mode 100644
index 0000000..4e162ab
--- /dev/null
+++ b/org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/loading/DirContextURLStreamHandlerService.java
@@ -0,0 +1,35 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 VMware Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Apache License v2.0 which accompanies this distribution.
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Apache License v2.0 is available at
+ * http://www.opensource.org/licenses/apache2.0.php.
+ * You may elect to redistribute this code under either of these licenses.
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.gemini.web.tomcat.internal.loading;
+
+import java.io.IOException;
+import java.net.URL;
+import java.net.URLConnection;
+
+import org.apache.naming.resources.DirContextURLStreamHandler;
+import org.osgi.service.url.AbstractURLStreamHandlerService;
+
+public class DirContextURLStreamHandlerService extends AbstractURLStreamHandlerService {
+
+ private final DirContextURLStreamHandler handler = new DirContextURLStreamHandler();
+
+ @Override
+ public URLConnection openConnection(URL u) throws IOException {
+ return new URL(null, u.toExternalForm(), this.handler).openConnection();
+ }
+
+}
diff --git a/org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/loading/StandardWebBundleClassLoaderFactory.java b/org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/loading/StandardWebBundleClassLoaderFactory.java
new file mode 100644
index 0000000..50a2078
--- /dev/null
+++ b/org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/loading/StandardWebBundleClassLoaderFactory.java
@@ -0,0 +1,54 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 VMware Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Apache License v2.0 which accompanies this distribution.
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Apache License v2.0 is available at
+ * http://www.opensource.org/licenses/apache2.0.php.
+ * You may elect to redistribute this code under either of these licenses.
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.gemini.web.tomcat.internal.loading;
+
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+
+import org.eclipse.gemini.web.tomcat.spi.ClassLoaderCustomizer;
+import org.eclipse.gemini.web.tomcat.spi.WebBundleClassLoaderFactory;
+import org.osgi.framework.Bundle;
+
+
+/**
+ * TODO Document StandardWebBundleClassLoaderFactory
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * TODO Document concurrent semantics of StandardWebBundleClassLoaderFactory
+ *
+ */
+public class StandardWebBundleClassLoaderFactory implements WebBundleClassLoaderFactory {
+
+ private final ClassLoaderCustomizer classLoaderCustomizer;
+
+ public StandardWebBundleClassLoaderFactory(ClassLoaderCustomizer classLoaderCustomizer) {
+ this.classLoaderCustomizer = classLoaderCustomizer;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public ClassLoader createWebBundleClassLoader(final Bundle webBundle) {
+ return AccessController.doPrivileged(new PrivilegedAction<ClassLoader>() {
+ public ClassLoader run() {
+ return new BundleWebappClassLoader(webBundle, classLoaderCustomizer);
+ }
+ });
+ }
+}
diff --git a/org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/loading/URLResource.java b/org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/loading/URLResource.java
new file mode 100644
index 0000000..7ef6844
--- /dev/null
+++ b/org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/loading/URLResource.java
@@ -0,0 +1,44 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 VMware Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Apache License v2.0 which accompanies this distribution.
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Apache License v2.0 is available at
+ * http://www.opensource.org/licenses/apache2.0.php.
+ * You may elect to redistribute this code under either of these licenses.
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.gemini.web.tomcat.internal.loading;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+
+import org.apache.naming.resources.Resource;
+
+
+final class URLResource extends Resource{
+
+ private final URL url;
+
+ public URLResource(URL url) {
+ this.url = url;
+ }
+
+ @Override
+ public InputStream streamContent() throws IOException {
+ if(this.binaryContent == null) {
+ this.inputStream = url.openStream();
+ }
+ return super.streamContent();
+
+ }
+
+
+}
diff --git a/org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/support/BundleDependencyDeterminer.java b/org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/support/BundleDependencyDeterminer.java
new file mode 100644
index 0000000..23cf554
--- /dev/null
+++ b/org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/support/BundleDependencyDeterminer.java
@@ -0,0 +1,42 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 VMware Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Apache License v2.0 which accompanies this distribution.
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Apache License v2.0 is available at
+ * http://www.opensource.org/licenses/apache2.0.php.
+ * You may elect to redistribute this code under either of these licenses.
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.gemini.web.tomcat.internal.support;
+
+import java.util.Set;
+
+import org.osgi.framework.Bundle;
+
+/**
+ * Determines a {@link Bundle Bundle's} dependencies.
+ *
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * Implementations <strong>must</strong> be thread-safe.
+ *
+ */
+public interface BundleDependencyDeterminer {
+
+ /**
+ * Returns the dependencies of the supplied <code>Bundle</code>, i.e. the <code>Bundles</code> to which it is wired.
+ *
+ * @param bundle the <code>Bundle</code> for which the dependencies are required.
+ * @return the dependencies of the supplied <code>Bundle</code>.
+ */
+ Set<Bundle> getDependencies(Bundle bundle);
+}
diff --git a/org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/support/BundleFileResolver.java b/org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/support/BundleFileResolver.java
new file mode 100644
index 0000000..ffb00a4
--- /dev/null
+++ b/org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/support/BundleFileResolver.java
@@ -0,0 +1,38 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 VMware Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Apache License v2.0 which accompanies this distribution.
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Apache License v2.0 is available at
+ * http://www.opensource.org/licenses/apache2.0.php.
+ * You may elect to redistribute this code under either of these licenses.
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.gemini.web.tomcat.internal.support;
+
+import java.io.File;
+
+import org.osgi.framework.Bundle;
+
+/**
+ * Strategy interface for resolving the {@link File} content of an installed {@link Bundle}. This is the
+ * <code>File</code> that the framework is actually using - it may not correspond to the location the
+ * <code>Bundle</code> was installed from.
+ *
+ */
+public interface BundleFileResolver {
+
+ /**
+ * Attempts to resolve the supplied {@link Bundle} to the {@link File} its content is being loaded from.
+ *
+ * @param bundle the <code>Bundle</code>.
+ * @return the <code>File</code> or <code>null</code> if no <code>File</code> could be found.
+ */
+ File resolve(Bundle bundle);
+}
diff --git a/org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/support/BundleFileResolverFactory.java b/org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/support/BundleFileResolverFactory.java
new file mode 100644
index 0000000..5af94c3
--- /dev/null
+++ b/org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/support/BundleFileResolverFactory.java
@@ -0,0 +1,44 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 VMware Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Apache License v2.0 which accompanies this distribution.
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Apache License v2.0 is available at
+ * http://www.opensource.org/licenses/apache2.0.php.
+ * You may elect to redistribute this code under either of these licenses.
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.gemini.web.tomcat.internal.support;
+
+import java.io.File;
+
+import org.osgi.framework.Bundle;
+
+/**
+ * Factory for {@link BundleFileResolver} implementations.
+ *
+ */
+public final class BundleFileResolverFactory {
+
+ public static BundleFileResolver createBundleFileResolver() {
+ if (EquinoxBundleFileResolver.canUse()) {
+ return new EquinoxBundleFileResolver();
+ } else {
+ return new NoOpBundleFileResolver();
+ }
+ }
+
+ private static class NoOpBundleFileResolver implements BundleFileResolver {
+
+ public File resolve(Bundle bundle) {
+ return null;
+ }
+
+ }
+}
diff --git a/org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/support/EquinoxBundleFileResolver.java b/org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/support/EquinoxBundleFileResolver.java
new file mode 100644
index 0000000..1d8ead6
--- /dev/null
+++ b/org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/support/EquinoxBundleFileResolver.java
@@ -0,0 +1,55 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 VMware Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Apache License v2.0 which accompanies this distribution.
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Apache License v2.0 is available at
+ * http://www.opensource.org/licenses/apache2.0.php.
+ * You may elect to redistribute this code under either of these licenses.
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.gemini.web.tomcat.internal.support;
+
+import java.io.File;
+
+import org.eclipse.osgi.baseadaptor.BaseData;
+import org.eclipse.osgi.framework.adaptor.BundleData;
+import org.eclipse.osgi.framework.internal.core.BundleHost;
+import org.osgi.framework.Bundle;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+final class EquinoxBundleFileResolver implements BundleFileResolver {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(EquinoxBundleFileResolver.class);
+
+ public File resolve(Bundle bundle) {
+ if (bundle instanceof BundleHost) {
+ BundleHost bh = (BundleHost) bundle;
+ BundleData bundleData = bh.getBundleData();
+ if (bundleData instanceof BaseData) {
+ File file = ((BaseData) bundleData).getBundleFile().getBaseFile();
+ if (LOGGER.isDebugEnabled()) {
+ LOGGER.debug("Resolved bundle '" + bundle.getSymbolicName() + "' to file '" + file.getAbsolutePath() + "'");
+ }
+ return file;
+ }
+ }
+ return null;
+ }
+
+ public static boolean canUse() {
+ try {
+ EquinoxBundleFileResolver.class.getClassLoader().loadClass(BundleHost.class.getName());
+ return true;
+ } catch (Exception ex) {
+ return false;
+ }
+ }
+}
diff --git a/org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/support/PackageAdminBundleDependencyDeterminer.java b/org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/support/PackageAdminBundleDependencyDeterminer.java
new file mode 100644
index 0000000..f99d254
--- /dev/null
+++ b/org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/support/PackageAdminBundleDependencyDeterminer.java
@@ -0,0 +1,76 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 VMware Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Apache License v2.0 which accompanies this distribution.
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Apache License v2.0 is available at
+ * http://www.opensource.org/licenses/apache2.0.php.
+ * You may elect to redistribute this code under either of these licenses.
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.gemini.web.tomcat.internal.support;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.packageadmin.ExportedPackage;
+import org.osgi.service.packageadmin.PackageAdmin;
+
+
+/**
+ * A <code>BundleDependencyDeterminer</code> that uses {@link PackageAdmin} to
+ * determine a <code>Bundle</code>'s dependencies.
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ * Thread-safe.
+ *
+ */
+public final class PackageAdminBundleDependencyDeterminer implements BundleDependencyDeterminer {
+
+ private final BundleContext bundleContext;
+
+ private final PackageAdmin packageAdmin;
+
+ public PackageAdminBundleDependencyDeterminer(BundleContext bundleContext, PackageAdmin packageAdmin) {
+ this.bundleContext = bundleContext;
+ this.packageAdmin = packageAdmin;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Set<Bundle> getDependencies(Bundle rootBundle) {
+ Set<Bundle> dependencies = new HashSet<Bundle>();
+
+ Bundle[] bundles = this.bundleContext.getBundles();
+
+ if (bundles != null) {
+ for (Bundle bundle : bundles) {
+ ExportedPackage[] exportedPackages = this.packageAdmin.getExportedPackages(bundle);
+ if (exportedPackages != null) {
+ for (ExportedPackage exportedPackage : exportedPackages) {
+ Bundle[] importers = exportedPackage.getImportingBundles();
+ if (importers != null) {
+ for (Bundle importer : importers) {
+ if (importer.equals(rootBundle)) {
+ dependencies.add(bundle);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return dependencies;
+ }
+}
diff --git a/org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/spi/ClassLoaderCustomizer.java b/org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/spi/ClassLoaderCustomizer.java
new file mode 100644
index 0000000..29e61cd
--- /dev/null
+++ b/org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/spi/ClassLoaderCustomizer.java
@@ -0,0 +1,36 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 VMware Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Apache License v2.0 which accompanies this distribution.
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Apache License v2.0 is available at
+ * http://www.opensource.org/licenses/apache2.0.php.
+ * You may elect to redistribute this code under either of these licenses.
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.gemini.web.tomcat.spi;
+
+import java.lang.instrument.ClassFileTransformer;
+
+import org.osgi.framework.Bundle;
+
+public interface ClassLoaderCustomizer {
+
+ /**
+ * Allows extensions to customize the {@link ClassLoader} chain created for deployed web applications.
+ *
+ * @param bundle the {@link Bundle} being deployed.
+ * @return the extra <code>ClassLoaders</code> that be added to the end of the chain
+ */
+ ClassLoader[] extendClassLoaderChain(Bundle bundle);
+
+ void addClassFileTransformer(ClassFileTransformer transformer, Bundle bundle);
+
+ ClassLoader createThrowawayClassLoader(Bundle bundle);
+}
diff --git a/org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/spi/WebBundleClassLoaderFactory.java b/org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/spi/WebBundleClassLoaderFactory.java
new file mode 100644
index 0000000..026adc4
--- /dev/null
+++ b/org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/spi/WebBundleClassLoaderFactory.java
@@ -0,0 +1,32 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 VMware Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Apache License v2.0 which accompanies this distribution.
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Apache License v2.0 is available at
+ * http://www.opensource.org/licenses/apache2.0.php.
+ * You may elect to redistribute this code under either of these licenses.
+ *
+ * Contributors:
+ * VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.gemini.web.tomcat.spi;
+
+import org.osgi.framework.Bundle;
+
+/**
+ * TODO Document WebBundleClassLoaderFactory
+ * <p />
+ *
+ * <strong>Concurrent Semantics</strong><br />
+ *
+ * TODO Document concurrent semantics of WebBundleClassLoaderFactory
+ *
+ */
+public interface WebBundleClassLoaderFactory {
+ ClassLoader createWebBundleClassLoader(Bundle webBundle);
+}
diff --git a/org.eclipse.gemini.web.tomcat/src/main/resources/META-INF/MANIFEST.MF b/org.eclipse.gemini.web.tomcat/src/main/resources/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..4c796d2
--- /dev/null
+++ b/org.eclipse.gemini.web.tomcat/src/main/resources/META-INF/MANIFEST.MF
@@ -0,0 +1,32 @@
+Manifest-Version: 1.0
+Export-Package: org.eclipse.gemini.web.tomcat.spi;version="1";uses:="o
+ rg.osgi.framework"
+Bundle-Version: 1
+Tool: Bundlor 1.0.0.M6
+Bundle-Name: SpringSource OSGi Web Container Tomcat Bootstrap
+Bundle-ManifestVersion: 2
+Bundle-Activator: org.eclipse.gemini.web.tomcat.internal.Activator
+Bundle-SymbolicName: org.eclipse.gemini.web.tomcat
+Import-Package: javax.management;version="0",javax.naming;version="0",
+ javax.naming.directory;version="0",javax.servlet;version="2.5",org.ap
+ ache.catalina;version="6.0.20.S2-r5956",org.apache.catalina.connector
+ ;version="6.0.20.S2-r5956",org.apache.catalina.core;version="6.0.20.S
+ 2-r5956",org.apache.catalina.deploy;version="6.0.20.S2-r5956",org.apa
+ che.catalina.startup;version="6.0.20.S2-r5956",org.apache.catalina.ut
+ il;version="6.0.20.S2-r5956",org.apache.juli.logging;version="6.0.20.
+ S2-r5956",org.apache.naming;version="6.0.20.S2-r5956",org.apache.nami
+ ng.resources;version="6.0.20.S2-r5956",org.apache.tomcat;version="6.0
+ .20.S2-r5956",org.apache.tomcat.util;version="6.0.20.S2-r5956",org.ap
+ ache.tomcat.util.digester;version="6.0.20.S2-r5956",org.apache.tomcat
+ .util.modeler;version="6.0.20.S2-r5956",org.eclipse.gemini.web.core;v
+ ersion="1.0",org.eclipse.gemini.web.core.spi;version="1.0",org.eclips
+ e.osgi.baseadaptor;version="0";resolution:="optional",org.eclipse.osg
+ i.baseadaptor.bundlefile;version="0";resolution:="optional",org.eclip
+ se.osgi.framework.adaptor;version="0";resolution:="optional",org.ecli
+ pse.osgi.framework.internal.core;version="0";resolution:="optional",o
+ rg.eclipse.virgo.util.io;version="2.0",org.eclipse.virgo.util.osgi;ve
+ rsion="2.0",org.osgi.framework;version="0",org.osgi.service.packagead
+ min;version="1.0",org.osgi.service.url;version="1.0",org.osgi.util.tr
+ acker;version="1.4.2",org.slf4j;version="1.5.10",org.springframework.
+ osgi.util;version="1.2.0",org.xml.sax;version="0"
+
diff --git a/org.eclipse.gemini.web.tomcat/src/main/resources/META-INF/tomcat/default-server.xml b/org.eclipse.gemini.web.tomcat/src/main/resources/META-INF/tomcat/default-server.xml
new file mode 100644
index 0000000..5d7a0a8
--- /dev/null
+++ b/org.eclipse.gemini.web.tomcat/src/main/resources/META-INF/tomcat/default-server.xml
@@ -0,0 +1,76 @@
+<?xml version='1.0' encoding='utf-8'?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<!-- Note: A "Server" is not itself a "Container", so you may not
+ define subcomponents such as "Valves" at this level.
+ Documentation at /docs/config/server.html
+ -->
+<Server port="8005" shutdown="SHUTDOWN">
+
+
+ <!--Initialize Jasper prior to webapps are loaded. Documentation at /docs/jasper-howto.html
+-->
+ <Listener className="org.apache.catalina.core.JasperListener" />
+ <!-- JMX Support for the Tomcat server. Documentation at /docs/non-existent.html
+-->
+ <Listener className="org.apache.catalina.mbeans.ServerLifecycleListener" />
+
+ <!-- A "Service" is a collection of one or more "Connectors" that share
+ a single "Container" Note: A "Service" is not itself a "Container",
+ so you may not define subcomponents such as "Valves" at this level.
+ Documentation at /docs/config/service.html
+ -->
+ <Service name="Catalina">
+
+
+ <!-- A "Connector" represents an endpoint by which requests are received
+ and responses are returned. Documentation at :
+ Java HTTP Connector: /docs/config/http.html (blocking & non-blocking)
+ Java AJP Connector: /docs/config/ajp.html
+ APR (HTTP/AJP) Connector: /docs/apr.html
+ Define a non-SSL HTTP/1.1 Connector on port 8080
+ -->
+ <Connector port="8080" protocol="HTTP/1.1"
+ connectionTimeout="20000"
+ redirectPort="8443" />
+
+ <!-- Define an AJP 1.3 Connector on port 8009 -->
+ <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
+
+
+ <!-- An Engine represents the entry point (within Catalina) that processes
+ every request. The Engine implementation for Tomcat stand alone
+ analyzes the HTTP headers included with the request, and passes them
+ on to the appropriate Host (virtual host).
+ Documentation at /docs/config/engine.html
+-->
+
+ <!-- You should set jvmRoute to support load-balancing via AJP ie :
+ <Engine name="Catalina" defaultHost="localhost" jvmRoute="jvm1">
+ -->
+ <Engine name="Catalina" defaultHost="localhost">
+
+ <!-- Define the default virtual host
+ Note: XML Schema validation will not work with Xerces 2.2.
+ -->
+ <Host name="localhost" deployOnStartup="false" autoDeploy="false"
+ unpackWARs="true" xmlValidation="false" xmlNamespaceAware="false">
+
+ </Host>
+ </Engine>
+ </Service>
+</Server>
diff --git a/org.eclipse.gemini.web.tomcat/src/main/resources/web-embed.xml b/org.eclipse.gemini.web.tomcat/src/main/resources/web-embed.xml
new file mode 100644
index 0000000..88e9f49
--- /dev/null
+++ b/org.eclipse.gemini.web.tomcat/src/main/resources/web-embed.xml
@@ -0,0 +1,1186 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<web-app xmlns="http://java.sun.com/xml/ns/javaee"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
+ version="2.5">
+
+ <!-- ======================== Introduction ============================== -->
+ <!-- This document defines default values for *all* web applications -->
+ <!-- loaded into this instance of Tomcat. As each application is -->
+ <!-- deployed, this file is processed, followed by the -->
+ <!-- "/WEB-INF/web.xml" deployment descriptor from your own -->
+ <!-- applications. -->
+ <!-- -->
+ <!-- WARNING: Do not configure application-specific resources here! -->
+ <!-- They should go in the "/WEB-INF/web.xml" file in your application. -->
+
+
+ <!-- ================== Built In Servlet Definitions ==================== -->
+
+
+ <!-- The default servlet for all web applications, that serves static -->
+ <!-- resources. It processes all requests that are not mapped to other -->
+ <!-- servlets with servlet mappings (defined either here or in your own -->
+ <!-- web.xml file. This servlet supports the following initialization -->
+ <!-- parameters (default values are in square brackets): -->
+ <!-- -->
+ <!-- debug Debugging detail level for messages logged -->
+ <!-- by this servlet. [0] -->
+ <!-- -->
+ <!-- fileEncoding Encoding to be used to read static resources -->
+ <!-- [platform default] -->
+ <!-- -->
+ <!-- input Input buffer size (in bytes) when reading -->
+ <!-- resources to be served. [2048] -->
+ <!-- -->
+ <!-- listings Should directory listings be produced if there -->
+ <!-- is no welcome file in this directory? [false] -->
+ <!-- WARNING: Listings for directories with many -->
+ <!-- entries can be slow and may consume -->
+ <!-- significant proportions of server resources. -->
+ <!-- -->
+ <!-- output Output buffer size (in bytes) when writing -->
+ <!-- resources to be served. [2048] -->
+ <!-- -->
+ <!-- readonly Is this context "read only", so HTTP -->
+ <!-- commands like PUT and DELETE are -->
+ <!-- rejected? [true] -->
+ <!-- -->
+ <!-- readmeFile File name to display with the directory -->
+ <!-- contents. [null] -->
+ <!-- -->
+ <!-- sendfileSize If the connector used supports sendfile, this -->
+ <!-- represents the minimal file size in KB for -->
+ <!-- which sendfile will be used. Use a negative -->
+ <!-- value to always disable sendfile. [48] -->
+ <!-- -->
+ <!-- For directory listing customization. Checks localXsltFile, then -->
+ <!-- globalXsltFile, then defaults to original behavior. -->
+ <!-- -->
+ <!-- localXsltFile Make directory listings an XML doc and -->
+ <!-- pass the result to this style sheet residing -->
+ <!-- in that directory. This overrides -->
+ <!-- globalXsltFile[null] -->
+ <!-- -->
+ <!-- globalXsltFile Site wide configuration version of -->
+ <!-- localXsltFile This argument is expected -->
+ <!-- to be a physical file. [null] -->
+ <!-- -->
+ <!-- -->
+
+ <servlet>
+ <servlet-name>default</servlet-name>
+ <servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
+ <init-param>
+ <param-name>debug</param-name>
+ <param-value>0</param-value>
+ </init-param>
+ <init-param>
+ <param-name>listings</param-name>
+ <param-value>false</param-value>
+ </init-param>
+ <load-on-startup>1</load-on-startup>
+ </servlet>
+
+
+ <!-- The "invoker" servlet, which executes anonymous servlet classes -->
+ <!-- that have not been defined in a web.xml file. Traditionally, this -->
+ <!-- servlet is mapped to the URL pattern "/servlet/*", but you can map -->
+ <!-- it to other patterns as well. The extra path info portion of such a -->
+ <!-- request must be the fully qualified class name of a Java class that -->
+ <!-- implements Servlet (or extends HttpServlet), or the servlet name -->
+ <!-- of an existing servlet definition. This servlet supports the -->
+ <!-- following initialization parameters (default values are in square -->
+ <!-- brackets): -->
+ <!-- -->
+ <!-- debug Debugging detail level for messages logged -->
+ <!-- by this servlet. [0] -->
+
+<!--
+ <servlet>
+ <servlet-name>invoker</servlet-name>
+ <servlet-class>
+ org.apache.catalina.servlets.InvokerServlet
+ </servlet-class>
+ <init-param>
+ <param-name>debug</param-name>
+ <param-value>0</param-value>
+ </init-param>
+ <load-on-startup>2</load-on-startup>
+ </servlet>
+-->
+
+
+ <!-- The JSP page compiler and execution servlet, which is the mechanism -->
+ <!-- used by Tomcat to support JSP pages. Traditionally, this servlet -->
+ <!-- is mapped to the URL pattern "*.jsp". This servlet supports the -->
+ <!-- following initialization parameters (default values are in square -->
+ <!-- brackets): -->
+ <!-- -->
+ <!-- checkInterval If development is false and checkInterval is -->
+ <!-- greater than zero, background compilations are -->
+ <!-- enabled. checkInterval is the time in seconds -->
+ <!-- between checks to see if a JSP page (and its -->
+ <!-- dependent files) needs to be recompiled. [0] -->
+ <!-- -->
+ <!-- classdebuginfo Should the class file be compiled with -->
+ <!-- debugging information? [true] -->
+ <!-- -->
+ <!-- classpath What class path should I use while compiling -->
+ <!-- generated servlets? [Created dynamically -->
+ <!-- based on the current web application] -->
+ <!-- -->
+ <!-- compiler Which compiler Ant should use to compile JSP -->
+ <!-- pages. See the jasper documentation for more -->
+ <!-- information. -->
+ <!-- -->
+ <!-- compilerSourceVM Compiler source VM -->
+ <!-- default is System.properties -->
+ <!-- java.specification.version > 1.4 -->
+ <!-- [1.5] else [1.4] -->
+ <!-- -->
+ <!-- compilerTargetVM Compiler target VM -->
+ <!-- default is System.properties -->
+ <!-- java.specification.version > 1.4 -->
+ <!-- [1.5] else [1.4] -->
+ <!-- -->
+ <!-- development Is Jasper used in development mode? If true, -->
+ <!-- the frequency at which JSPs are checked for -->
+ <!-- modification may be specified via the -->
+ <!-- modificationTestInterval parameter. [true] -->
+ <!-- -->
+ <!-- displaySourceFragment -->
+ <!-- Should a source fragment be included in -->
+ <!-- exception messages? [true] -->
+ <!-- -->
+ <!-- dumpSmap Should the SMAP info for JSR45 debugging be -->
+ <!-- dumped to a file? [false] -->
+ <!-- False if suppressSmap is true -->
+ <!-- -->
+ <!-- enablePooling Determines whether tag handler pooling is -->
+ <!-- enabled [true] -->
+ <!-- -->
+ <!-- engineOptionsClass Allows specifying the Options class used to -->
+ <!-- configure Jasper. If not present, the default -->
+ <!-- EmbeddedServletOptions will be used. -->
+ <!-- -->
+ <!-- errorOnUseBeanInvalidClassAttribute -->
+ <!-- Should Jasper issue an error when the value of -->
+ <!-- the class attribute in an useBean action is -->
+ <!-- not a valid bean class? [true] -->
+ <!-- -->
+ <!-- fork Tell Ant to fork compiles of JSP pages so that -->
+ <!-- a separate JVM is used for JSP page compiles -->
+ <!-- from the one Tomcat is running in. [true] -->
+ <!-- -->
+ <!-- genStrAsCharArray Should text strings be generated as char -->
+ <!-- arrays, to improve performance in some cases? -->
+ <!-- [false] -->
+ <!-- -->
+ <!-- ieClassId The class-id value to be sent to Internet -->
+ <!-- Explorer when using <jsp:plugin> tags. -->
+ <!-- [clsid:8AD9C840-044E-11D1-B3E9-00805F499D93] -->
+ <!-- -->
+ <!-- javaEncoding Java file encoding to use for generating java -->
+ <!-- source files. [UTF8] -->
+ <!-- -->
+ <!-- keepgenerated Should we keep the generated Java source code -->
+ <!-- for each page instead of deleting it? [true] -->
+ <!-- -->
+ <!-- mappedfile Should we generate static content with one -->
+ <!-- print statement per input line, to ease -->
+ <!-- debugging? [true] -->
+ <!-- -->
+ <!-- modificationTestInterval -->
+ <!-- Causes a JSP (and its dependent files) to not -->
+ <!-- be checked for modification during the -->
+ <!-- specified time interval (in seconds) from the -->
+ <!-- last time the JSP was checked for -->
+ <!-- modification. A value of 0 will cause the JSP -->
+ <!-- to be checked on every access. -->
+ <!-- Used in development mode only. [4] -->
+ <!-- -->
+ <!-- scratchdir What scratch directory should we use when -->
+ <!-- compiling JSP pages? [default work directory -->
+ <!-- for the current web application] -->
+ <!-- -->
+ <!-- suppressSmap Should the generation of SMAP info for JSR45 -->
+ <!-- debugging be suppressed? [false] -->
+ <!-- -->
+ <!-- trimSpaces Should white spaces in template text between -->
+ <!-- actions or directives be trimmed? [false] -->
+ <!-- -->
+ <!-- xpoweredBy Determines whether X-Powered-By response -->
+ <!-- header is added by generated servlet [false] -->
+ <!-- -->
+ <!-- If you wish to use Jikes to compile JSP pages: -->
+ <!-- Please see the "Using Jikes" section of the Jasper-HowTo -->
+ <!-- page in the Tomcat documentation. -->
+
+ <servlet>
+ <servlet-name>jsp</servlet-name>
+ <servlet-class>org.apache.jasper.servlet.JspServlet</servlet-class>
+ <init-param>
+ <param-name>fork</param-name>
+ <param-value>false</param-value>
+ </init-param>
+ <init-param>
+ <param-name>xpoweredBy</param-name>
+ <param-value>false</param-value>
+ </init-param>
+ <load-on-startup>3</load-on-startup>
+ </servlet>
+
+
+ <!-- NOTE: An SSI Filter is also available as an alternative SSI -->
+ <!-- implementation. Use either the Servlet or the Filter but NOT both. -->
+ <!-- -->
+ <!-- Server Side Includes processing servlet, which processes SSI -->
+ <!-- directives in HTML pages consistent with similar support in web -->
+ <!-- servers like Apache. Traditionally, this servlet is mapped to the -->
+ <!-- URL pattern "*.shtml". This servlet supports the following -->
+ <!-- initialization parameters (default values are in square brackets): -->
+ <!-- -->
+ <!-- buffered Should output from this servlet be buffered? -->
+ <!-- (0=false, 1=true) [0] -->
+ <!-- -->
+ <!-- debug Debugging detail level for messages logged -->
+ <!-- by this servlet. [0] -->
+ <!-- -->
+ <!-- expires The number of seconds before a page with SSI -->
+ <!-- directives will expire. [No default] -->
+ <!-- -->
+ <!-- isVirtualWebappRelative -->
+ <!-- Should "virtual" paths be interpreted as -->
+ <!-- relative to the context root, instead of -->
+ <!-- the server root? (0=false, 1=true) [0] -->
+ <!-- -->
+ <!-- inputEncoding The encoding to assume for SSI resources if -->
+ <!-- one is not available from the resource. -->
+ <!-- [Platform default] -->
+ <!-- -->
+ <!-- outputEncoding The encoding to use for the page that results -->
+ <!-- from the SSI processing. [UTF-8] -->
+
+<!--
+ <servlet>
+ <servlet-name>ssi</servlet-name>
+ <servlet-class>
+ org.apache.catalina.ssi.SSIServlet
+ </servlet-class>
+ <init-param>
+ <param-name>buffered</param-name>
+ <param-value>1</param-value>
+ </init-param>
+ <init-param>
+ <param-name>debug</param-name>
+ <param-value>0</param-value>
+ </init-param>
+ <init-param>
+ <param-name>expires</param-name>
+ <param-value>666</param-value>
+ </init-param>
+ <init-param>
+ <param-name>isVirtualWebappRelative</param-name>
+ <param-value>0</param-value>
+ </init-param>
+ <load-on-startup>4</load-on-startup>
+ </servlet>
+-->
+
+
+ <!-- Common Gateway Includes (CGI) processing servlet, which supports -->
+ <!-- execution of external applications that conform to the CGI spec -->
+ <!-- requirements. Typically, this servlet is mapped to the URL pattern -->
+ <!-- "/cgi-bin/*", which means that any CGI applications that are -->
+ <!-- executed must be present within the web application. This servlet -->
+ <!-- supports the following initialization parameters (default values -->
+ <!-- are in square brackets): -->
+ <!-- -->
+ <!-- cgiPathPrefix The CGI search path will start at -->
+ <!-- webAppRootDir + File.separator + this prefix. -->
+ <!-- [WEB-INF/cgi] -->
+ <!-- -->
+ <!-- debug Debugging detail level for messages logged -->
+ <!-- by this servlet. [0] -->
+ <!-- -->
+ <!-- executable Name of the exectuable used to run the -->
+ <!-- script. [perl] -->
+ <!-- -->
+ <!-- parameterEncoding Name of parameter encoding to be used with -->
+ <!-- CGI servlet. -->
+ <!-- [System.getProperty("file.encoding","UTF-8")] -->
+ <!-- -->
+ <!-- passShellEnvironment Should the shell environment variables (if -->
+ <!-- any) be passed to the CGI script? [false] -->
+
+<!--
+ <servlet>
+ <servlet-name>cgi</servlet-name>
+ <servlet-class>org.apache.catalina.servlets.CGIServlet</servlet-class>
+ <init-param>
+ <param-name>debug</param-name>
+ <param-value>0</param-value>
+ </init-param>
+ <init-param>
+ <param-name>cgiPathPrefix</param-name>
+ <param-value>WEB-INF/cgi</param-value>
+ </init-param>
+ <load-on-startup>5</load-on-startup>
+ </servlet>
+-->
+
+
+ <!-- ================ Built In Servlet Mappings ========================= -->
+
+
+ <!-- The servlet mappings for the built in servlets defined above. Note -->
+ <!-- that, by default, the CGI and SSI servlets are *not* mapped. You -->
+ <!-- must uncomment these mappings (or add them to your application's own -->
+ <!-- web.xml deployment descriptor) to enable these services -->
+
+ <!-- The mapping for the default servlet -->
+ <servlet-mapping>
+ <servlet-name>default</servlet-name>
+ <url-pattern>/</url-pattern>
+ </servlet-mapping>
+
+ <!-- The mapping for the invoker servlet -->
+<!--
+ <servlet-mapping>
+ <servlet-name>invoker</servlet-name>
+ <url-pattern>/servlet/*</url-pattern>
+ </servlet-mapping>
+-->
+
+ <!-- The mapping for the JSP servlet -->
+ <servlet-mapping>
+ <servlet-name>jsp</servlet-name>
+ <url-pattern>*.jsp</url-pattern>
+ </servlet-mapping>
+
+ <servlet-mapping>
+ <servlet-name>jsp</servlet-name>
+ <url-pattern>*.jspx</url-pattern>
+ </servlet-mapping>
+
+ <!-- The mapping for the SSI servlet -->
+<!--
+ <servlet-mapping>
+ <servlet-name>ssi</servlet-name>
+ <url-pattern>*.shtml</url-pattern>
+ </servlet-mapping>
+-->
+
+ <!-- The mapping for the CGI Gateway servlet -->
+
+<!--
+ <servlet-mapping>
+ <servlet-name>cgi</servlet-name>
+ <url-pattern>/cgi-bin/*</url-pattern>
+ </servlet-mapping>
+-->
+
+
+ <!-- ================== Built In Filter Definitions ===================== -->
+
+ <!-- NOTE: An SSI Servlet is also available as an alternative SSI -->
+ <!-- implementation. Use either the Servlet or the Filter but NOT both. -->
+ <!-- -->
+ <!-- Server Side Includes processing filter, which processes SSI -->
+ <!-- directives in HTML pages consistent with similar support in web -->
+ <!-- servers like Apache. Traditionally, this filter is mapped to the -->
+ <!-- URL pattern "*.shtml", though it can be mapped to "*" as it will -->
+ <!-- selectively enable/disable SSI processing based on mime types. For -->
+ <!-- this to work you will need to uncomment the .shtml mime type -->
+ <!-- definition towards the bottom of this file. -->
+ <!-- The contentType init param allows you to apply SSI processing to JSP -->
+ <!-- pages, javascript, or any other content you wish. This filter -->
+ <!-- supports the following initialization parameters (default values are -->
+ <!-- in square brackets): -->
+ <!-- -->
+ <!-- contentType A regex pattern that must be matched before -->
+ <!-- SSI processing is applied. -->
+ <!-- [text/x-server-parsed-html(;.*)?] -->
+ <!-- -->
+ <!-- debug Debugging detail level for messages logged -->
+ <!-- by this servlet. [0] -->
+ <!-- -->
+ <!-- expires The number of seconds before a page with SSI -->
+ <!-- directives will expire. [No default] -->
+ <!-- -->
+ <!-- isVirtualWebappRelative -->
+ <!-- Should "virtual" paths be interpreted as -->
+ <!-- relative to the context root, instead of -->
+ <!-- the server root? (0=false, 1=true) [0] -->
+
+<!--
+ <filter>
+ <filter-name>ssi</filter-name>
+ <filter-class>
+ org.apache.catalina.ssi.SSIFilter
+ </filter-class>
+ <init-param>
+ <param-name>contentType</param-name>
+ <param-value>text/x-server-parsed-html(;.*)?</param-value>
+ </init-param>
+ <init-param>
+ <param-name>debug</param-name>
+ <param-value>0</param-value>
+ </init-param>
+ <init-param>
+ <param-name>expires</param-name>
+ <param-value>666</param-value>
+ </init-param>
+ <init-param>
+ <param-name>isVirtualWebappRelative</param-name>
+ <param-value>0</param-value>
+ </init-param>
+ </filter>
+-->
+
+
+ <!-- ==================== Built In Filter Mappings ====================== -->
+
+ <!-- The mapping for the SSI Filter -->
+<!--
+ <filter-mapping>
+ <filter-name>ssi</filter-name>
+ <url-pattern>*.shtml</url-pattern>
+ </filter-mapping>
+-->
+
+
+ <!-- ==================== Default Session Configurati