mirror of
https://github.com/apache/struts.git
synced 2026-08-06 07:06:58 +00:00
Move the OSGi plugin to trunk, from the sandbox
git-svn-id: https://svn.apache.org/repos/asf/struts/struts2/trunk@765209 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
@@ -0,0 +1,95 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.apache.struts</groupId>
|
||||
<artifactId>struts2-plugins</artifactId>
|
||||
<version>2.0.9</version>
|
||||
</parent>
|
||||
<groupId>org.apache.struts</groupId>
|
||||
<artifactId>struts2-osgi-plugin</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
<name>Struts 2 OSGI Plugin</name>
|
||||
|
||||
<scm>
|
||||
<connection>scm:svn:http://svn.apache.org/repos/asf/struts/sandbox/trunk/struts2-osgi-plugin/</connection>
|
||||
<developerConnection>scm:svn:https://svn.apache.org/repos/asf/struts/sandbox/trunk/struts2-osgi-plugin/</developerConnection>
|
||||
<url>http://svn.apache.org/viewcvs.cgi/struts/sandbox/trunk/struts2-osgi-plugin/</url>
|
||||
</scm>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<inherited>true</inherited>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-source-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>attach-sources</id>
|
||||
<goals>
|
||||
<goal>jar</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.apache.felix</groupId>
|
||||
<artifactId>org.apache.felix.main</artifactId>
|
||||
<version>1.4.1</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.struts</groupId>
|
||||
<artifactId>struts2-core</artifactId>
|
||||
<version>2.1.7-SNAPSHOT</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.velocity</groupId>
|
||||
<artifactId>velocity</artifactId>
|
||||
<version>1.5</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.velocity</groupId>
|
||||
<artifactId>velocity-tools</artifactId>
|
||||
<version>1.3</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>servlet-api</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>struts</groupId>
|
||||
<artifactId>struts</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>commons-digester</groupId>
|
||||
<artifactId>commons-digester</artifactId>
|
||||
<version>2.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<scope>test</scope>
|
||||
<version>3.8.1</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.easymock</groupId>
|
||||
<artifactId>easymock</artifactId>
|
||||
<scope>test</scope>
|
||||
<version>2.4</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* $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 java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.osgi.framework.Bundle;
|
||||
import org.osgi.framework.BundleContext;
|
||||
import org.osgi.framework.InvalidSyntaxException;
|
||||
import org.osgi.framework.ServiceReference;
|
||||
|
||||
public interface BundleAccessor {
|
||||
|
||||
String CURRENT_BUNDLE_NAME = "__bundle_name__";
|
||||
|
||||
Class loadClass(String name) throws ClassNotFoundException;
|
||||
|
||||
InputStream loadResourceAsStream(String name) throws IOException;
|
||||
|
||||
InputStream loadResourceFromAllBundlesAsStream(String name) throws IOException;
|
||||
|
||||
URL loadResourceFromAllBundles(String name) throws IOException;
|
||||
|
||||
Set<String> getPackagesByBundle(Bundle bundle);
|
||||
|
||||
Object getService(ServiceReference ref);
|
||||
|
||||
ServiceReference getServiceReference(String className);
|
||||
|
||||
ServiceReference[] getServiceReferences(String className, String params) throws InvalidSyntaxException;
|
||||
|
||||
public ServiceReference[] getAllServiceReferences(String className);
|
||||
|
||||
void addPackageFromBundle(Bundle bundle, String packageName);
|
||||
|
||||
void setBundleContext(BundleContext bundleContext);
|
||||
|
||||
void setOsgiHost(OsgiHost osgiHost);
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* $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.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;
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
public URL getResource(String name) {
|
||||
return DefaultBundleAccessor.getInstance().loadResource(name, true);
|
||||
}
|
||||
|
||||
public Enumeration<URL> getResources(String name) throws IOException {
|
||||
return Collections.enumeration(DefaultBundleAccessor.getInstance().loadResources(name, true));
|
||||
}
|
||||
|
||||
public InputStream getResourceAsStream(String name) throws IOException {
|
||||
return DefaultBundleAccessor.getInstance().loadResourceAsStream(name);
|
||||
}
|
||||
|
||||
public ClassLoaderInterface getParent() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* $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 java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.ServletContext;
|
||||
|
||||
import org.apache.struts2.osgi.loaders.FreeMarkerBundleResourceLoader;
|
||||
import org.apache.struts2.views.freemarker.FreemarkerManager;
|
||||
import org.apache.struts2.views.freemarker.StrutsClassTemplateLoader;
|
||||
|
||||
import com.opensymphony.xwork2.util.logging.Logger;
|
||||
import com.opensymphony.xwork2.util.logging.LoggerFactory;
|
||||
|
||||
import freemarker.cache.FileTemplateLoader;
|
||||
import freemarker.cache.MultiTemplateLoader;
|
||||
import freemarker.cache.TemplateLoader;
|
||||
import freemarker.cache.WebappTemplateLoader;
|
||||
|
||||
/**
|
||||
* This class extends FreemarkerManager in core to add a template loader
|
||||
* (that finds resources inside bundles) to MultiTemplateLoader
|
||||
*/
|
||||
public class BundleFreemarkerManager extends FreemarkerManager {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(BundleFreemarkerManager.class);
|
||||
|
||||
@Override
|
||||
protected TemplateLoader getTemplateLoader(ServletContext servletContext) {
|
||||
// construct a FileTemplateLoader for the init-param 'TemplatePath'
|
||||
FileTemplateLoader templatePathLoader = null;
|
||||
|
||||
String templatePath = servletContext.getInitParameter("TemplatePath");
|
||||
if (templatePath == null) {
|
||||
templatePath = servletContext.getInitParameter("templatePath");
|
||||
}
|
||||
|
||||
if (templatePath != null) {
|
||||
try {
|
||||
templatePathLoader = new FileTemplateLoader(new File(templatePath));
|
||||
} catch (IOException e) {
|
||||
if (LOG.isErrorEnabled())
|
||||
LOG.error("Invalid template path specified: [#0]", e, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// presume that most apps will require the class and webapp template loader
|
||||
// if people wish to
|
||||
return templatePathLoader != null ?
|
||||
new MultiTemplateLoader(new TemplateLoader[]{
|
||||
templatePathLoader,
|
||||
new WebappTemplateLoader(servletContext),
|
||||
new StrutsClassTemplateLoader(),
|
||||
new FreeMarkerBundleResourceLoader()
|
||||
})
|
||||
: new MultiTemplateLoader(new TemplateLoader[]{
|
||||
new WebappTemplateLoader(servletContext),
|
||||
new StrutsClassTemplateLoader(),
|
||||
new FreeMarkerBundleResourceLoader()
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* $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 java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.util.*;
|
||||
|
||||
import org.osgi.framework.Bundle;
|
||||
import org.osgi.framework.BundleContext;
|
||||
|
||||
import com.opensymphony.xwork2.ObjectFactory;
|
||||
import com.opensymphony.xwork2.ActionContext;
|
||||
import com.opensymphony.xwork2.inject.Inject;
|
||||
import com.opensymphony.xwork2.config.Configuration;
|
||||
import com.opensymphony.xwork2.config.ConfigurationException;
|
||||
import com.opensymphony.xwork2.config.PackageProvider;
|
||||
import com.opensymphony.xwork2.config.entities.PackageConfig;
|
||||
import com.opensymphony.xwork2.config.impl.DefaultConfiguration;
|
||||
import com.opensymphony.xwork2.config.providers.XmlConfigurationProvider;
|
||||
import com.opensymphony.xwork2.util.location.Location;
|
||||
import com.opensymphony.xwork2.util.logging.Logger;
|
||||
import com.opensymphony.xwork2.util.logging.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Package loader implementation that loads resources from a bundle
|
||||
*/
|
||||
public class BundlePackageLoader implements PackageLoader {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(BundlePackageLoader.class);
|
||||
|
||||
public List<PackageConfig> loadPackages(Bundle bundle, BundleContext bundleContext, ObjectFactory objectFactory, 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.init(config);
|
||||
prov.loadPackages();
|
||||
|
||||
List<PackageConfig> list = new ArrayList<PackageConfig>(config.getPackageConfigs().values());
|
||||
list.removeAll(pkgConfigs.values());
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
static class BundleConfigurationProvider extends XmlConfigurationProvider {
|
||||
private Bundle bundle;
|
||||
private BundleContext bundleContext;
|
||||
|
||||
public BundleConfigurationProvider(String filename, Bundle bundle, BundleContext bundleContext) {
|
||||
super(filename, false);
|
||||
this.bundle = bundle;
|
||||
this.bundleContext = bundleContext;
|
||||
}
|
||||
|
||||
public BundleConfigurationProvider(String filename) {
|
||||
super(filename);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Iterator<URL> getConfigurationUrls(String fileName) throws IOException {
|
||||
Enumeration<URL> e = bundle.getResources("struts.xml");
|
||||
return e.hasMoreElements() ? new EnumeratorIterator<URL>(e) : null;
|
||||
}
|
||||
}
|
||||
|
||||
static class EnumeratorIterator<E> implements Iterator<E> {
|
||||
Enumeration<E> e = null;
|
||||
|
||||
public EnumeratorIterator(Enumeration<E> e) {
|
||||
this.e = e;
|
||||
}
|
||||
|
||||
public boolean hasNext() {
|
||||
return e.hasMoreElements();
|
||||
}
|
||||
|
||||
public E next() {
|
||||
return e.nextElement();
|
||||
}
|
||||
|
||||
public void remove() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,211 @@
|
||||
/*
|
||||
* $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 java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
import java.net.MalformedURLException;
|
||||
import java.util.*;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
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;
|
||||
|
||||
/**
|
||||
* Helper class that find resources and loads classes from the list of bundles
|
||||
*/
|
||||
public class DefaultBundleAccessor implements BundleAccessor {
|
||||
|
||||
private static DefaultBundleAccessor self;
|
||||
private static final Logger LOG = LoggerFactory.getLogger(DefaultBundleAccessor.class);
|
||||
|
||||
private BundleContext bundleContext;
|
||||
private Map<String, String> packageToBundle = new HashMap<String, String>();
|
||||
private Map<Bundle, Set<String>> packagesByBundle = new HashMap<Bundle, Set<String>>();
|
||||
private OsgiHost osgiHost;
|
||||
|
||||
public DefaultBundleAccessor() {
|
||||
self = this;
|
||||
}
|
||||
|
||||
public static DefaultBundleAccessor getInstance() {
|
||||
return self;
|
||||
}
|
||||
|
||||
public Object getService(ServiceReference ref) {
|
||||
return bundleContext != null ? bundleContext.getService(ref) : null;
|
||||
}
|
||||
|
||||
public ServiceReference getServiceReference(String className) {
|
||||
return bundleContext != null ? bundleContext.getServiceReference(className) : null;
|
||||
}
|
||||
|
||||
public ServiceReference[] getAllServiceReferences(String className) {
|
||||
if (bundleContext != null) {
|
||||
try {
|
||||
return bundleContext.getServiceReferences(className, null);
|
||||
} catch (InvalidSyntaxException e) {
|
||||
//cannot happen we are passing null as the param
|
||||
if (LOG.isErrorEnabled())
|
||||
LOG.error("Invalid syntax for service lookup", e);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public ServiceReference[] getServiceReferences(String className, String params) throws InvalidSyntaxException {
|
||||
return bundleContext != null ? bundleContext.getServiceReferences(className, params) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add as Bundle -> Package mapping
|
||||
* @param bundle the bundle where the package was loaded from
|
||||
* @param packageName the anme of the loaded package
|
||||
*/
|
||||
public void addPackageFromBundle(Bundle bundle, String packageName) {
|
||||
this.packageToBundle.put(packageName, bundle.getSymbolicName());
|
||||
Set<String> pkgs = packagesByBundle.get(bundle);
|
||||
if (pkgs == null) {
|
||||
pkgs = new HashSet<String>();
|
||||
packagesByBundle.put(bundle, pkgs);
|
||||
}
|
||||
pkgs.add(packageName);
|
||||
}
|
||||
|
||||
public Class<?> loadClass(String className) throws ClassNotFoundException {
|
||||
Bundle bundle = getCurrentBundle();
|
||||
if (bundle != null) {
|
||||
Class cls = bundle.loadClass(className);
|
||||
if (LOG.isTraceEnabled())
|
||||
LOG.trace("Located class [#0] in bundle [#1]", className, bundle.getSymbolicName());
|
||||
return cls;
|
||||
}
|
||||
|
||||
throw new ClassNotFoundException("Unable to find class " + className);
|
||||
}
|
||||
|
||||
private Bundle getCurrentBundle() {
|
||||
ActionContext ctx = ActionContext.getContext();
|
||||
String bundleName = (String) ctx.get(CURRENT_BUNDLE_NAME);
|
||||
if (bundleName == null) {
|
||||
ActionInvocation inv = ctx.getActionInvocation();
|
||||
ActionProxy proxy = inv.getProxy();
|
||||
ActionConfig actionConfig = proxy.getConfig();
|
||||
bundleName = packageToBundle.get(actionConfig.getPackageName());
|
||||
}
|
||||
if (bundleName != null) {
|
||||
return osgiHost.getActiveBundles().get(bundleName);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public List<URL> loadResources(String name) throws IOException {
|
||||
return loadResources(name, false);
|
||||
}
|
||||
|
||||
public List<URL> loadResources(String name, boolean translate) throws IOException {
|
||||
Bundle bundle = getCurrentBundle();
|
||||
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());
|
||||
}
|
||||
return resources;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
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()) {
|
||||
return (URL) e.nextElement();
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public InputStream loadResourceFromAllBundlesAsStream(String name) throws IOException {
|
||||
URL url = loadResourceFromAllBundles(name);
|
||||
if (url != null) {
|
||||
return url.openStream();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public URL loadResource(String name) {
|
||||
return loadResource(name, false);
|
||||
}
|
||||
|
||||
public URL loadResource(String name, boolean translate) {
|
||||
Bundle bundle = getCurrentBundle();
|
||||
if (bundle != null) {
|
||||
URL url = bundle.getResource(name);
|
||||
try {
|
||||
return translate ? OsgiUtil.translateBundleURLToJarURL(url, getCurrentBundle()) : url;
|
||||
} catch (Exception e) {
|
||||
if (LOG.isErrorEnabled()) {
|
||||
LOG.error("Unable to translate bundle URL to jar URL", e);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public Set<String> getPackagesByBundle(Bundle bundle) {
|
||||
return packagesByBundle.get(bundle);
|
||||
}
|
||||
|
||||
public InputStream loadResourceAsStream(String name) throws IOException {
|
||||
URL url = loadResource(name);
|
||||
if (url != null) {
|
||||
return url.openStream();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void setBundleContext(BundleContext bundleContext) {
|
||||
this.bundleContext = bundleContext;
|
||||
}
|
||||
|
||||
public void setOsgiHost(OsgiHost osgiHost) {
|
||||
this.osgiHost = osgiHost;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* $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 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;
|
||||
|
||||
public class DelegatingObjectFactory extends ObjectFactory implements ObjectFactoryDestroyable {
|
||||
private ObjectFactory delegateObjectFactory;
|
||||
private BundleAccessor bundleResourceLoader;
|
||||
private OsgiConfigurationProvider osgiConfigurationProvider;
|
||||
|
||||
@Inject
|
||||
public void setDelegateObjectFactory(@Inject Container container,
|
||||
@Inject("struts.objectFactory.delegate") String delegate) {
|
||||
if (delegate == null) {
|
||||
delegate = "struts";
|
||||
}
|
||||
delegateObjectFactory = container.getInstance(ObjectFactory.class, delegate);
|
||||
}
|
||||
|
||||
@Inject
|
||||
public void setBundleResourceLoader(BundleAccessor rl) {
|
||||
this.bundleResourceLoader = rl;
|
||||
}
|
||||
|
||||
|
||||
public boolean isNoArgConstructorRequired() {
|
||||
return delegateObjectFactory.isNoArgConstructorRequired();
|
||||
}
|
||||
|
||||
public Object buildBean(Class clazz, Map extraContext) throws Exception {
|
||||
return delegateObjectFactory.buildBean(clazz, extraContext);
|
||||
}
|
||||
|
||||
public Object buildBean(String className, Map<String, Object> extraContext, boolean injectInternal) throws Exception {
|
||||
try {
|
||||
return delegateObjectFactory.buildBean(className, extraContext, injectInternal);
|
||||
} catch (Exception e) {
|
||||
Object object = bundleResourceLoader.loadClass(className).newInstance();
|
||||
if (injectInternal)
|
||||
injectInternalBeans(object);
|
||||
return object;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class getClassInstance(String className) throws ClassNotFoundException {
|
||||
try {
|
||||
return delegateObjectFactory.getClassInstance(className);
|
||||
}
|
||||
catch (Exception e) {
|
||||
return bundleResourceLoader.loadClass(className);
|
||||
}
|
||||
}
|
||||
|
||||
public void destroy() {
|
||||
if (osgiConfigurationProvider != null) {
|
||||
osgiConfigurationProvider.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
@Inject("osgi")
|
||||
public void setOsgiConfigurationProvider(PackageProvider osgiConfigurationProvider) {
|
||||
this.osgiConfigurationProvider = (OsgiConfigurationProvider) osgiConfigurationProvider;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,376 @@
|
||||
/*
|
||||
* $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.config.ConfigurationException;
|
||||
import com.opensymphony.xwork2.inject.Inject;
|
||||
import com.opensymphony.xwork2.util.URLUtil;
|
||||
import com.opensymphony.xwork2.util.finder.ResourceFinder;
|
||||
import com.opensymphony.xwork2.util.logging.Logger;
|
||||
import com.opensymphony.xwork2.util.logging.LoggerFactory;
|
||||
import com.opensymphony.xwork2.ActionContext;
|
||||
import org.apache.commons.lang.xwork.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.StrutsStatics;
|
||||
import org.osgi.framework.Bundle;
|
||||
import org.osgi.framework.BundleActivator;
|
||||
import org.osgi.framework.Constants;
|
||||
import org.osgi.framework.BundleContext;
|
||||
import org.osgi.framework.BundleListener;
|
||||
import org.osgi.framework.BundleEvent;
|
||||
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 int 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));
|
||||
|
||||
//add third party bundles in level 2
|
||||
List<String> bundleJarsLevel2 = new ArrayList<String>();
|
||||
bundleJarsLevel2.addAll(getBundlesInDir("bundles/other"));
|
||||
|
||||
//start app bundles in level 3
|
||||
List<String> bundleJarsLevel3 = new ArrayList<String>();
|
||||
bundleJarsLevel3.addAll(getBundlesInDir("bundles"));
|
||||
|
||||
|
||||
configProps.put(AutoActivator.AUTO_START_PROP + ".1", StringUtils.join(bundleJarsLevel1, " "));
|
||||
configProps.put(AutoActivator.AUTO_START_PROP + ".2", StringUtils.join(bundleJarsLevel2, " "));
|
||||
configProps.put(AutoActivator.AUTO_START_PROP + ".3", StringUtils.join(bundleJarsLevel3, " "));
|
||||
|
||||
|
||||
return bundleJarsLevel1.size() + bundleJarsLevel2.size() + bundleJarsLevel3.size();
|
||||
}
|
||||
|
||||
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 bundlerDir = new File(url.toURI());
|
||||
File[] bundles = bundlerDir.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 {
|
||||
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();
|
||||
String 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 {
|
||||
JarFile jarFile = new JarFile(new File(URLUtil.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();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,245 @@
|
||||
/*
|
||||
* $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.ActionContext;
|
||||
import com.opensymphony.xwork2.ObjectFactory;
|
||||
import com.opensymphony.xwork2.config.Configuration;
|
||||
import com.opensymphony.xwork2.config.ConfigurationException;
|
||||
import com.opensymphony.xwork2.config.PackageProvider;
|
||||
import com.opensymphony.xwork2.config.entities.PackageConfig;
|
||||
import com.opensymphony.xwork2.inject.Container;
|
||||
import com.opensymphony.xwork2.inject.Inject;
|
||||
import com.opensymphony.xwork2.util.finder.ClassLoaderInterface;
|
||||
import com.opensymphony.xwork2.util.logging.Logger;
|
||||
import com.opensymphony.xwork2.util.logging.LoggerFactory;
|
||||
import org.apache.struts2.osgi.loaders.VelocityBundleResourceLoader;
|
||||
import org.apache.struts2.views.velocity.VelocityManager;
|
||||
import org.apache.velocity.app.Velocity;
|
||||
import org.apache.commons.lang.xwork.StringUtils;
|
||||
import org.osgi.framework.Bundle;
|
||||
import org.osgi.framework.BundleContext;
|
||||
import org.osgi.framework.BundleListener;
|
||||
import org.osgi.framework.BundleEvent;
|
||||
|
||||
import javax.servlet.ServletContext;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
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 OsgiHost osgiHost;
|
||||
private BundleContext bundleContext;
|
||||
private BundleAccessor bundleAccessor;
|
||||
private boolean bundlesChanged = false;
|
||||
private ServletContext servletContext;
|
||||
|
||||
public void init(Configuration configuration) throws ConfigurationException {
|
||||
osgiHost = (OsgiHost) servletContext.getAttribute(StrutsOsgiListener.OSGI_HOST);
|
||||
bundleContext = osgiHost.getBundleContext();
|
||||
bundleAccessor.setBundleContext(bundleContext);
|
||||
bundleAccessor.setOsgiHost(osgiHost);
|
||||
this.configuration = configuration;
|
||||
}
|
||||
|
||||
public synchronized void loadPackages() throws ConfigurationException {
|
||||
if (LOG.isTraceEnabled())
|
||||
LOG.trace("Loading packages from XML and Convention on startup");
|
||||
|
||||
//init action contect
|
||||
ActionContext ctx = ActionContext.getContext();
|
||||
if (ctx == null) {
|
||||
ctx = new ActionContext(new HashMap());
|
||||
ActionContext.setContext(ctx);
|
||||
}
|
||||
|
||||
Set<String> bundleNames = new HashSet<String>();
|
||||
|
||||
//iterate over the bundles and load packages from them
|
||||
for (Bundle bundle : osgiHost.getBundles().values()) {
|
||||
String bundleName = bundle.getSymbolicName();
|
||||
if (shouldProcessBundle(bundle) && !bundleNames.contains(bundleName)) {
|
||||
bundleNames.add(bundleName);
|
||||
//load XML and COnvention config
|
||||
loadConfigFromBundle(bundle);
|
||||
}
|
||||
}
|
||||
|
||||
bundlesChanged = false;
|
||||
bundleContext.addBundleListener(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads XML config as well as Convention config from a bundle
|
||||
* Limitation: Constants and Beans are ignored on XML config
|
||||
*/
|
||||
protected void loadConfigFromBundle(Bundle bundle) {
|
||||
String bundleName = bundle.getSymbolicName();
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Loading packages from bundle [#0]", bundleName);
|
||||
|
||||
//init action context
|
||||
ActionContext ctx = ActionContext.getContext();
|
||||
if (ctx == null) {
|
||||
ctx = new ActionContext(new HashMap());
|
||||
ActionContext.setContext(ctx);
|
||||
}
|
||||
|
||||
try {
|
||||
//the Convention plugin will use BundleClassLoaderInterface from the ActionContext to find resources
|
||||
//and load classes
|
||||
ctx.put(ClassLoaderInterface.CLASS_LOADER_INTERFACE, new BundleClassLoaderInterface());
|
||||
ctx.put(BundleAccessor.CURRENT_BUNDLE_NAME, bundleName);
|
||||
|
||||
if (LOG.isTraceEnabled())
|
||||
LOG.trace("Loading XML config from bundle [#0]", bundleName);
|
||||
|
||||
//XML config
|
||||
PackageLoader loader = new BundlePackageLoader();
|
||||
for (PackageConfig pkg : loader.loadPackages(bundle, bundleContext, objectFactory, 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());
|
||||
|
||||
PackageProvider conventionPackageProvider = configuration.getContainer().getInstance(PackageProvider.class, "convention.packageProvider");
|
||||
if (conventionPackageProvider != null) {
|
||||
if (LOG.isTraceEnabled())
|
||||
LOG.trace("Loading Convention config from bundle [#0]", bundleName);
|
||||
conventionPackageProvider.loadPackages();
|
||||
}
|
||||
|
||||
Set<String> packagesAfterLoading = new HashSet(configuration.getPackageConfigNames());
|
||||
packagesAfterLoading.removeAll(packagesBeforeLoading);
|
||||
if (!packagesAfterLoading.isEmpty()) {
|
||||
//add the new packages to the map of bundle -> package
|
||||
for (String packageName : packagesAfterLoading)
|
||||
bundleAccessor.addPackageFromBundle(bundle, packageName);
|
||||
}
|
||||
|
||||
if (this.configuration.getRuntimeConfiguration() != null) {
|
||||
//if there is a runtime config, it meas that this method was called froma bundle start event
|
||||
//instead of the initial load, in that case, reload the config
|
||||
this.configuration.rebuildRuntimeConfiguration();
|
||||
}
|
||||
} finally {
|
||||
ctx.put(BundleAccessor.CURRENT_BUNDLE_NAME, null);
|
||||
ctx.put(ClassLoaderInterface.CLASS_LOADER_INTERFACE, null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks for "Struts2-Enabled" header in the bundle
|
||||
*/
|
||||
protected boolean shouldProcessBundle(Bundle bundle) {
|
||||
String strutsEnabled = (String) bundle.getHeaders().get(OsgiHost.OSGI_HEADER_STRUTS_ENABLED);
|
||||
|
||||
return "true".equalsIgnoreCase(strutsEnabled);
|
||||
}
|
||||
|
||||
public synchronized boolean needsReload() {
|
||||
return bundlesChanged;
|
||||
}
|
||||
|
||||
@Inject
|
||||
public void setObjectFactory(ObjectFactory factory) {
|
||||
this.objectFactory = factory;
|
||||
}
|
||||
|
||||
@Inject
|
||||
public void setBundleAccessor(BundleAccessor acc) {
|
||||
this.bundleAccessor = acc;
|
||||
}
|
||||
|
||||
@Inject
|
||||
public void setVelocityManager(VelocityManager vm) {
|
||||
Properties props = new Properties();
|
||||
props.setProperty("osgi.resource.loader.description", "OSGI bundle loader");
|
||||
props.setProperty("osgi.resource.loader.class", VelocityBundleResourceLoader.class.getName());
|
||||
props.setProperty(Velocity.RESOURCE_LOADER, "strutsfile,strutsclass,osgi");
|
||||
vm.setVelocityProperties(props);
|
||||
}
|
||||
|
||||
@Inject
|
||||
public void setServletContext(ServletContext servletContext) {
|
||||
this.servletContext = servletContext;
|
||||
}
|
||||
|
||||
public void destroy() {
|
||||
try {
|
||||
osgiHost.destroy();
|
||||
} catch (Exception e) {
|
||||
if (LOG.isErrorEnabled())
|
||||
LOG.error("Failed to stop OSGi container", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Listens to bundle event to load/unload config
|
||||
*/
|
||||
public void bundleChanged(BundleEvent bundleEvent) {
|
||||
Bundle bundle = bundleEvent.getBundle();
|
||||
String bundleName = bundle.getSymbolicName();
|
||||
if (bundleName != null && shouldProcessBundle(bundle)) {
|
||||
switch (bundleEvent.getType()) {
|
||||
case BundleEvent.STARTED:
|
||||
if (LOG.isTraceEnabled())
|
||||
LOG.trace("The bundlde [#0] has been activated and will be scanned for struts configuration", bundleName);
|
||||
loadConfigFromBundle(bundle);
|
||||
break;
|
||||
case BundleEvent.STOPPED:
|
||||
onBundleStopped(bundle);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called when a bundle is stopped, so the config that is related to it is removed
|
||||
*
|
||||
* @param bundle the bundle that stopped
|
||||
*/
|
||||
protected void onBundleStopped(Bundle bundle) {
|
||||
Set<String> packages = bundleAccessor.getPackagesByBundle(bundle);
|
||||
if (!packages.isEmpty()) {
|
||||
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)
|
||||
configuration.removePackageConfig(packageName);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* $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.osgi.framework.BundleActivator;
|
||||
import org.osgi.framework.Bundle;
|
||||
import org.osgi.framework.BundleContext;
|
||||
|
||||
import javax.servlet.ServletContext;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Implementations of this class start an OSGi container. They must also add the BundleContext to
|
||||
* the ServletContext under the key OsgiHost.OSGI_BUNDLE_CONTEXT;
|
||||
*/
|
||||
public interface OsgiHost {
|
||||
String OSGI_BUNDLE_CONTEXT = "__struts_osgi_bundle_context";
|
||||
String OSGI_HEADER_STRUTS_ENABLED = "Struts2-Enabled";
|
||||
|
||||
void destroy() throws Exception;
|
||||
void init(ServletContext servletContext);
|
||||
Map<String, Bundle> getBundles();
|
||||
Map<String, Bundle> getActiveBundles();
|
||||
BundleContext getBundleContext();
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* $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 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;
|
||||
|
||||
public class OsgiUtil {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(OsgiUtil.class);
|
||||
|
||||
/**
|
||||
* A bundle is a jar, and a bunble URL will be useless to clients, this method translates
|
||||
* a URL to a resource inside a bundle from "bundle:something/path" to "jar:file:bundlelocation!/path"
|
||||
*/
|
||||
public static URL translateBundleURLToJarURL(URL bundleUrl, Bundle bundle) throws MalformedURLException {
|
||||
if (bundleUrl != null && "bundle".equalsIgnoreCase(bundleUrl.getProtocol())) {
|
||||
StringBuilder sb = new StringBuilder("jar:");
|
||||
sb.append(bundle.getLocation());
|
||||
sb.append("!");
|
||||
sb.append(bundleUrl.getFile());
|
||||
return new URL(sb.toString());
|
||||
}
|
||||
|
||||
return bundleUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls getBean() on the passed object using refelection. Used on Spring context
|
||||
* because they are loaded from bundles (in anothe class loader)
|
||||
*/
|
||||
public static Object getBean(Object beanFactory, String beanId) {
|
||||
try {
|
||||
Method getBeanMethod = beanFactory.getClass().getMethod("getBean", String.class);
|
||||
return getBeanMethod.invoke(beanFactory, beanId);
|
||||
} catch (Exception ex) {
|
||||
if (LOG.isErrorEnabled())
|
||||
LOG.error("Unable to call getBean() on object of type [#0], with bean id [#1]", ex, beanFactory.getClass().getName(), beanId);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls containsBean on the passed object using refelection. Used on Spring context
|
||||
* because they are loaded from bundles (in anothe class loader)
|
||||
*/
|
||||
public static boolean containsBean(Object beanFactory, String beanId) {
|
||||
try {
|
||||
Method getBeanMethod = beanFactory.getClass().getMethod("containsBean", String.class);
|
||||
return (Boolean) getBeanMethod.invoke(beanFactory, beanId);
|
||||
} catch (Exception ex) {
|
||||
if (LOG.isErrorEnabled())
|
||||
LOG.error("Unable to call containsBean() on object of type [#0], with bean id [#1]", ex, beanFactory.getClass().getName(), beanId);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* $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 java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.osgi.framework.Bundle;
|
||||
import org.osgi.framework.BundleContext;
|
||||
|
||||
import com.opensymphony.xwork2.ObjectFactory;
|
||||
import com.opensymphony.xwork2.config.ConfigurationException;
|
||||
import com.opensymphony.xwork2.config.PackageProvider;
|
||||
import com.opensymphony.xwork2.config.entities.PackageConfig;
|
||||
|
||||
/**
|
||||
* Implementations of this interface can load packages from a Bundle
|
||||
*/
|
||||
public interface PackageLoader {
|
||||
List<PackageConfig> loadPackages(Bundle bundle, BundleContext bundleContext, ObjectFactory objectFactory, Map<String, PackageConfig> map) throws ConfigurationException;
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* $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.ObjectFactory;
|
||||
import com.opensymphony.xwork2.util.ClassLoaderUtil;
|
||||
import com.opensymphony.xwork2.inject.Inject;
|
||||
import org.osgi.framework.ServiceReference;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* This Object factory uses the ActionContext(s) published by Spring OSGi
|
||||
* to lookup beans
|
||||
*/
|
||||
public class SpringOsgiObjectFactory extends ObjectFactory {
|
||||
private final static String SPRING_SERVICE_NAME = "org.springframework.context.ApplicationContext";
|
||||
|
||||
private BundleAccessor bundleAccessor;
|
||||
|
||||
public Object buildBean(String className, Map<String, Object> extraContext, boolean injectInternal) throws Exception {
|
||||
if (containsBean(className))
|
||||
return getBean(className);
|
||||
else {
|
||||
Class clazz = ClassLoaderUtil.loadClass(className, SpringOsgiObjectFactory.class);
|
||||
Object object = clazz.newInstance();
|
||||
if (injectInternal)
|
||||
injectInternalBeans(object);
|
||||
return object;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public Object buildBean(Class clazz, Map<String, Object> extraContext) throws Exception {
|
||||
return clazz.newInstance();
|
||||
}
|
||||
|
||||
public Class getClassInstance(String className) throws ClassNotFoundException {
|
||||
return containsBean(className) ? getBean(className).getClass() : ClassLoaderUtil.loadClass(className, SpringOsgiObjectFactory.class);
|
||||
}
|
||||
|
||||
protected Object getBean(String beanName) {
|
||||
ServiceReference[] refs = bundleAccessor.getAllServiceReferences(SPRING_SERVICE_NAME);
|
||||
if (refs != null) {
|
||||
for (ServiceReference ref : refs) {
|
||||
Object context = bundleAccessor.getService(ref);
|
||||
if (OsgiUtil.containsBean(context, beanName))
|
||||
return OsgiUtil.getBean(context, beanName);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
protected boolean containsBean(String beanName) {
|
||||
ServiceReference[] refs = bundleAccessor.getAllServiceReferences(SPRING_SERVICE_NAME);
|
||||
if (refs != null) {
|
||||
for (ServiceReference ref : refs) {
|
||||
Object context = bundleAccessor.getService(ref);
|
||||
if (OsgiUtil.containsBean(context, beanName))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Inject
|
||||
public void setBundleAccessor(BundleAccessor bundleAccessor) {
|
||||
this.bundleAccessor = bundleAccessor;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package org.apache.struts2.osgi;
|
||||
|
||||
import org.apache.struts2.StrutsException;
|
||||
|
||||
import javax.servlet.ServletContextListener;
|
||||
import javax.servlet.ServletContextEvent;
|
||||
import javax.servlet.ServletContext;
|
||||
|
||||
/**
|
||||
* ServletContextListener that starts Apache Felix
|
||||
*/
|
||||
public class StrutsOsgiListener implements ServletContextListener {
|
||||
public static final String OSGI_HOST = "__struts_osgi_host";
|
||||
private FelixOsgiHost osgiHost;
|
||||
|
||||
public void contextInitialized(ServletContextEvent sce) {
|
||||
ServletContext servletContext = sce.getServletContext();
|
||||
osgiHost = new FelixOsgiHost();
|
||||
servletContext.setAttribute(OSGI_HOST, osgiHost);
|
||||
try {
|
||||
osgiHost.init(servletContext);
|
||||
} catch (Exception e) {
|
||||
throw new StrutsException("Apache Felix failed to start", e);
|
||||
}
|
||||
}
|
||||
|
||||
public void contextDestroyed(ServletContextEvent sce) {
|
||||
try {
|
||||
osgiHost.destroy();
|
||||
} catch (Exception e) {
|
||||
throw new StrutsException("Apache Felix failed to stop", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* $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.interceptor;
|
||||
|
||||
import org.osgi.framework.BundleContext;
|
||||
|
||||
/**
|
||||
* Actions implementing this interface will receive an instance of the BundleContext,
|
||||
* the OsgiInterceptor must be applied to the action.
|
||||
*/
|
||||
public interface BundleContextAware {
|
||||
void setBundleContext(BundleContext bundleContext);
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* $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.interceptor;
|
||||
|
||||
import com.opensymphony.xwork2.interceptor.AbstractInterceptor;
|
||||
import com.opensymphony.xwork2.ActionInvocation;
|
||||
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.osgi.framework.BundleContext;
|
||||
import org.osgi.framework.ServiceReference;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.util.List;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
public String intercept(ActionInvocation invocation) throws Exception {
|
||||
if (bundleContext != null) {
|
||||
Object action = invocation.getAction();
|
||||
|
||||
//inject BundleContext
|
||||
if (action instanceof BundleContextAware)
|
||||
((BundleContextAware)action).setBundleContext(bundleContext);
|
||||
|
||||
//inject service implementations
|
||||
if (action instanceof ServiceAware) {
|
||||
Type[] types = action.getClass().getGenericInterfaces();
|
||||
if (types != null) {
|
||||
for (Type type : types) {
|
||||
if (type instanceof ParameterizedType) {
|
||||
ParameterizedType parameterizedType = (ParameterizedType) type;
|
||||
if (parameterizedType.getRawType() instanceof Class) {
|
||||
Class clazz = (Class) parameterizedType.getRawType();
|
||||
if (ServiceAware.class.equals(clazz)) {
|
||||
Class serviceClass = (Class) parameterizedType.getActualTypeArguments()[0];
|
||||
ServiceReference[] refs = bundleContext.getAllServiceReferences(serviceClass.getName(), null);
|
||||
//get the services
|
||||
if (refs != null) {
|
||||
List services = new ArrayList(refs.length);
|
||||
for (ServiceReference ref : refs) {
|
||||
Object service = bundleContext.getService(ref);
|
||||
//wow, that's a lot of nested ifs
|
||||
if (service != null)
|
||||
services.add(service);
|
||||
}
|
||||
|
||||
if (!services.isEmpty())
|
||||
((ServiceAware)action).setServices(services);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (LOG.isWarnEnabled()){
|
||||
LOG.warn("The OSGi interceptor was not able to find the BundleContext in the ServletContext");
|
||||
}
|
||||
|
||||
return invocation.invoke();
|
||||
}
|
||||
|
||||
@Inject
|
||||
public void setServletContext(ServletContext servletContext) {
|
||||
this.bundleContext = (BundleContext) servletContext.getAttribute(OsgiHost.OSGI_BUNDLE_CONTEXT);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* $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.interceptor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Classes implementing this interface, will be injected a list of services
|
||||
* registered with the type of the parameterized type
|
||||
* @param <T> The type of the service
|
||||
*/
|
||||
public interface ServiceAware<T> {
|
||||
void setServices(List<T> services);
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* $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.loaders;
|
||||
|
||||
import java.net.URL;
|
||||
|
||||
import org.apache.struts2.osgi.DefaultBundleAccessor;
|
||||
|
||||
import freemarker.cache.URLTemplateLoader;
|
||||
|
||||
/**
|
||||
* Finds FreeMarker templates in bundles
|
||||
*/
|
||||
public class FreeMarkerBundleResourceLoader extends URLTemplateLoader {
|
||||
|
||||
@Override
|
||||
protected URL getURL(String name) {
|
||||
return DefaultBundleAccessor.getInstance().loadResource(name);
|
||||
}
|
||||
|
||||
}
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* $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.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;
|
||||
|
||||
/**
|
||||
* Loads static resources from bundles
|
||||
*
|
||||
*/
|
||||
public class StaticContentBundleResourceLoader extends DefaultStaticContentLoader {
|
||||
private BundleAccessor bundleAccessor;
|
||||
|
||||
protected URL findResource(String path) throws IOException {
|
||||
return bundleAccessor.loadResourceFromAllBundles(path);
|
||||
}
|
||||
|
||||
@Inject
|
||||
public void setBundleAccessor(BundleAccessor bundleAccessor) {
|
||||
this.bundleAccessor = bundleAccessor;
|
||||
}
|
||||
}
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* $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.loaders;
|
||||
|
||||
import java.io.InputStream;
|
||||
|
||||
import org.apache.struts2.osgi.DefaultBundleAccessor;
|
||||
import org.apache.velocity.exception.ResourceNotFoundException;
|
||||
import org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader;
|
||||
|
||||
/**
|
||||
* Finds Velocity templates in bundles
|
||||
*/
|
||||
public class VelocityBundleResourceLoader extends ClasspathResourceLoader {
|
||||
|
||||
public synchronized InputStream getResourceStream(String name)
|
||||
throws ResourceNotFoundException {
|
||||
if ((name == null) || (name.length() == 0)) {
|
||||
throw new ResourceNotFoundException("No template name provided");
|
||||
}
|
||||
|
||||
if (name.startsWith("/")) {
|
||||
name = name.substring(1);
|
||||
}
|
||||
|
||||
try {
|
||||
return DefaultBundleAccessor.getInstance().loadResourceAsStream(name);
|
||||
} catch (Exception e) {
|
||||
throw new ResourceNotFoundException(e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
|
||||
<beans>
|
||||
<bean id="parent-context-bean" class="org.springframework.context.support.ClassPathXmlApplicationContext">
|
||||
<constructor-arg>
|
||||
<list>
|
||||
<value>classpath*:/spring/*.xml</value>
|
||||
</list>
|
||||
</constructor-arg>
|
||||
</bean>
|
||||
</beans>
|
||||
@@ -0,0 +1,25 @@
|
||||
# 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.
|
||||
|
||||
scanning.package.includes = com.opensymphony.xwork2, \
|
||||
org.apache.commons.lang.xwork, \
|
||||
org.apache.struts2, \
|
||||
ognl, \
|
||||
freemarker, \
|
||||
org.apache.velocity, \
|
||||
javax.servlet
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
|
||||
<!DOCTYPE struts PUBLIC
|
||||
"-//Apache Software Foundation//DTD Struts Configuration 2.1.7//EN"
|
||||
"http://struts.apache.org/dtds/struts-2.1.7.dtd">
|
||||
|
||||
<struts order="10">
|
||||
<constant name="struts.objectFactory" value="osgi" />
|
||||
<constant name="struts.objectFactory.delegate" value="struts" />
|
||||
<constant name="struts.freemarker.manager.classname" value="org.apache.struts2.osgi.BundleFreemarkerManager" />
|
||||
<constant name="struts.staticContentLoader" value="org.apache.struts2.osgi.loaders.StaticContentBundleResourceLoader" />
|
||||
|
||||
<constant name="struts.convention.action.includeJars" value="jar:file:.*?/bundles/.*?\.jar(!/)?" />
|
||||
|
||||
<bean type="org.apache.struts2.osgi.BundleAccessor" class="org.apache.struts2.osgi.DefaultBundleAccessor" />
|
||||
<bean type="org.apache.struts2.osgi.PackageLoader" class="org.apache.struts2.osgi.BundlePackageLoader" />
|
||||
|
||||
<bean name="osgi" type="com.opensymphony.xwork2.ObjectFactory" class="org.apache.struts2.osgi.DelegatingObjectFactory" />
|
||||
<bean name="springOsgi" type="com.opensymphony.xwork2.ObjectFactory" class="org.apache.struts2.osgi.SpringOsgiObjectFactory" />
|
||||
<bean name="osgi" type="com.opensymphony.xwork2.config.PackageProvider" class="org.apache.struts2.osgi.OsgiConfigurationProvider" />
|
||||
<bean name="osgi" type="com.opensymphony.xwork2.util.finder.ClassLoaderInterface" class="org.apache.struts2.osgi.BundleClassLoaderInterface" />
|
||||
|
||||
<package name="osgi-default" extends="struts-default" abstract="yes">
|
||||
<interceptors>
|
||||
<interceptor name="osgi" class="org.apache.struts2.osgi.interceptor.OsgiInterceptor" />
|
||||
|
||||
<interceptor-stack name="osgiStack">
|
||||
<interceptor-ref name="defaultStack"/>
|
||||
<interceptor-ref name="osgi"/>
|
||||
</interceptor-stack>
|
||||
</interceptors>
|
||||
|
||||
<default-interceptor-ref name="osgiStack"/>
|
||||
</package>
|
||||
</struts>
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* $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 junit.framework.TestCase;
|
||||
|
||||
public class FelixOsgiHostTest extends TestCase {
|
||||
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"));
|
||||
}
|
||||
}
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
package org.apache.struts2.osgi.interceptor;
|
||||
|
||||
import org.easymock.EasyMock;
|
||||
import org.apache.struts2.osgi.OsgiHost;
|
||||
import org.osgi.framework.BundleContext;
|
||||
import org.osgi.framework.ServiceReference;
|
||||
|
||||
import javax.servlet.ServletContext;
|
||||
|
||||
import com.opensymphony.xwork2.ActionInvocation;
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class OsgiInterceptorTest extends TestCase {
|
||||
public void testBundleContextAware() throws Exception {
|
||||
ServletContext servletContext = EasyMock.createStrictMock(ServletContext.class);
|
||||
BundleContext bundleContext = EasyMock.createStrictMock(BundleContext.class);
|
||||
ActionInvocation actionInvocation = EasyMock.createStrictMock(ActionInvocation.class);
|
||||
BundleContextAware bundleContextAware = EasyMock.createStrictMock(BundleContextAware.class);
|
||||
|
||||
EasyMock.expect(servletContext.getAttribute(OsgiHost.OSGI_BUNDLE_CONTEXT)).andReturn(bundleContext);
|
||||
EasyMock.expect(actionInvocation.getAction()).andReturn(bundleContextAware);
|
||||
bundleContextAware.setBundleContext(bundleContext);
|
||||
EasyMock.expect(actionInvocation.invoke()).andReturn("");
|
||||
|
||||
EasyMock.replay(bundleContextAware);
|
||||
EasyMock.replay(servletContext);
|
||||
EasyMock.replay(actionInvocation);
|
||||
|
||||
OsgiInterceptor osgiInterceptor = new OsgiInterceptor();
|
||||
osgiInterceptor.setServletContext(servletContext);
|
||||
osgiInterceptor.intercept(actionInvocation);
|
||||
|
||||
EasyMock.verify(bundleContextAware);
|
||||
}
|
||||
|
||||
public void testBundleContextAwareNegative() throws Exception {
|
||||
ServletContext servletContext = EasyMock.createStrictMock(ServletContext.class);
|
||||
ActionInvocation actionInvocation = EasyMock.createStrictMock(ActionInvocation.class);
|
||||
BundleContextAware bundleContextAware = EasyMock.createStrictMock(BundleContextAware.class);
|
||||
|
||||
EasyMock.expect(servletContext.getAttribute(OsgiHost.OSGI_BUNDLE_CONTEXT)).andReturn(null);
|
||||
EasyMock.expect(actionInvocation.invoke()).andReturn("");
|
||||
|
||||
EasyMock.replay(bundleContextAware);
|
||||
EasyMock.replay(servletContext);
|
||||
EasyMock.replay(actionInvocation);
|
||||
|
||||
OsgiInterceptor osgiInterceptor = new OsgiInterceptor();
|
||||
osgiInterceptor.setServletContext(servletContext);
|
||||
osgiInterceptor.intercept(actionInvocation);
|
||||
|
||||
EasyMock.verify(bundleContextAware);
|
||||
}
|
||||
|
||||
public void testServiceAware() throws Exception {
|
||||
ServletContext servletContext = EasyMock.createStrictMock(ServletContext.class);
|
||||
BundleContext bundleContext = EasyMock.createStrictMock(BundleContext.class);
|
||||
ActionInvocation actionInvocation = EasyMock.createStrictMock(ActionInvocation.class);
|
||||
SomeAction someAction = new SomeAction();
|
||||
|
||||
//service refs
|
||||
ServiceReference objectRef = EasyMock.createNiceMock(ServiceReference.class);
|
||||
Object someObject = new Object();
|
||||
|
||||
EasyMock.expect(servletContext.getAttribute(OsgiHost.OSGI_BUNDLE_CONTEXT)).andReturn(bundleContext);
|
||||
EasyMock.expect(actionInvocation.getAction()).andReturn(someAction);
|
||||
EasyMock.expect(actionInvocation.invoke()).andReturn("");
|
||||
EasyMock.expect(bundleContext.getAllServiceReferences(Object.class.getName(), null)).andReturn(new ServiceReference[] {objectRef});
|
||||
EasyMock.expect(bundleContext.getService(objectRef)).andReturn(someObject);
|
||||
|
||||
EasyMock.replay(bundleContext);
|
||||
EasyMock.replay(servletContext);
|
||||
EasyMock.replay(actionInvocation);
|
||||
|
||||
OsgiInterceptor osgiInterceptor = new OsgiInterceptor();
|
||||
osgiInterceptor.setServletContext(servletContext);
|
||||
osgiInterceptor.intercept(actionInvocation);
|
||||
|
||||
List<Object> objects = someAction.getServices();
|
||||
assertNotNull(objects);
|
||||
assertSame(someObject, objects.get(0));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* $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.interceptor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class SomeAction implements ServiceAware<Object> {
|
||||
private List<Object> services;
|
||||
|
||||
|
||||
public List<Object> getServices() {
|
||||
return services;
|
||||
}
|
||||
|
||||
public void setServices(List<Object> services) {
|
||||
this.services = services;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user