From 1c5b931cfb0c814ad59f1f9d1897875739f47652 Mon Sep 17 00:00:00 2001 From: Lukasz Lenart Date: Tue, 21 May 2013 14:14:03 +0000 Subject: [PATCH] WW-3958 Updates Apache Felix and adds option to cooperate with Felix integrated in Glassfish git-svn-id: https://svn.apache.org/repos/asf/struts/struts2/trunk@1484815 13f79535-47bb-0310-9956-ffa450edef68 --- .../osgi/admin/actions/BundlesAction.java | 7 +- plugins/osgi/pom.xml | 8 + .../apache/struts2/osgi/BundleAccessor.java | 13 +- .../osgi/BundleClassLoaderInterface.java | 16 +- .../struts2/osgi/BundlePackageLoader.java | 5 +- .../struts2/osgi/DefaultBundleAccessor.java | 40 +- .../struts2/osgi/DelegatingObjectFactory.java | 6 +- .../osgi/OsgiConfigurationProvider.java | 38 +- .../apache/struts2/osgi/OsgiHostFactory.java | 49 ++ .../org/apache/struts2/osgi/OsgiUtil.java | 15 +- .../apache/struts2/osgi/PackageLoader.java | 5 +- .../struts2/osgi/SpringOsgiObjectFactory.java | 2 + .../struts2/osgi/StrutsOsgiListener.java | 30 +- .../BaseOsgiHost.java} | 756 ++++++++---------- .../struts2/osgi/host/FelixOsgiHost.java | 173 ++++ .../struts2/osgi/host/GlassfishOSGiHost.java | 181 +++++ .../struts2/osgi/{ => host}/OsgiHost.java | 2 +- .../osgi/interceptor/BundleContextAware.java | 2 + .../osgi/interceptor/OsgiInterceptor.java | 16 +- .../osgi/interceptor/ServiceAware.java | 2 + .../StaticContentBundleResourceLoader.java | 14 +- .../loaders/VelocityBundleResourceLoader.java | 1 + .../osgi/{ => host}/FelixOsgiHostTest.java | 21 +- .../osgi/interceptor/OsgiInterceptorTest.java | 2 +- pom.xml | 10 + 25 files changed, 891 insertions(+), 523 deletions(-) create mode 100644 plugins/osgi/src/main/java/org/apache/struts2/osgi/OsgiHostFactory.java rename plugins/osgi/src/main/java/org/apache/struts2/osgi/{FelixOsgiHost.java => host/BaseOsgiHost.java} (62%) create mode 100644 plugins/osgi/src/main/java/org/apache/struts2/osgi/host/FelixOsgiHost.java create mode 100644 plugins/osgi/src/main/java/org/apache/struts2/osgi/host/GlassfishOSGiHost.java rename plugins/osgi/src/main/java/org/apache/struts2/osgi/{ => host}/OsgiHost.java (94%) rename plugins/osgi/src/test/java/org/apache/struts2/osgi/{ => host}/FelixOsgiHostTest.java (54%) diff --git a/bundles/admin/src/main/java/org/apache/struts2/osgi/admin/actions/BundlesAction.java b/bundles/admin/src/main/java/org/apache/struts2/osgi/admin/actions/BundlesAction.java index ee64f301c..5d81de896 100644 --- a/bundles/admin/src/main/java/org/apache/struts2/osgi/admin/actions/BundlesAction.java +++ b/bundles/admin/src/main/java/org/apache/struts2/osgi/admin/actions/BundlesAction.java @@ -26,9 +26,8 @@ import com.opensymphony.xwork2.config.Configuration; import com.opensymphony.xwork2.config.entities.PackageConfig; import com.opensymphony.xwork2.inject.Inject; import org.apache.struts2.osgi.BundleAccessor; -import org.apache.struts2.osgi.OsgiHost; +import org.apache.struts2.osgi.host.OsgiHost; import org.apache.struts2.osgi.StrutsOsgiListener; -import org.apache.struts2.osgi.OsgiConfigurationProvider; import org.apache.struts2.util.ServletContextAware; import org.osgi.framework.Bundle; import org.osgi.framework.BundleException; @@ -40,10 +39,6 @@ import java.util.Collection; import java.util.List; import java.util.Collections; import java.util.Comparator; -import java.util.Map; -import java.util.HashMap; -import java.util.Enumeration; -import java.util.Dictionary; public class BundlesAction extends ActionSupport implements ServletContextAware { diff --git a/plugins/osgi/pom.xml b/plugins/osgi/pom.xml index 34377398f..b1b1dcb8b 100644 --- a/plugins/osgi/pom.xml +++ b/plugins/osgi/pom.xml @@ -22,6 +22,14 @@ org.apache.felix org.apache.felix.main + + org.apache.felix + org.apache.felix.shell + + + org.apache.felix + org.apache.felix.shell.tui + org.apache.velocity diff --git a/plugins/osgi/src/main/java/org/apache/struts2/osgi/BundleAccessor.java b/plugins/osgi/src/main/java/org/apache/struts2/osgi/BundleAccessor.java index 6d48a42e3..83f111ec9 100644 --- a/plugins/osgi/src/main/java/org/apache/struts2/osgi/BundleAccessor.java +++ b/plugins/osgi/src/main/java/org/apache/struts2/osgi/BundleAccessor.java @@ -21,17 +21,17 @@ package org.apache.struts2.osgi; -import java.io.IOException; -import java.io.InputStream; -import java.net.URL; -import java.util.Map; -import java.util.Set; - +import org.apache.struts2.osgi.host.OsgiHost; import org.osgi.framework.Bundle; import org.osgi.framework.BundleContext; import org.osgi.framework.InvalidSyntaxException; import org.osgi.framework.ServiceReference; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.Set; + public interface BundleAccessor { String CURRENT_BUNDLE_NAME = "__bundle_name__"; @@ -59,4 +59,5 @@ public interface BundleAccessor { void setBundleContext(BundleContext bundleContext); void setOsgiHost(OsgiHost osgiHost); + } diff --git a/plugins/osgi/src/main/java/org/apache/struts2/osgi/BundleClassLoaderInterface.java b/plugins/osgi/src/main/java/org/apache/struts2/osgi/BundleClassLoaderInterface.java index 537176c6b..0723624e2 100644 --- a/plugins/osgi/src/main/java/org/apache/struts2/osgi/BundleClassLoaderInterface.java +++ b/plugins/osgi/src/main/java/org/apache/struts2/osgi/BundleClassLoaderInterface.java @@ -23,18 +23,19 @@ package org.apache.struts2.osgi; import com.opensymphony.xwork2.util.finder.ClassLoaderInterface; -import java.net.URL; -import java.util.Enumeration; -import java.util.Collections; import java.io.IOException; import java.io.InputStream; - -import org.osgi.framework.Bundle; +import java.net.URL; +import java.util.Collection; +import java.util.Collections; +import java.util.Enumeration; +import java.util.Hashtable; /** * ClassLoaderInterface instance that delegates to the singleton of DefaultBundleAccessor */ public class BundleClassLoaderInterface implements ClassLoaderInterface { + public Class loadClass(String name) throws ClassNotFoundException { return DefaultBundleAccessor.getInstance().loadClass(name); } @@ -44,6 +45,10 @@ public class BundleClassLoaderInterface implements ClassLoaderInterface { } public Enumeration getResources(String name) throws IOException { + Collection coll = DefaultBundleAccessor.getInstance().loadResources(name, true); + if (coll == null){ + return new Hashtable().elements(); + } return Collections.enumeration(DefaultBundleAccessor.getInstance().loadResources(name, true)); } @@ -54,4 +59,5 @@ public class BundleClassLoaderInterface implements ClassLoaderInterface { public ClassLoaderInterface getParent() { return null; } + } diff --git a/plugins/osgi/src/main/java/org/apache/struts2/osgi/BundlePackageLoader.java b/plugins/osgi/src/main/java/org/apache/struts2/osgi/BundlePackageLoader.java index bb24aa948..1a01414e4 100644 --- a/plugins/osgi/src/main/java/org/apache/struts2/osgi/BundlePackageLoader.java +++ b/plugins/osgi/src/main/java/org/apache/struts2/osgi/BundlePackageLoader.java @@ -21,7 +21,7 @@ package org.apache.struts2.osgi; -import com.opensymphony.xwork2.FileManager; +import com.opensymphony.xwork2.FileManagerFactory; import com.opensymphony.xwork2.ObjectFactory; import com.opensymphony.xwork2.config.Configuration; import com.opensymphony.xwork2.config.ConfigurationException; @@ -49,13 +49,14 @@ public class BundlePackageLoader implements PackageLoader { private static final Logger LOG = LoggerFactory.getLogger(BundlePackageLoader.class); public List loadPackages(Bundle bundle, BundleContext bundleContext, ObjectFactory objectFactory, - FileManager fileManager, Map pkgConfigs) throws ConfigurationException { + FileManagerFactory fileManagerFactory, Map pkgConfigs) throws ConfigurationException { Configuration config = new DefaultConfiguration("struts.xml"); BundleConfigurationProvider prov = new BundleConfigurationProvider("struts.xml", bundle, bundleContext); for (PackageConfig pkg : pkgConfigs.values()) { config.addPackageConfig(pkg.getName(), pkg); } prov.setObjectFactory(objectFactory); + prov.setFileManagerFactory(fileManagerFactory); prov.init(config); prov.loadPackages(); diff --git a/plugins/osgi/src/main/java/org/apache/struts2/osgi/DefaultBundleAccessor.java b/plugins/osgi/src/main/java/org/apache/struts2/osgi/DefaultBundleAccessor.java index c3c476f39..91c436317 100644 --- a/plugins/osgi/src/main/java/org/apache/struts2/osgi/DefaultBundleAccessor.java +++ b/plugins/osgi/src/main/java/org/apache/struts2/osgi/DefaultBundleAccessor.java @@ -21,25 +21,28 @@ package org.apache.struts2.osgi; -import java.io.IOException; -import java.io.InputStream; -import java.net.URL; -import java.net.MalformedURLException; -import java.util.*; -import java.util.Map.Entry; - +import com.opensymphony.xwork2.ActionContext; +import com.opensymphony.xwork2.ActionInvocation; +import com.opensymphony.xwork2.ActionProxy; +import com.opensymphony.xwork2.config.entities.ActionConfig; +import com.opensymphony.xwork2.util.logging.Logger; +import com.opensymphony.xwork2.util.logging.LoggerFactory; +import org.apache.struts2.osgi.host.OsgiHost; import org.osgi.framework.Bundle; import org.osgi.framework.BundleContext; import org.osgi.framework.InvalidSyntaxException; import org.osgi.framework.ServiceReference; -import com.opensymphony.xwork2.util.logging.Logger; -import com.opensymphony.xwork2.util.logging.LoggerFactory; -import com.opensymphony.xwork2.ActionContext; -import com.opensymphony.xwork2.ActionInvocation; -import com.opensymphony.xwork2.ActionProxy; -import com.opensymphony.xwork2.inject.Inject; -import com.opensymphony.xwork2.config.entities.ActionConfig; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; /** * Helper class that find resources and loads classes from the list of bundles @@ -139,8 +142,10 @@ public class DefaultBundleAccessor implements BundleAccessor { if (bundle != null) { List resources = new ArrayList(); Enumeration e = bundle.getResources(name); - while (e.hasMoreElements()) { - resources.add(translate ? OsgiUtil.translateBundleURLToJarURL((URL) e.nextElement(), getCurrentBundle()) : (URL) e.nextElement()); + if (e != null) { + while (e.hasMoreElements()) { + resources.add(translate ? OsgiUtil.translateBundleURLToJarURL((URL) e.nextElement(), getCurrentBundle()) : (URL) e.nextElement()); + } } return resources; } @@ -151,7 +156,7 @@ public class DefaultBundleAccessor implements BundleAccessor { public URL loadResourceFromAllBundles(String name) throws IOException { for (Map.Entry entry : osgiHost.getActiveBundles().entrySet()) { Enumeration e = entry.getValue().getResources(name); - if (e.hasMoreElements()) { + if (e != null && e.hasMoreElements()) { return (URL) e.nextElement(); } } @@ -208,4 +213,5 @@ public class DefaultBundleAccessor implements BundleAccessor { public void setOsgiHost(OsgiHost osgiHost) { this.osgiHost = osgiHost; } + } diff --git a/plugins/osgi/src/main/java/org/apache/struts2/osgi/DelegatingObjectFactory.java b/plugins/osgi/src/main/java/org/apache/struts2/osgi/DelegatingObjectFactory.java index d71686082..079e257f1 100644 --- a/plugins/osgi/src/main/java/org/apache/struts2/osgi/DelegatingObjectFactory.java +++ b/plugins/osgi/src/main/java/org/apache/struts2/osgi/DelegatingObjectFactory.java @@ -21,15 +21,16 @@ package org.apache.struts2.osgi; -import java.util.Map; - import com.opensymphony.xwork2.ObjectFactory; import com.opensymphony.xwork2.config.PackageProvider; import com.opensymphony.xwork2.inject.Container; import com.opensymphony.xwork2.inject.Inject; import org.apache.struts2.util.ObjectFactoryDestroyable; +import java.util.Map; + public class DelegatingObjectFactory extends ObjectFactory implements ObjectFactoryDestroyable { + private ObjectFactory delegateObjectFactory; private BundleAccessor bundleResourceLoader; private OsgiConfigurationProvider osgiConfigurationProvider; @@ -88,4 +89,5 @@ public class DelegatingObjectFactory extends ObjectFactory implements ObjectFact public void setOsgiConfigurationProvider(PackageProvider osgiConfigurationProvider) { this.osgiConfigurationProvider = (OsgiConfigurationProvider) osgiConfigurationProvider; } + } diff --git a/plugins/osgi/src/main/java/org/apache/struts2/osgi/OsgiConfigurationProvider.java b/plugins/osgi/src/main/java/org/apache/struts2/osgi/OsgiConfigurationProvider.java index 17c8ef50e..e61c29acc 100644 --- a/plugins/osgi/src/main/java/org/apache/struts2/osgi/OsgiConfigurationProvider.java +++ b/plugins/osgi/src/main/java/org/apache/struts2/osgi/OsgiConfigurationProvider.java @@ -22,7 +22,6 @@ package org.apache.struts2.osgi; import com.opensymphony.xwork2.ActionContext; -import com.opensymphony.xwork2.FileManager; import com.opensymphony.xwork2.FileManagerFactory; import com.opensymphony.xwork2.ObjectFactory; import com.opensymphony.xwork2.config.Configuration; @@ -34,6 +33,7 @@ import com.opensymphony.xwork2.util.finder.ClassLoaderInterface; import com.opensymphony.xwork2.util.logging.Logger; import com.opensymphony.xwork2.util.logging.LoggerFactory; import org.apache.commons.lang3.StringUtils; +import org.apache.struts2.osgi.host.OsgiHost; import org.apache.struts2.osgi.loaders.VelocityBundleResourceLoader; import org.apache.struts2.views.velocity.VelocityManager; import org.apache.velocity.app.Velocity; @@ -52,11 +52,12 @@ import java.util.Set; * Struts package provider that starts the OSGi container and deelgates package loading */ public class OsgiConfigurationProvider implements PackageProvider, BundleListener { + private static final Logger LOG = LoggerFactory.getLogger(OsgiConfigurationProvider.class); private Configuration configuration; private ObjectFactory objectFactory; - private FileManager fileManager; + private FileManagerFactory fileManagerFactory; private OsgiHost osgiHost; private BundleContext bundleContext; @@ -81,10 +82,10 @@ public class OsgiConfigurationProvider implements PackageProvider, BundleListene if (LOG.isTraceEnabled()) LOG.trace("Loading packages from XML and Convention on startup"); - //init action contect + //init action context ActionContext ctx = ActionContext.getContext(); if (ctx == null) { - ctx = new ActionContext(new HashMap()); + ctx = createActionContext(); ActionContext.setContext(ctx); } @@ -95,7 +96,7 @@ public class OsgiConfigurationProvider implements PackageProvider, BundleListene String bundleName = bundle.getSymbolicName(); if (shouldProcessBundle(bundle) && !bundleNames.contains(bundleName)) { bundleNames.add(bundleName); - //load XML and COnvention config + //load XML and Convention config loadConfigFromBundle(bundle); } } @@ -104,6 +105,10 @@ public class OsgiConfigurationProvider implements PackageProvider, BundleListene bundleContext.addBundleListener(this); } + protected ActionContext createActionContext() { + return new ActionContext(new HashMap()); + } + /** * Loads XML config as well as Convention config from a bundle * Limitation: Constants and Beans are ignored on XML config @@ -117,7 +122,7 @@ public class OsgiConfigurationProvider implements PackageProvider, BundleListene //init action context ActionContext ctx = ActionContext.getContext(); if (ctx == null) { - ctx = new ActionContext(new HashMap()); + ctx = createActionContext(); ActionContext.setContext(ctx); } @@ -133,14 +138,14 @@ public class OsgiConfigurationProvider implements PackageProvider, BundleListene //XML config PackageLoader loader = new BundlePackageLoader(); - for (PackageConfig pkg : loader.loadPackages(bundle, bundleContext, objectFactory, fileManager, configuration.getPackageConfigs())) { + for (PackageConfig pkg : loader.loadPackages(bundle, bundleContext, objectFactory, fileManagerFactory, configuration.getPackageConfigs())) { configuration.addPackageConfig(pkg.getName(), pkg); bundleAccessor.addPackageFromBundle(bundle, pkg.getName()); } //Convention //get the existing packages before reloading the provider (se we can figure out what are the new packages) - Set packagesBeforeLoading = new HashSet(configuration.getPackageConfigNames()); + Set packagesBeforeLoading = new HashSet(configuration.getPackageConfigNames()); PackageProvider conventionPackageProvider = configuration.getContainer().getInstance(PackageProvider.class, "convention.packageProvider"); if (conventionPackageProvider != null) { @@ -149,7 +154,7 @@ public class OsgiConfigurationProvider implements PackageProvider, BundleListene conventionPackageProvider.loadPackages(); } - Set packagesAfterLoading = new HashSet(configuration.getPackageConfigNames()); + Set packagesAfterLoading = new HashSet(configuration.getPackageConfigNames()); packagesAfterLoading.removeAll(packagesBeforeLoading); if (!packagesAfterLoading.isEmpty()) { //add the new packages to the map of bundle -> package @@ -172,7 +177,7 @@ public class OsgiConfigurationProvider implements PackageProvider, BundleListene * Checks for "Struts2-Enabled" header in the bundle */ protected boolean shouldProcessBundle(Bundle bundle) { - String strutsEnabled = (String) bundle.getHeaders().get(OsgiHost.OSGI_HEADER_STRUTS_ENABLED); + String strutsEnabled = bundle.getHeaders().get(OsgiHost.OSGI_HEADER_STRUTS_ENABLED); return "true".equalsIgnoreCase(strutsEnabled); } @@ -205,16 +210,18 @@ public class OsgiConfigurationProvider implements PackageProvider, BundleListene this.servletContext = servletContext; } + @Inject public void setFileManagerFactory(FileManagerFactory fmFactory) { - this.fileManager = fmFactory.getFileManager(); + this.fileManagerFactory = fmFactory; } public void destroy() { try { osgiHost.destroy(); } catch (Exception e) { - if (LOG.isErrorEnabled()) + if (LOG.isErrorEnabled()) { LOG.error("Failed to stop OSGi container", e); + } } } @@ -246,10 +253,13 @@ public class OsgiConfigurationProvider implements PackageProvider, BundleListene protected void onBundleStopped(Bundle bundle) { Set packages = bundleAccessor.getPackagesByBundle(bundle); if (!packages.isEmpty()) { - if (LOG.isTraceEnabled()) + if (LOG.isTraceEnabled()) { LOG.trace("The bundle [#0] has been stopped. The packages [#1] will be disabled", bundle.getSymbolicName(), StringUtils.join(packages, ",")); - for (String packageName : packages) + } + for (String packageName : packages) { configuration.removePackageConfig(packageName); + } } } + } diff --git a/plugins/osgi/src/main/java/org/apache/struts2/osgi/OsgiHostFactory.java b/plugins/osgi/src/main/java/org/apache/struts2/osgi/OsgiHostFactory.java new file mode 100644 index 000000000..a6588928f --- /dev/null +++ b/plugins/osgi/src/main/java/org/apache/struts2/osgi/OsgiHostFactory.java @@ -0,0 +1,49 @@ +/* + * $Id$ + * + * 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. + */ +package org.apache.struts2.osgi; + +import org.apache.struts2.osgi.host.FelixOsgiHost; +import org.apache.struts2.osgi.host.GlassfishOSGiHost; +import org.apache.struts2.osgi.host.OsgiHost; + +/** + * OsgiHostFactory that creates proper OsgiHost implementation according to + * context param from web.xml + *

