mirror of
https://github.com/apache/struts.git
synced 2026-08-07 15:46:57 +00:00
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
This commit is contained in:
+1
-6
@@ -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 {
|
||||
|
||||
|
||||
@@ -22,6 +22,14 @@
|
||||
<groupId>org.apache.felix</groupId>
|
||||
<artifactId>org.apache.felix.main</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.felix</groupId>
|
||||
<artifactId>org.apache.felix.shell</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.felix</groupId>
|
||||
<artifactId>org.apache.felix.shell.tui</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.velocity</groupId>
|
||||
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
|
||||
@@ -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<URL> getResources(String name) throws IOException {
|
||||
Collection<URL> coll = DefaultBundleAccessor.getInstance().loadResources(name, true);
|
||||
if (coll == null){
|
||||
return new Hashtable<Object, URL>().elements();
|
||||
}
|
||||
return Collections.enumeration(DefaultBundleAccessor.getInstance().loadResources(name, true));
|
||||
}
|
||||
|
||||
@@ -54,4 +59,5 @@ public class BundleClassLoaderInterface implements ClassLoaderInterface {
|
||||
public ClassLoaderInterface getParent() {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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<PackageConfig> loadPackages(Bundle bundle, BundleContext bundleContext, ObjectFactory objectFactory,
|
||||
FileManager fileManager, Map<String, PackageConfig> pkgConfigs) throws ConfigurationException {
|
||||
FileManagerFactory fileManagerFactory, Map<String, PackageConfig> 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();
|
||||
|
||||
|
||||
@@ -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<URL> resources = new ArrayList<URL>();
|
||||
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<String, Bundle> 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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<String, Object>());
|
||||
}
|
||||
|
||||
/**
|
||||
* 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<String> packagesBeforeLoading = new HashSet(configuration.getPackageConfigNames());
|
||||
Set<String> packagesBeforeLoading = new HashSet<String>(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<String> packagesAfterLoading = new HashSet(configuration.getPackageConfigNames());
|
||||
Set<String> packagesAfterLoading = new HashSet<String>(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<String> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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
|
||||
* <p/>
|
||||
* 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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
/**
|
||||
|
||||
@@ -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<PackageConfig> loadPackages(Bundle bundle, BundleContext bundleContext, ObjectFactory objectFactory, FileManager fileManager,
|
||||
List<PackageConfig> loadPackages(Bundle bundle, BundleContext bundleContext, ObjectFactory objectFactory,
|
||||
FileManagerFactory fileManagerFactory,
|
||||
Map<String, PackageConfig> map) throws ConfigurationException;
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+329
-427
@@ -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
|
||||
* <br/>
|
||||
* Servlet config params:
|
||||
* <p>struts.osgi.clearBundleCache: Defaults to "true" delete installed bundles when the comntainer starts</p>
|
||||
* <p>struts.osgi.logLevel: Defaults to "1". Felix log level. 1 = error, 2 = warning, 3 = information, and 4 = debug </p>
|
||||
* <p>struts.osgi.runLevel: Defaults to "3". Run level to start the container.</p>
|
||||
*/
|
||||
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<BundleActivator> list = new ArrayList<BundleActivator>();
|
||||
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<String> bundleJarsLevel1 = new ArrayList<String>();
|
||||
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<String, String> runLevels = getRunLevelDirs("bundles");
|
||||
if (runLevels.isEmpty()) {
|
||||
//there are no run level dirs, search for bundles in that dir
|
||||
List<String> 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<String> 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<String, String> getRunLevelDirs(String dir) {
|
||||
Map<String, String> dirs = new HashMap<String, String>();
|
||||
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<String> getBundlesInDir(String dir) {
|
||||
List<String> bundleJars = new ArrayList<String>();
|
||||
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<String> exportedPackages = new ArrayList<String>();
|
||||
//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<URL, Set<String>> subpackagesMap = finder.findPackagesMap(StringUtils.replace(rootPackage.trim(), ".", "/"));
|
||||
for (Map.Entry<URL, Set<String>> entry : subpackagesMap.entrySet()) {
|
||||
URL url = entry.getKey();
|
||||
Set<String> 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<String> parts = new ArrayList<String>();
|
||||
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<String, Bundle> getBundles() {
|
||||
Map<String, Bundle> bundles = new HashMap<String, Bundle>();
|
||||
for (Bundle bundle : felix.getBundleContext().getBundles()) {
|
||||
bundles.put(bundle.getSymbolicName(), bundle);
|
||||
}
|
||||
|
||||
return Collections.unmodifiableMap(bundles);
|
||||
}
|
||||
|
||||
public Map<String, Bundle> getActiveBundles() {
|
||||
Map<String, Bundle> bundles = new HashMap<String, Bundle>();
|
||||
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<String, Bundle> getBundles();
|
||||
|
||||
public abstract Map<String, Bundle> 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<String> bundleJarsLevel1 = new ArrayList<String>();
|
||||
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<String, String> runLevels = getRunLevelDirs("bundles");
|
||||
if (runLevels.isEmpty()) {
|
||||
//there are no run level dirs, search for bundles in that dir
|
||||
List<String> 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<String> 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<String, String> getRunLevelDirs(String dir) {
|
||||
Map<String, String> dirs = new HashMap<String, String>();
|
||||
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<String> getBundlesInDir(String dir) {
|
||||
List<String> bundleJars = new ArrayList<String>();
|
||||
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<String> exportedPackages = new ArrayList<String>();
|
||||
//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<URL, Set<String>> subpackagesMap = finder.findPackagesMap(StringUtils.replace(rootPackage.trim(), ".", "/"));
|
||||
for (Map.Entry<URL, Set<String>> entry : subpackagesMap.entrySet()) {
|
||||
URL url = entry.getKey();
|
||||
Set<String> 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<String> parts = new ArrayList<String>();
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
* <br/>
|
||||
* Servlet config params:
|
||||
* <p>struts.osgi.clearBundleCache: Defaults to "true" delete installed bundles when the comntainer starts</p>
|
||||
* <p>struts.osgi.logLevel: Defaults to "1". Felix log level. 1 = error, 2 = warning, 3 = information, and 4 = debug </p>
|
||||
* <p>struts.osgi.runLevel: Defaults to "3". Run level to start the container.</p>
|
||||
*/
|
||||
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<String, Bundle> getBundles() {
|
||||
Map<String, Bundle> bundles = new HashMap<String, Bundle>();
|
||||
for (Bundle bundle : felix.getBundleContext().getBundles()) {
|
||||
bundles.put(bundle.getSymbolicName(), bundle);
|
||||
}
|
||||
|
||||
return Collections.unmodifiableMap(bundles);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Bundle> getActiveBundles() {
|
||||
Map<String, Bundle> bundles = new HashMap<String, Bundle>();
|
||||
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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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<Bundle> installed = new ArrayList<Bundle>();
|
||||
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<URL> findBundles() throws Exception {
|
||||
ArrayList<URL> list = new ArrayList<URL>();
|
||||
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<String, Bundle> getBundles() {
|
||||
Map<String, Bundle> bundles = new HashMap<String, Bundle>();
|
||||
for (Bundle bundle : bctx.getBundles()) {
|
||||
bundles.put(bundle.getSymbolicName(), bundle);
|
||||
}
|
||||
|
||||
return Collections.unmodifiableMap(bundles);
|
||||
}
|
||||
|
||||
public Map<String, Bundle> getActiveBundles() {
|
||||
Map<String, Bundle> bundles = new HashMap<String, Bundle>();
|
||||
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 {
|
||||
}
|
||||
|
||||
}
|
||||
+1
-1
@@ -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;
|
||||
@@ -27,5 +27,7 @@ import org.osgi.framework.BundleContext;
|
||||
* the OsgiInterceptor must be applied to the action.
|
||||
*/
|
||||
public interface BundleContextAware {
|
||||
|
||||
void setBundleContext(BundleContext bundleContext);
|
||||
|
||||
}
|
||||
|
||||
@@ -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<T>, this interceptor will call setService(List<T>)
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -28,5 +28,7 @@ import java.util.List;
|
||||
* @param <T> The type of the service
|
||||
*/
|
||||
public interface ServiceAware<T> {
|
||||
|
||||
void setServices(List<T> services);
|
||||
|
||||
}
|
||||
|
||||
+7
-7
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+1
@@ -48,4 +48,5 @@ public class VelocityBundleResourceLoader extends ClasspathResourceLoader {
|
||||
throw new ResourceNotFoundException(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+13
-8
@@ -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"));
|
||||
}
|
||||
|
||||
}
|
||||
+1
-1
@@ -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;
|
||||
|
||||
|
||||
@@ -493,6 +493,16 @@
|
||||
<dependency>
|
||||
<groupId>org.apache.felix</groupId>
|
||||
<artifactId>org.apache.felix.main</artifactId>
|
||||
<version>4.0.3</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.felix</groupId>
|
||||
<artifactId>org.apache.felix.shell</artifactId>
|
||||
<version>1.4.3</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.felix</groupId>
|
||||
<artifactId>org.apache.felix.shell.tui</artifactId>
|
||||
<version>1.4.1</version>
|
||||
</dependency>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user