+ * Two implementations are supported right now: + * - Apache Felix + * - Glassfish (which contains Apache Felix already) + */ +public class OsgiHostFactory { + + public static final String GLASSFISH = "Glassfish"; + + private OsgiHostFactory() { + } + + public static OsgiHost createOsgiHost(String platform) { + if (GLASSFISH.equalsIgnoreCase(platform)) { + return new GlassfishOSGiHost(); + } + return new FelixOsgiHost(); + } + +} \ No newline at end of file diff --git a/plugins/osgi/src/main/java/org/apache/struts2/osgi/OsgiUtil.java b/plugins/osgi/src/main/java/org/apache/struts2/osgi/OsgiUtil.java index 53aa03acd..e1df19248 100644 --- a/plugins/osgi/src/main/java/org/apache/struts2/osgi/OsgiUtil.java +++ b/plugins/osgi/src/main/java/org/apache/struts2/osgi/OsgiUtil.java @@ -21,19 +21,16 @@ package org.apache.struts2.osgi; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.net.URL; -import java.net.MalformedURLException; - -import org.osgi.framework.BundleContext; -import org.osgi.framework.InvalidSyntaxException; -import org.osgi.framework.ServiceReference; -import org.osgi.framework.Bundle; import com.opensymphony.xwork2.util.logging.Logger; import com.opensymphony.xwork2.util.logging.LoggerFactory; +import org.osgi.framework.Bundle; + +import java.lang.reflect.Method; +import java.net.MalformedURLException; +import java.net.URL; public class OsgiUtil { + private static final Logger LOG = LoggerFactory.getLogger(OsgiUtil.class); /** diff --git a/plugins/osgi/src/main/java/org/apache/struts2/osgi/PackageLoader.java b/plugins/osgi/src/main/java/org/apache/struts2/osgi/PackageLoader.java index eb3319780..69d2cfd93 100644 --- a/plugins/osgi/src/main/java/org/apache/struts2/osgi/PackageLoader.java +++ b/plugins/osgi/src/main/java/org/apache/struts2/osgi/PackageLoader.java @@ -21,7 +21,7 @@ package org.apache.struts2.osgi; -import com.opensymphony.xwork2.FileManager; +import com.opensymphony.xwork2.FileManagerFactory; import com.opensymphony.xwork2.ObjectFactory; import com.opensymphony.xwork2.config.ConfigurationException; import com.opensymphony.xwork2.config.entities.PackageConfig; @@ -36,7 +36,8 @@ import java.util.Map; */ public interface PackageLoader { - List loadPackages(Bundle bundle, BundleContext bundleContext, ObjectFactory objectFactory, FileManager fileManager, + List loadPackages(Bundle bundle, BundleContext bundleContext, ObjectFactory objectFactory, + FileManagerFactory fileManagerFactory, Map map) throws ConfigurationException; } diff --git a/plugins/osgi/src/main/java/org/apache/struts2/osgi/SpringOsgiObjectFactory.java b/plugins/osgi/src/main/java/org/apache/struts2/osgi/SpringOsgiObjectFactory.java index 4e595764e..3cebb328e 100644 --- a/plugins/osgi/src/main/java/org/apache/struts2/osgi/SpringOsgiObjectFactory.java +++ b/plugins/osgi/src/main/java/org/apache/struts2/osgi/SpringOsgiObjectFactory.java @@ -33,6 +33,7 @@ import java.util.Map; * to lookup beans */ public class SpringOsgiObjectFactory extends ObjectFactory { + private final static String SPRING_SERVICE_NAME = "org.springframework.context.ApplicationContext"; private BundleAccessor bundleAccessor; @@ -88,4 +89,5 @@ public class SpringOsgiObjectFactory extends ObjectFactory { public void setBundleAccessor(BundleAccessor bundleAccessor) { this.bundleAccessor = bundleAccessor; } + } diff --git a/plugins/osgi/src/main/java/org/apache/struts2/osgi/StrutsOsgiListener.java b/plugins/osgi/src/main/java/org/apache/struts2/osgi/StrutsOsgiListener.java index ec1c322ec..31b980f0c 100644 --- a/plugins/osgi/src/main/java/org/apache/struts2/osgi/StrutsOsgiListener.java +++ b/plugins/osgi/src/main/java/org/apache/struts2/osgi/StrutsOsgiListener.java @@ -1,26 +1,39 @@ package org.apache.struts2.osgi; +import com.opensymphony.xwork2.util.logging.Logger; +import com.opensymphony.xwork2.util.logging.LoggerFactory; import org.apache.struts2.StrutsException; +import org.apache.struts2.osgi.host.OsgiHost; -import javax.servlet.ServletContextListener; -import javax.servlet.ServletContextEvent; import javax.servlet.ServletContext; +import javax.servlet.ServletContextEvent; +import javax.servlet.ServletContextListener; /** - * ServletContextListener that starts Apache Felix + * ServletContextListener that starts Osgi host */ public class StrutsOsgiListener implements ServletContextListener { + public static final String OSGI_HOST = "__struts_osgi_host"; - private FelixOsgiHost osgiHost; + public static final String PLATFORM_KEY = "struts.osgi.host"; + + private static final Logger LOG = LoggerFactory.getLogger(StrutsOsgiListener.class); + + private OsgiHost osgiHost; public void contextInitialized(ServletContextEvent sce) { ServletContext servletContext = sce.getServletContext(); - osgiHost = new FelixOsgiHost(); + + String platform = servletContext.getInitParameter(PLATFORM_KEY); + if (LOG.isDebugEnabled()) { + LOG.debug("Defined OSGi platform as [#0] via context-param [#1]", platform, PLATFORM_KEY); + } + osgiHost = OsgiHostFactory.createOsgiHost(platform); servletContext.setAttribute(OSGI_HOST, osgiHost); try { osgiHost.init(servletContext); } catch (Exception e) { - throw new StrutsException("Apache Felix failed to start", e); + throw new StrutsException("Cannot init OSGi platform!", e); } } @@ -28,7 +41,8 @@ public class StrutsOsgiListener implements ServletContextListener { try { osgiHost.destroy(); } catch (Exception e) { - throw new StrutsException("Apache Felix failed to stop", e); + throw new StrutsException("Cannot stop OSGi platform!", e); } } -} + +} \ No newline at end of file diff --git a/plugins/osgi/src/main/java/org/apache/struts2/osgi/FelixOsgiHost.java b/plugins/osgi/src/main/java/org/apache/struts2/osgi/host/BaseOsgiHost.java similarity index 62% rename from plugins/osgi/src/main/java/org/apache/struts2/osgi/FelixOsgiHost.java rename to plugins/osgi/src/main/java/org/apache/struts2/osgi/host/BaseOsgiHost.java index 98ed5c390..0670a2134 100644 --- a/plugins/osgi/src/main/java/org/apache/struts2/osgi/FelixOsgiHost.java +++ b/plugins/osgi/src/main/java/org/apache/struts2/osgi/host/BaseOsgiHost.java @@ -1,427 +1,329 @@ -/* - * $Id$ - * - * 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. - */ - -package org.apache.struts2.osgi; - -import com.opensymphony.xwork2.FileManager; -import com.opensymphony.xwork2.FileManagerFactory; -import com.opensymphony.xwork2.config.ConfigurationException; -import com.opensymphony.xwork2.util.finder.ResourceFinder; -import com.opensymphony.xwork2.util.logging.Logger; -import com.opensymphony.xwork2.util.logging.LoggerFactory; -import org.apache.commons.lang3.StringUtils; -import org.apache.felix.framework.Felix; -import org.apache.felix.framework.util.FelixConstants; -import org.apache.felix.main.AutoActivator; -import org.apache.felix.main.Main; -import org.apache.felix.shell.ShellService; -import org.apache.struts2.ServletActionContext; -import org.apache.struts2.StrutsException; -import org.osgi.framework.Bundle; -import org.osgi.framework.BundleActivator; -import org.osgi.framework.BundleContext; -import org.osgi.framework.Constants; -import org.osgi.util.tracker.ServiceTracker; - -import javax.servlet.ServletContext; -import java.io.File; -import java.io.FilenameFilter; -import java.io.IOException; -import java.net.URL; -import java.security.CodeSource; -import java.security.ProtectionDomain; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Properties; -import java.util.Set; -import java.util.jar.JarFile; -import java.util.jar.Manifest; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * Apache felix implementation of an OsgiHost - * See http://felix.apache.org/site/apache-felix-framework-launching-and-embedding.html - *
- * Servlet config params: - *

struts.osgi.clearBundleCache: Defaults to "true" delete installed bundles when the comntainer starts

- *

struts.osgi.logLevel: Defaults to "1". Felix log level. 1 = error, 2 = warning, 3 = information, and 4 = debug

- *

struts.osgi.runLevel: Defaults to "3". Run level to start the container.

- */ -public class FelixOsgiHost implements OsgiHost { - private static final Logger LOG = LoggerFactory.getLogger(FelixOsgiHost.class); - - private Felix felix; - private static final Pattern versionPattern = Pattern.compile("([\\d])+[\\.-]"); - private ServletContext servletContext; - - protected void startFelix() { - //load properties from felix embedded file - Properties configProps = getProperties("default.properties"); - - // Copy framework properties from the system properties. - Main.copySystemProperties(configProps); - replaceSystemPackages(configProps); - - //struts, xwork and felix exported packages - Properties strutsConfigProps = getProperties("struts-osgi.properties"); - addExportedPackages(strutsConfigProps, configProps); - - //find bundles and adde em to autostart property - addAutoStartBundles(configProps); - - // Bundle cache - String storageDir = System.getProperty("java.io.tmpdir") + ".felix-cache"; - configProps.setProperty(Constants.FRAMEWORK_STORAGE, storageDir); - if (LOG.isDebugEnabled()) - LOG.debug("Storing bundles at [#0]", storageDir); - - String cleanBundleCache = getServletContextParam("struts.osgi.clearBundleCache", "true"); - if ("true".equalsIgnoreCase(cleanBundleCache)) { - if (LOG.isDebugEnabled()) - LOG.debug("Clearing bundle cache"); - configProps.put(FelixConstants.FRAMEWORK_STORAGE_CLEAN, FelixConstants.FRAMEWORK_STORAGE_CLEAN_ONFIRSTINIT); - } - - //other properties - configProps.put(FelixConstants.SERVICE_URLHANDLERS_PROP, "false"); - configProps.put(FelixConstants.LOG_LEVEL_PROP, getServletContextParam("struts.osgi.logLevel", "1")); - configProps.put(FelixConstants.BUNDLE_CLASSPATH, "."); - configProps.put(FelixConstants.FRAMEWORK_BEGINNING_STARTLEVEL, getServletContextParam("struts.osgi.runLevel", "3")); - - try { - List list = new ArrayList(); - list.add(new AutoActivator(configProps)); - configProps.put(FelixConstants.SYSTEMBUNDLE_ACTIVATORS_PROP, list); - - felix = new Felix(configProps); - felix.start(); - if (LOG.isTraceEnabled()) - LOG.trace("Apache Felix is running"); - } - catch (Exception ex) { - throw new ConfigurationException("Couldn't start Apache Felix", ex); - } - - addSpringOSGiSupport(); - - //add the bundle context to the ServletContext - servletContext.setAttribute(OSGI_BUNDLE_CONTEXT, felix.getBundleContext()); - } - - /** - * Gets a param from the ServletContext, returning the default value if the param is not set - * - * @param paramName the name of the param to get from the ServletContext - * @param defaultValue value to return if the param is not set - * @return - */ - private String getServletContextParam(String paramName, String defaultValue) { - return StringUtils.defaultString(this.servletContext.getInitParameter(paramName), defaultValue); - } - - protected void addAutoStartBundles(Properties configProps) { - //starts system bundles in level 1 - List bundleJarsLevel1 = new ArrayList(); - bundleJarsLevel1.add(getJarUrl(ShellService.class)); - bundleJarsLevel1.add(getJarUrl(ServiceTracker.class)); - configProps.put(AutoActivator.AUTO_START_PROP + ".1", StringUtils.join(bundleJarsLevel1, " ")); - - //get a list of directories under /bundles with numeric names (the runlevel) - Map runLevels = getRunLevelDirs("bundles"); - if (runLevels.isEmpty()) { - //there are no run level dirs, search for bundles in that dir - List bundles = getBundlesInDir("bundles"); - if (!bundles.isEmpty()) - configProps.put(AutoActivator.AUTO_START_PROP + ".2", StringUtils.join(bundles, " ")); - } else { - for (String runLevel : runLevels.keySet()) { - if ("1".endsWith(runLevel)) - throw new StrutsException("Run level dirs must be greater than 1. Run level 1 is reserved for the Felix bundles"); - List bundles = getBundlesInDir(runLevels.get(runLevel)); - configProps.put(AutoActivator.AUTO_START_PROP + "." + runLevel, StringUtils.join(bundles, " ")); - } - } - } - - /** - * Return a list of directories under a directory whose name is a number - */ - protected Map getRunLevelDirs(String dir) { - Map dirs = new HashMap(); - try { - ResourceFinder finder = new ResourceFinder(); - URL url = finder.find("bundles"); - if (url != null) { - if ("file".equals(url.getProtocol())) { - File bundlesDir = new File(url.toURI()); - String[] runLevelDirs = bundlesDir.list(new FilenameFilter() { - public boolean accept(File file, String name) { - try { - return file.isDirectory() && Integer.valueOf(name) > 0; - } catch (NumberFormatException ex) { - //the name is not a number - return false; - } - } - }); - - if (runLevelDirs != null && runLevelDirs.length > 0) { - //add all the dirs to the list - for (String runLevel : runLevelDirs) - dirs.put(runLevel, StringUtils.chomp(dir, "/") + "/" + runLevel); - - } else if (LOG.isDebugEnabled()) { - LOG.debug("No run level directories found under the [#0] directory", dir); - } - } else if (LOG.isWarnEnabled()) - LOG.warn("Unable to read [#0] directory", dir); - } else if (LOG.isWarnEnabled()) - LOG.warn("The [#0] directory was not found", dir); - } catch (Exception e) { - if (LOG.isWarnEnabled()) - LOG.warn("Unable load bundles from the [#0] directory", e, dir); - } - return dirs; - } - - protected List getBundlesInDir(String dir) { - List bundleJars = new ArrayList(); - try { - - ResourceFinder finder = new ResourceFinder(); - URL url = finder.find(dir); - if (url != null) { - if ("file".equals(url.getProtocol())) { - File bundlesDir = new File(url.toURI()); - File[] bundles = bundlesDir.listFiles(new FilenameFilter() { - public boolean accept(File file, String name) { - return StringUtils.endsWith(name, ".jar"); - } - }); - - if (bundles != null && bundles.length > 0) { - //add all the bundles to the list - for (File bundle : bundles) { - String externalForm = bundle.toURI().toURL().toExternalForm(); - if (LOG.isDebugEnabled()) { - LOG.debug("Adding bundle [#0]", externalForm); - } - bundleJars.add(externalForm); - } - - } else if (LOG.isDebugEnabled()) { - LOG.debug("No bundles found under the [#0] directory", dir); - } - } else if (LOG.isWarnEnabled()) - LOG.warn("Unable to read [#0] directory", dir); - } else if (LOG.isWarnEnabled()) - LOG.warn("The [#0] directory was not found", dir); - } catch (Exception e) { - if (LOG.isWarnEnabled()) - LOG.warn("Unable load bundles from the [#0] directory", e, dir); - } - return bundleJars; - } - - protected void addSpringOSGiSupport() { - // see the javadoc for org.springframework.osgi.web.context.support.OsgiBundleXmlWebApplicationContext for more details - // OsgiBundleXmlWebApplicationContext expects the the BundleContext to be set in the ServletContext under the attribute - // OsgiBundleXmlWebApplicationContext.BUNDLE_CONTEXT_ATTRIBUTE - try { - Class clazz = Class.forName("org.springframework.osgi.web.context.support.OsgiBundleXmlWebApplicationContext"); - String key = (String) clazz.getDeclaredField("BUNDLE_CONTEXT_ATTRIBUTE").get(null); - servletContext.setAttribute(key, felix.getBundleContext()); - } catch (ClassNotFoundException e) { - if (LOG.isDebugEnabled()) { - LOG.debug("Spring OSGi support is not enabled"); - } - } catch (Exception e) { - if (LOG.isErrorEnabled()) { - LOG.error("The API of Spring OSGi has changed and the field [#0] is no longer available. The OSGi plugin needs to be updated", e, - "org.springframework.osgi.web.context.support.OsgiBundleXmlWebApplicationContext.BUNDLE_CONTEXT_ATTRIBUTE"); - } - } - } - - protected String getJarUrl(Class clazz) { - ProtectionDomain protectionDomain = clazz.getProtectionDomain(); - CodeSource codeSource = protectionDomain.getCodeSource(); - URL loc = codeSource.getLocation(); - return loc.toString(); - } - - protected void replaceSystemPackages(Properties properties) { - //Felix has a way to load the config file and substitution expressions - //but the method does not have a way to specify the file (other than in an env variable) - - //${jre-${java.specification.version}} - String systemPackages = (String) properties.get(Constants.FRAMEWORK_SYSTEMPACKAGES); - String jreVersion = "jre-" + System.getProperty("java.version").substring(0, 3); - systemPackages = systemPackages.replace("${jre-${java.specification.version}}", (String) properties.get(jreVersion)); - properties.put(Constants.FRAMEWORK_SYSTEMPACKAGES, systemPackages); - } - - /* - Find subpackages of the packages defined in the property file and export them - */ - protected void addExportedPackages(Properties strutsConfigProps, Properties configProps) { - String[] rootPackages = StringUtils.split((String) strutsConfigProps.get("scanning.package.includes"), ","); - ResourceFinder finder = new ResourceFinder(StringUtils.EMPTY); - List exportedPackages = new ArrayList(); - //build a list of subpackages - for (String rootPackage : rootPackages) { - try { - String version = null; - if (rootPackage.indexOf(";") > 0) { - String[] splitted = rootPackage.split(";"); - rootPackage = splitted[0]; - version = splitted[1]; - } - Map> subpackagesMap = finder.findPackagesMap(StringUtils.replace(rootPackage.trim(), ".", "/")); - for (Map.Entry> entry : subpackagesMap.entrySet()) { - URL url = entry.getKey(); - Set packages = entry.getValue(); - - //get version if not set - if (StringUtils.isBlank(version)) - version = getVersion(url); - - if (packages != null) { - for (String subpackage : packages) { - exportedPackages.add(subpackage + "; version=" + version); - } - } - } - } catch (IOException e) { - if (LOG.isErrorEnabled()) - LOG.error("Unable to find subpackages of [#0]", e, rootPackage); - } - } - - //make a string with the exported packages and add it to the system properties - if (!exportedPackages.isEmpty()) { - String systemPackages = (String) configProps.get(Constants.FRAMEWORK_SYSTEMPACKAGES); - systemPackages = StringUtils.chomp(systemPackages, ",") + "," + StringUtils.join(exportedPackages, ","); - configProps.put(Constants.FRAMEWORK_SYSTEMPACKAGES, systemPackages); - } - } - - /** - * Gets the version used to export the packages. it tries to get it from MANIFEST.MF, or the file name - */ - protected String getVersion(URL url) { - if ("jar".equals(url.getProtocol())) { - try { - FileManager fileManager = ServletActionContext.getContext().getInstance(FileManagerFactory.class).getFileManager(); - JarFile jarFile = new JarFile(new File(fileManager.normalizeToFileProtocol(url).toURI())); - Manifest manifest = jarFile.getManifest(); - if (manifest != null) { - String version = manifest.getMainAttributes().getValue("Bundle-Version"); - if (StringUtils.isNotBlank(version)) { - return getVersionFromString(version); - } - } else { - //try to get the version from the file name - return getVersionFromString(jarFile.getName()); - } - } catch (Exception e) { - if (LOG.isErrorEnabled()) - LOG.error("Unable to extract version from [#0], defaulting to '1.0.0'", url.toExternalForm()); - - } - } - - return "1.0.0"; - } - - /** - * Extracts numbers followed by "." or "-" from the string and joins them with "." - */ - protected static String getVersionFromString(String str) { - Matcher matcher = versionPattern.matcher(str); - List parts = new ArrayList(); - while (matcher.find()) { - parts.add(matcher.group(1)); - } - - //default - if (parts.size() == 0) - return "1.0.0"; - - while (parts.size() < 3) - parts.add("0"); - - return StringUtils.join(parts, "."); - } - - protected Properties getProperties(String fileName) { - ResourceFinder finder = new ResourceFinder(""); - try { - return finder.findProperties(fileName); - } catch (IOException e) { - if (LOG.isErrorEnabled()) - LOG.error("Unable to read property file [#]", fileName); - return new Properties(); - } - } - - /** - * This bundle map will not change, but the status of the bundles can change over time. - * Use getActiveBundles() for active bundles - */ - public Map getBundles() { - Map bundles = new HashMap(); - for (Bundle bundle : felix.getBundleContext().getBundles()) { - bundles.put(bundle.getSymbolicName(), bundle); - } - - return Collections.unmodifiableMap(bundles); - } - - public Map getActiveBundles() { - Map bundles = new HashMap(); - for (Bundle bundle : felix.getBundleContext().getBundles()) { - if (bundle.getState() == Bundle.ACTIVE) - bundles.put(bundle.getSymbolicName(), bundle); - } - - return Collections.unmodifiableMap(bundles); - } - - public BundleContext getBundleContext() { - return felix.getBundleContext(); - } - - public void destroy() throws Exception { - felix.stop(); - if (LOG.isTraceEnabled()) - LOG.trace("Apache Felix has stopped"); - } - - public void init(ServletContext servletContext) { - this.servletContext = servletContext; - startFelix(); - } -} +/* + * $Id$ + * + * 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. + */ +package org.apache.struts2.osgi.host; + +import com.opensymphony.xwork2.FileManager; +import com.opensymphony.xwork2.FileManagerFactory; +import com.opensymphony.xwork2.util.finder.ResourceFinder; +import com.opensymphony.xwork2.util.logging.Logger; +import com.opensymphony.xwork2.util.logging.LoggerFactory; +import org.apache.commons.lang.StringUtils; +import org.apache.felix.main.AutoProcessor; +import org.apache.felix.shell.ShellService; +import org.apache.struts2.ServletActionContext; +import org.apache.struts2.StrutsException; +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleContext; +import org.osgi.framework.Constants; +import org.osgi.util.tracker.ServiceTracker; + +import javax.servlet.ServletContext; +import java.io.File; +import java.io.FilenameFilter; +import java.io.IOException; +import java.net.URL; +import java.security.CodeSource; +import java.security.ProtectionDomain; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.Set; +import java.util.jar.JarFile; +import java.util.jar.Manifest; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * A base OsgiHost implementation + */ +public abstract class BaseOsgiHost implements OsgiHost { + + private static final Logger LOG = LoggerFactory.getLogger(BaseOsgiHost.class); + + protected static final Pattern versionPattern = Pattern.compile("([\\d])+[\\.-]"); + + protected ServletContext servletContext; + + public abstract void init(ServletContext servletContext); + + public abstract void destroy() throws Exception; + + /** + * This bundle map will not change, but the status of the bundles can change over time. + * Use getActiveBundles() for active bundles + */ + public abstract Map getBundles(); + + public abstract Map getActiveBundles(); + + public abstract BundleContext getBundleContext(); + + protected abstract void addSpringOSGiSupport(); + + /** + * Gets a param from the ServletContext, returning the default value if the param is not set + * + * @param paramName the name of the param to get from the ServletContext + * @param defaultValue value to return if the param is not set + * @return + */ + protected String getServletContextParam(String paramName, String defaultValue) { + return StringUtils.defaultString(this.servletContext.getInitParameter(paramName), defaultValue); + } + + protected void addAutoStartBundles(Properties configProps) { + //starts system bundles in level 1 + List bundleJarsLevel1 = new ArrayList(); + bundleJarsLevel1.add(getJarUrl(ShellService.class)); + bundleJarsLevel1.add(getJarUrl(ServiceTracker.class)); + + configProps.put(AutoProcessor.AUTO_START_PROP + ".1", StringUtils.join(bundleJarsLevel1, " ")); + + //get a list of directories under /bundles with numeric names (the runlevel) + Map runLevels = getRunLevelDirs("bundles"); + if (runLevels.isEmpty()) { + //there are no run level dirs, search for bundles in that dir + List bundles = getBundlesInDir("bundles"); + if (!bundles.isEmpty()) { + configProps.put(AutoProcessor.AUTO_START_PROP + ".2", StringUtils.join(bundles, " ")); + } + } else { + for (String runLevel : runLevels.keySet()) { + if ("1".endsWith(runLevel)) { + throw new StrutsException("Run level dirs must be greater than 1. Run level 1 is reserved for the Felix bundles"); + } + List bundles = getBundlesInDir(runLevels.get(runLevel)); + configProps.put(AutoProcessor.AUTO_START_PROP + "." + runLevel, StringUtils.join(bundles, " ")); + } + } + } + + /** + * Return a list of directories under a directory whose name is a number + */ + protected Map getRunLevelDirs(String dir) { + Map dirs = new HashMap(); + try { + ResourceFinder finder = new ResourceFinder(); + URL url = finder.find("bundles"); + if (url != null) { + if ("file".equals(url.getProtocol())) { + File bundlesDir = new File(url.toURI()); + String[] runLevelDirs = bundlesDir.list(new FilenameFilter() { + public boolean accept(File file, String name) { + try { + return file.isDirectory() && Integer.valueOf(name) > 0; + } catch (NumberFormatException ex) { + //the name is not a number + return false; + } + } + }); + + if (runLevelDirs != null && runLevelDirs.length > 0) { + //add all the dirs to the list + for (String runLevel : runLevelDirs) { + dirs.put(runLevel, StringUtils.chomp(dir, "/") + "/" + runLevel); + } + } else if (LOG.isDebugEnabled()) { + LOG.debug("No run level directories found under the [#0] directory", dir); + } + } else if (LOG.isWarnEnabled()) { + LOG.warn("Unable to read [#0] directory", dir); + } + } else if (LOG.isWarnEnabled()) { + LOG.warn("The [#0] directory was not found", dir); + } + } catch (Exception e) { + if (LOG.isWarnEnabled()) { + LOG.warn("Unable load bundles from the [#0] directory", e, dir); + } + } + return dirs; + } + + protected List getBundlesInDir(String dir) { + List bundleJars = new ArrayList(); + try { + ResourceFinder finder = new ResourceFinder(); + URL url = finder.find(dir); + if (url != null) { + if ("file".equals(url.getProtocol())) { + File bundlesDir = new File(url.toURI()); + File[] bundles = bundlesDir.listFiles(new FilenameFilter() { + public boolean accept(File file, String name) { + return StringUtils.endsWith(name, ".jar"); + } + }); + + if (bundles != null && bundles.length > 0) { + //add all the bundles to the list + for (File bundle : bundles) { + String externalForm = bundle.toURI().toURL().toExternalForm(); + if (LOG.isDebugEnabled()) { + LOG.debug("Adding bundle [#0]", externalForm); + } + bundleJars.add(externalForm); + } + + } else if (LOG.isDebugEnabled()) { + LOG.debug("No bundles found under the [#0] directory", dir); + } + } else if (LOG.isWarnEnabled()) { + LOG.warn("Unable to read [#0] directory", dir); + } + } else if (LOG.isWarnEnabled()) { + LOG.warn("The [#0] directory was not found", dir); + } + } catch (Exception e) { + if (LOG.isWarnEnabled()) { + LOG.warn("Unable load bundles from the [#0] directory", e, dir); + } + } + return bundleJars; + } + + protected String getJarUrl(Class clazz) { + ProtectionDomain protectionDomain = clazz.getProtectionDomain(); + CodeSource codeSource = protectionDomain.getCodeSource(); + URL loc = codeSource.getLocation(); + return loc.toString(); + } + + protected void replaceSystemPackages(Properties properties) { + //Felix has a way to load the config file and substitution expressions + //but the method does not have a way to specify the file (other than in an env variable) + + //${jre-${java.specification.version}} + String systemPackages = (String) properties.get(Constants.FRAMEWORK_SYSTEMPACKAGES); + String jreVersion = "jre-" + System.getProperty("java.version").substring(0, 3); + systemPackages = systemPackages.replace("${jre-${java.specification.version}}", (String) properties.get(jreVersion)); + properties.put(Constants.FRAMEWORK_SYSTEMPACKAGES, systemPackages); + } + + /* + Find subpackages of the packages defined in the property file and export them + */ + protected void addExportedPackages(Properties strutsConfigProps, Properties configProps) { + String[] rootPackages = StringUtils.split((String) strutsConfigProps.get("scanning.package.includes"), ","); + ResourceFinder finder = new ResourceFinder(StringUtils.EMPTY); + List exportedPackages = new ArrayList(); + //build a list of subpackages + for (String rootPackage : rootPackages) { + try { + String version = null; + if (rootPackage.indexOf(";") > 0) { + String[] splitted = rootPackage.split(";"); + rootPackage = splitted[0]; + version = splitted[1]; + } + Map> subpackagesMap = finder.findPackagesMap(StringUtils.replace(rootPackage.trim(), ".", "/")); + for (Map.Entry> entry : subpackagesMap.entrySet()) { + URL url = entry.getKey(); + Set packages = entry.getValue(); + + //get version if not set + if (StringUtils.isBlank(version)) { + version = getVersion(url); + } + + if (packages != null) { + for (String subpackage : packages) { + exportedPackages.add(subpackage + "; version=" + version); + } + } + } + } catch (IOException e) { + if (LOG.isErrorEnabled()) { + LOG.error("Unable to find subpackages of [#0]", e, rootPackage); + } + } + } + + //make a string with the exported packages and add it to the system properties + if (!exportedPackages.isEmpty()) { + String systemPackages = (String) configProps.get(Constants.FRAMEWORK_SYSTEMPACKAGES); + systemPackages = StringUtils.chomp(systemPackages, ",") + "," + StringUtils.join(exportedPackages, ","); + configProps.put(Constants.FRAMEWORK_SYSTEMPACKAGES, systemPackages); + } + } + + /** + * Gets the version used to export the packages. it tries to get it from MANIFEST.MF, or the file name + */ + protected String getVersion(URL url) { + if ("jar".equals(url.getProtocol())) { + try { + FileManager fileManager = ServletActionContext.getContext().getInstance(FileManagerFactory.class).getFileManager(); + JarFile jarFile = new JarFile(new File(fileManager.normalizeToFileProtocol(url).toURI())); + Manifest manifest = jarFile.getManifest(); + if (manifest != null) { + String version = manifest.getMainAttributes().getValue("Bundle-Version"); + if (StringUtils.isNotBlank(version)) { + return getVersionFromString(version); + } + } else { + //try to get the version from the file name + return getVersionFromString(jarFile.getName()); + } + } catch (Exception e) { + if (LOG.isErrorEnabled()) { + LOG.error("Unable to extract version from [#0], defaulting to '1.0.0'", url.toExternalForm()); + } + } + } + return "1.0.0"; + } + + /** + * Extracts numbers followed by "." or "-" from the string and joins them with "." + */ + protected String getVersionFromString(String str) { + Matcher matcher = versionPattern.matcher(str); + List parts = new ArrayList(); + while (matcher.find()) { + parts.add(matcher.group(1)); + } + //default + if (parts.size() == 0) { + return "1.0.0"; + } + while (parts.size() < 3) { + parts.add("0"); + } + return StringUtils.join(parts, "."); + } + + protected Properties getProperties(String fileName) { + ResourceFinder finder = new ResourceFinder(""); + try { + return finder.findProperties(fileName); + } catch (IOException e) { + if (LOG.isErrorEnabled()) { + LOG.error("Unable to read property file [#]", fileName); + } + return new Properties(); + } + } + +} \ No newline at end of file diff --git a/plugins/osgi/src/main/java/org/apache/struts2/osgi/host/FelixOsgiHost.java b/plugins/osgi/src/main/java/org/apache/struts2/osgi/host/FelixOsgiHost.java new file mode 100644 index 000000000..77d2614cc --- /dev/null +++ b/plugins/osgi/src/main/java/org/apache/struts2/osgi/host/FelixOsgiHost.java @@ -0,0 +1,173 @@ +/* + * $Id$ + * + * 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. + */ + +package org.apache.struts2.osgi.host; + +import com.opensymphony.xwork2.config.ConfigurationException; +import com.opensymphony.xwork2.util.logging.Logger; +import com.opensymphony.xwork2.util.logging.LoggerFactory; + +import org.apache.felix.framework.Felix; +import org.apache.felix.framework.util.FelixConstants; +import org.apache.felix.main.AutoProcessor; +import org.apache.felix.main.Main; + +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleContext; +import org.osgi.framework.Constants; + +import javax.servlet.ServletContext; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; + +/** + * Apache felix implementation of an OsgiHost + * See http://felix.apache.org/site/apache-felix-framework-launching-and-embedding.html + *
+ * Servlet config params: + *

struts.osgi.clearBundleCache: Defaults to "true" delete installed bundles when the comntainer starts

+ *

struts.osgi.logLevel: Defaults to "1". Felix log level. 1 = error, 2 = warning, 3 = information, and 4 = debug

+ *

struts.osgi.runLevel: Defaults to "3". Run level to start the container.

+ */ +public class FelixOsgiHost extends BaseOsgiHost { + + private static final Logger LOG = LoggerFactory.getLogger(FelixOsgiHost.class); + + protected Felix felix; + + protected void startFelix() { + //load properties from felix embedded file + Properties configProps = getProperties("default.properties"); + + // Copy framework properties from the system properties. + Main.copySystemProperties(configProps); + replaceSystemPackages(configProps); + + //struts, xwork and felix exported packages + Properties strutsConfigProps = getProperties("struts-osgi.properties"); + addExportedPackages(strutsConfigProps, configProps); + + //find bundles and adde em to autostart property + addAutoStartBundles(configProps); + + // Bundle cache + String storageDir = System.getProperty("java.io.tmpdir") + ".felix-cache"; + configProps.setProperty(Constants.FRAMEWORK_STORAGE, storageDir); + if (LOG.isDebugEnabled()) + LOG.debug("Storing bundles at [#0]", storageDir); + + String cleanBundleCache = getServletContextParam("struts.osgi.clearBundleCache", "true"); + if ("true".equalsIgnoreCase(cleanBundleCache)) { + if (LOG.isDebugEnabled()) + LOG.debug("Clearing bundle cache"); + configProps.put(FelixConstants.FRAMEWORK_STORAGE_CLEAN, FelixConstants.FRAMEWORK_STORAGE_CLEAN_ONFIRSTINIT); + } + + //other properties + configProps.put(FelixConstants.SERVICE_URLHANDLERS_PROP, "false"); + configProps.put(FelixConstants.LOG_LEVEL_PROP, getServletContextParam("struts.osgi.logLevel", "1")); + configProps.put(FelixConstants.BUNDLE_CLASSPATH, "."); + configProps.put(FelixConstants.FRAMEWORK_BEGINNING_STARTLEVEL, getServletContextParam("struts.osgi.runLevel", "3")); + + try { + felix = new Felix(configProps); + felix.init(); + AutoProcessor.process(configProps, felix.getBundleContext()); + felix.start(); + + if (LOG.isTraceEnabled()) { + LOG.trace("Apache Felix is running"); + } + } + catch (Exception ex) { + throw new ConfigurationException("Couldn't start Apache Felix", ex); + } + + addSpringOSGiSupport(); + + //add the bundle context to the ServletContext + servletContext.setAttribute(OSGI_BUNDLE_CONTEXT, felix.getBundleContext()); + } + + @Override + public void init(ServletContext servletContext) { + this.servletContext = servletContext; + startFelix(); + } + + @Override + public Map getBundles() { + Map bundles = new HashMap(); + for (Bundle bundle : felix.getBundleContext().getBundles()) { + bundles.put(bundle.getSymbolicName(), bundle); + } + + return Collections.unmodifiableMap(bundles); + } + + @Override + public Map getActiveBundles() { + Map bundles = new HashMap(); + for (Bundle bundle : felix.getBundleContext().getBundles()) { + if (bundle.getState() == Bundle.ACTIVE) { + bundles.put(bundle.getSymbolicName(), bundle); + } + } + return Collections.unmodifiableMap(bundles); + } + + @Override + public BundleContext getBundleContext() { + return felix.getBundleContext(); + } + + @Override + public void destroy() throws Exception { + felix.stop(); + if (LOG.isTraceEnabled()) { + LOG.trace("Apache Felix has stopped"); + } + } + + @Override + protected void addSpringOSGiSupport() { + // see the javadoc for org.springframework.osgi.web.context.support.OsgiBundleXmlWebApplicationContext for more details + // OsgiBundleXmlWebApplicationContext expects the the BundleContext to be set in the ServletContext under the attribute + // OsgiBundleXmlWebApplicationContext.BUNDLE_CONTEXT_ATTRIBUTE + try { + Class clazz = Class.forName("org.springframework.osgi.web.context.support.OsgiBundleXmlWebApplicationContext"); + String key = (String) clazz.getDeclaredField("BUNDLE_CONTEXT_ATTRIBUTE").get(null); + servletContext.setAttribute(key, felix.getBundleContext()); + } catch (ClassNotFoundException e) { + if (LOG.isDebugEnabled()) { + LOG.debug("Spring OSGi support is not enabled"); + } + } catch (Exception e) { + if (LOG.isErrorEnabled()) { + LOG.error("The API of Spring OSGi has changed and the field [#0] is no longer available. The OSGi plugin needs to be updated", e, + "org.springframework.osgi.web.context.support.OsgiBundleXmlWebApplicationContext.BUNDLE_CONTEXT_ATTRIBUTE"); + } + } + } + +} diff --git a/plugins/osgi/src/main/java/org/apache/struts2/osgi/host/GlassfishOSGiHost.java b/plugins/osgi/src/main/java/org/apache/struts2/osgi/host/GlassfishOSGiHost.java new file mode 100644 index 000000000..1a500915a --- /dev/null +++ b/plugins/osgi/src/main/java/org/apache/struts2/osgi/host/GlassfishOSGiHost.java @@ -0,0 +1,181 @@ +/* + * $Id$ + * + * 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. + */ +package org.apache.struts2.osgi.host; + +import com.opensymphony.xwork2.util.logging.Logger; +import com.opensymphony.xwork2.util.logging.LoggerFactory; +import org.apache.felix.shell.ShellService; +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleContext; +import org.osgi.framework.BundleException; +import org.osgi.framework.BundleReference; + +import javax.servlet.ServletContext; +import java.net.URL; +import java.security.CodeSource; +import java.security.ProtectionDomain; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * A glassfish implementation of an OsgiHost + */ +public class GlassfishOSGiHost extends BaseOsgiHost implements OsgiHost { + + private static final Logger LOG = LoggerFactory.getLogger(GlassfishOSGiHost.class); + + /** + * Location inside the WAR where initial bundles are located. + */ + private static final String BUNDLES_DIR = "/WEB-INF/classes/bundles/2/"; + + private BundleContext bctx = null; + + @Override + public void init(ServletContext servletContext) { + this.servletContext = servletContext; + + installManagedBundles(); + + addSpringOSGiSupport(); + + // add the bundle context to the ServletContext + servletContext.setAttribute(OSGI_BUNDLE_CONTEXT, bctx); + } + + private void installManagedBundles() { + try { + + // Obtaining BundleContext from ServletContext class which is loaded + // by bundle class loader + BundleReference ref = (BundleReference) servletContext.getClass() + .getClassLoader(); + bctx = ref.getBundle().getBundleContext(); + + // installing managed bundles + installBundles(); + + } catch (Exception ex) { + LOG.error("Installing Managed Bundles met a problem", ex); + } + } + + private void installBundles() throws Exception { + ArrayList installed = new ArrayList(); + for (URL url : findBundles()) { + LOG.debug("Installing bundle [" + url + "]"); + Bundle bundle = bctx.installBundle(url.toExternalForm()); + installed.add(bundle); + } + for (Bundle bundle : installed) { + try { + bundle.start(); + } catch (BundleException e) { + e.printStackTrace(); + LOG.error("Failed to start " + bundle, e); + } + } + + } + + private List findBundles() throws Exception { + ArrayList list = new ArrayList(); + for (Object o : this.servletContext.getResourcePaths(BUNDLES_DIR)) { + String name = (String) o; + if (name.endsWith(".jar")) { + URL url = this.servletContext.getResource(name); + if (url != null) { + list.add(url); + } + } + } + + ProtectionDomain protectionDomain = ShellService.class.getProtectionDomain(); + CodeSource codeSource = protectionDomain.getCodeSource(); + URL loc = codeSource.getLocation(); + list.add(loc); + + return list; + } + + @Override + protected void addSpringOSGiSupport() { + // see the javadoc for + // org.springframework.osgi.web.context.support.OsgiBundleXmlWebApplicationContext + // for more details + // OsgiBundleXmlWebApplicationContext expects the the BundleContext to + // be set in the ServletContext under the attribute + // OsgiBundleXmlWebApplicationContext.BUNDLE_CONTEXT_ATTRIBUTE + try { + Class clazz = Class + .forName("org.springframework.osgi.web.context.support.OsgiBundleXmlWebApplicationContext"); + String key = (String) clazz.getDeclaredField( + "BUNDLE_CONTEXT_ATTRIBUTE").get(null); + servletContext.setAttribute(key, bctx); + } catch (ClassNotFoundException e) { + if (LOG.isDebugEnabled()) { + LOG.debug("Spring OSGi support is not enabled"); + } + } catch (Exception e) { + if (LOG.isErrorEnabled()) { + LOG.error( + "The API of Spring OSGi has changed and the field [#0] is no longer available. The OSGi plugin needs to be updated", + e, + "org.springframework.osgi.web.context.support.OsgiBundleXmlWebApplicationContext.BUNDLE_CONTEXT_ATTRIBUTE"); + } + } + } + + /** + * This bundle map will not change, but the status of the bundles can change + * over time. Use getActiveBundles() for active bundles + */ + @Override + public Map getBundles() { + Map bundles = new HashMap(); + for (Bundle bundle : bctx.getBundles()) { + bundles.put(bundle.getSymbolicName(), bundle); + } + + return Collections.unmodifiableMap(bundles); + } + + public Map getActiveBundles() { + Map bundles = new HashMap(); + for (Bundle bundle : bctx.getBundles()) { + if (bundle.getState() == Bundle.ACTIVE) + bundles.put(bundle.getSymbolicName(), bundle); + } + + return Collections.unmodifiableMap(bundles); + } + + public BundleContext getBundleContext() { + return bctx; + } + + public void destroy() throws Exception { + } + +} \ No newline at end of file diff --git a/plugins/osgi/src/main/java/org/apache/struts2/osgi/OsgiHost.java b/plugins/osgi/src/main/java/org/apache/struts2/osgi/host/OsgiHost.java similarity index 94% rename from plugins/osgi/src/main/java/org/apache/struts2/osgi/OsgiHost.java rename to plugins/osgi/src/main/java/org/apache/struts2/osgi/host/OsgiHost.java index edda076c6..7c333c65e 100644 --- a/plugins/osgi/src/main/java/org/apache/struts2/osgi/OsgiHost.java +++ b/plugins/osgi/src/main/java/org/apache/struts2/osgi/host/OsgiHost.java @@ -19,7 +19,7 @@ * under the License. */ -package org.apache.struts2.osgi; +package org.apache.struts2.osgi.host; import org.osgi.framework.BundleActivator; import org.osgi.framework.Bundle; diff --git a/plugins/osgi/src/main/java/org/apache/struts2/osgi/interceptor/BundleContextAware.java b/plugins/osgi/src/main/java/org/apache/struts2/osgi/interceptor/BundleContextAware.java index 2ea2a7ab8..aec026a79 100644 --- a/plugins/osgi/src/main/java/org/apache/struts2/osgi/interceptor/BundleContextAware.java +++ b/plugins/osgi/src/main/java/org/apache/struts2/osgi/interceptor/BundleContextAware.java @@ -27,5 +27,7 @@ import org.osgi.framework.BundleContext; * the OsgiInterceptor must be applied to the action. */ public interface BundleContextAware { + void setBundleContext(BundleContext bundleContext); + } diff --git a/plugins/osgi/src/main/java/org/apache/struts2/osgi/interceptor/OsgiInterceptor.java b/plugins/osgi/src/main/java/org/apache/struts2/osgi/interceptor/OsgiInterceptor.java index 7589498ed..4f6eed5ff 100644 --- a/plugins/osgi/src/main/java/org/apache/struts2/osgi/interceptor/OsgiInterceptor.java +++ b/plugins/osgi/src/main/java/org/apache/struts2/osgi/interceptor/OsgiInterceptor.java @@ -20,28 +20,27 @@ */ package org.apache.struts2.osgi.interceptor; -import com.opensymphony.xwork2.interceptor.AbstractInterceptor; import com.opensymphony.xwork2.ActionInvocation; +import com.opensymphony.xwork2.inject.Inject; +import com.opensymphony.xwork2.interceptor.AbstractInterceptor; import com.opensymphony.xwork2.util.logging.Logger; import com.opensymphony.xwork2.util.logging.LoggerFactory; -import com.opensymphony.xwork2.inject.Inject; - -import javax.servlet.ServletContext; - -import org.apache.struts2.osgi.OsgiHost; +import org.apache.struts2.osgi.host.OsgiHost; import org.osgi.framework.BundleContext; import org.osgi.framework.ServiceReference; -import java.lang.reflect.Type; +import javax.servlet.ServletContext; import java.lang.reflect.ParameterizedType; -import java.util.List; +import java.lang.reflect.Type; import java.util.ArrayList; +import java.util.List; /** * If a class implements BundleContextAware, this interceptor will call the setBundleContext(BundleContext) * method on it. If a class implements ServiceAware, this interceptor will call setService(List) */ public class OsgiInterceptor extends AbstractInterceptor { + private static final Logger LOG = LoggerFactory.getLogger(OsgiInterceptor.class); private BundleContext bundleContext; @@ -96,4 +95,5 @@ public class OsgiInterceptor extends AbstractInterceptor { public void setServletContext(ServletContext servletContext) { this.bundleContext = (BundleContext) servletContext.getAttribute(OsgiHost.OSGI_BUNDLE_CONTEXT); } + } diff --git a/plugins/osgi/src/main/java/org/apache/struts2/osgi/interceptor/ServiceAware.java b/plugins/osgi/src/main/java/org/apache/struts2/osgi/interceptor/ServiceAware.java index e7a549ea6..9c9fc7fb4 100644 --- a/plugins/osgi/src/main/java/org/apache/struts2/osgi/interceptor/ServiceAware.java +++ b/plugins/osgi/src/main/java/org/apache/struts2/osgi/interceptor/ServiceAware.java @@ -28,5 +28,7 @@ import java.util.List; * @param The type of the service */ public interface ServiceAware { + void setServices(List services); + } diff --git a/plugins/osgi/src/main/java/org/apache/struts2/osgi/loaders/StaticContentBundleResourceLoader.java b/plugins/osgi/src/main/java/org/apache/struts2/osgi/loaders/StaticContentBundleResourceLoader.java index 8cf11f58f..ef2827444 100644 --- a/plugins/osgi/src/main/java/org/apache/struts2/osgi/loaders/StaticContentBundleResourceLoader.java +++ b/plugins/osgi/src/main/java/org/apache/struts2/osgi/loaders/StaticContentBundleResourceLoader.java @@ -21,20 +21,19 @@ package org.apache.struts2.osgi.loaders; -import java.io.IOException; -import java.io.InputStream; -import java.net.URL; - -import org.apache.struts2.dispatcher.DefaultStaticContentLoader; -import org.apache.struts2.osgi.DefaultBundleAccessor; -import org.apache.struts2.osgi.BundleAccessor; import com.opensymphony.xwork2.inject.Inject; +import org.apache.struts2.dispatcher.DefaultStaticContentLoader; +import org.apache.struts2.osgi.BundleAccessor; + +import java.io.IOException; +import java.net.URL; /** * Loads static resources from bundles * */ public class StaticContentBundleResourceLoader extends DefaultStaticContentLoader { + private BundleAccessor bundleAccessor; protected URL findResource(String path) throws IOException { @@ -45,4 +44,5 @@ public class StaticContentBundleResourceLoader extends DefaultStaticContentLoade public void setBundleAccessor(BundleAccessor bundleAccessor) { this.bundleAccessor = bundleAccessor; } + } diff --git a/plugins/osgi/src/main/java/org/apache/struts2/osgi/loaders/VelocityBundleResourceLoader.java b/plugins/osgi/src/main/java/org/apache/struts2/osgi/loaders/VelocityBundleResourceLoader.java index 20ddd5313..ae25ea19b 100644 --- a/plugins/osgi/src/main/java/org/apache/struts2/osgi/loaders/VelocityBundleResourceLoader.java +++ b/plugins/osgi/src/main/java/org/apache/struts2/osgi/loaders/VelocityBundleResourceLoader.java @@ -48,4 +48,5 @@ public class VelocityBundleResourceLoader extends ClasspathResourceLoader { throw new ResourceNotFoundException(e.getMessage()); } } + } diff --git a/plugins/osgi/src/test/java/org/apache/struts2/osgi/FelixOsgiHostTest.java b/plugins/osgi/src/test/java/org/apache/struts2/osgi/host/FelixOsgiHostTest.java similarity index 54% rename from plugins/osgi/src/test/java/org/apache/struts2/osgi/FelixOsgiHostTest.java rename to plugins/osgi/src/test/java/org/apache/struts2/osgi/host/FelixOsgiHostTest.java index 7024167ed..4cd584981 100644 --- a/plugins/osgi/src/test/java/org/apache/struts2/osgi/FelixOsgiHostTest.java +++ b/plugins/osgi/src/test/java/org/apache/struts2/osgi/host/FelixOsgiHostTest.java @@ -18,18 +18,23 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.struts2.osgi; +package org.apache.struts2.osgi.host; import junit.framework.TestCase; +import org.apache.struts2.osgi.host.FelixOsgiHost; public class FelixOsgiHostTest extends TestCase { + + private FelixOsgiHost felixHost = new FelixOsgiHost(); + public void testGetVersionFromString() { - assertEquals("2.1.1", FelixOsgiHost.getVersionFromString("2.1.1-SNAPSHOT")); - assertEquals("2.1.1", FelixOsgiHost.getVersionFromString("2.1.1.SNAPSHOT")); - assertEquals("2.1.1", FelixOsgiHost.getVersionFromString("something-2.1.1.SNAPSHOT")); - assertEquals("2.1.1", FelixOsgiHost.getVersionFromString("something-2-1-1.SNAPSHOT")); - assertEquals("2.1.0", FelixOsgiHost.getVersionFromString("something-2-1.SNAPSHOT")); - assertEquals("2.0.0", FelixOsgiHost.getVersionFromString("something-2.SNAPSHOT")); - assertEquals("1.0.0", FelixOsgiHost.getVersionFromString("something")); + assertEquals("2.1.1", felixHost.getVersionFromString("2.1.1-SNAPSHOT")); + assertEquals("2.1.1", felixHost.getVersionFromString("2.1.1.SNAPSHOT")); + assertEquals("2.1.1", felixHost.getVersionFromString("something-2.1.1.SNAPSHOT")); + assertEquals("2.1.1", felixHost.getVersionFromString("something-2-1-1.SNAPSHOT")); + assertEquals("2.1.0", felixHost.getVersionFromString("something-2-1.SNAPSHOT")); + assertEquals("2.0.0", felixHost.getVersionFromString("something-2.SNAPSHOT")); + assertEquals("1.0.0", felixHost.getVersionFromString("something")); } + } diff --git a/plugins/osgi/src/test/java/org/apache/struts2/osgi/interceptor/OsgiInterceptorTest.java b/plugins/osgi/src/test/java/org/apache/struts2/osgi/interceptor/OsgiInterceptorTest.java index 4cca06a41..d87a0f47b 100644 --- a/plugins/osgi/src/test/java/org/apache/struts2/osgi/interceptor/OsgiInterceptorTest.java +++ b/plugins/osgi/src/test/java/org/apache/struts2/osgi/interceptor/OsgiInterceptorTest.java @@ -1,7 +1,7 @@ package org.apache.struts2.osgi.interceptor; import org.easymock.EasyMock; -import org.apache.struts2.osgi.OsgiHost; +import org.apache.struts2.osgi.host.OsgiHost; import org.osgi.framework.BundleContext; import org.osgi.framework.ServiceReference; diff --git a/pom.xml b/pom.xml index 8de360783..595112242 100644 --- a/pom.xml +++ b/pom.xml @@ -493,6 +493,16 @@ org.apache.felix org.apache.felix.main + 4.0.3 + + + org.apache.felix + org.apache.felix.shell + 1.4.3 + + + org.apache.felix + org.apache.felix.shell.tui 1.4.1