Author: musachy Date: Fri Apr 3 19:13:06 2009 New Revision: 761766 URL: http://svn.apache.org/viewvc?rev=761766&view=rev Log: Refactor plugin. Update to Felix 1.4
Added: struts/sandbox/trunk/struts2-osgi-plugin/plugin/src/main/java/org/apache/struts2/osgi/FelixOsgiHost.java struts/sandbox/trunk/struts2-osgi-plugin/plugin/src/main/java/org/apache/struts2/osgi/OsgiHost.java struts/sandbox/trunk/struts2-osgi-plugin/plugin/src/test/java/org/apache/struts2/osgi/FelixOsgiHostTest.java Removed: struts/sandbox/trunk/struts2-osgi-plugin/plugin/src/test/java/org/apache/struts2/osgi/OsgiConfigurationProviderTest.java Modified: struts/sandbox/trunk/struts2-osgi-plugin/plugin/ (props changed) struts/sandbox/trunk/struts2-osgi-plugin/plugin/pom.xml struts/sandbox/trunk/struts2-osgi-plugin/plugin/src/main/java/org/apache/struts2/osgi/BundlePackageLoader.java struts/sandbox/trunk/struts2-osgi-plugin/plugin/src/main/java/org/apache/struts2/osgi/DefaultBundleAccessor.java struts/sandbox/trunk/struts2-osgi-plugin/plugin/src/main/java/org/apache/struts2/osgi/OsgiConfigurationProvider.java struts/sandbox/trunk/struts2-osgi-plugin/plugin/src/main/java/org/apache/struts2/osgi/loaders/StaticContentBundleResourceLoader.java struts/sandbox/trunk/struts2-osgi-plugin/plugin/src/main/resources/struts-osgi.properties struts/sandbox/trunk/struts2-osgi-plugin/plugin/src/main/resources/struts-plugin.xml Propchange: struts/sandbox/trunk/struts2-osgi-plugin/plugin/ ------------------------------------------------------------------------------ --- svn:ignore (added) +++ svn:ignore Fri Apr 3 19:13:06 2009 @@ -0,0 +1 @@ +target Modified: struts/sandbox/trunk/struts2-osgi-plugin/plugin/pom.xml URL: http://svn.apache.org/viewvc/struts/sandbox/trunk/struts2-osgi-plugin/plugin/pom.xml?rev=761766&r1=761765&r2=761766&view=diff ============================================================================== --- struts/sandbox/trunk/struts2-osgi-plugin/plugin/pom.xml (original) +++ struts/sandbox/trunk/struts2-osgi-plugin/plugin/pom.xml Fri Apr 3 19:13:06 2009 @@ -5,31 +5,49 @@ <parent> <groupId>org.apache.struts</groupId> <artifactId>struts2-plugins</artifactId> - <version>2.1.2</version> + <version>2.0.9</version> </parent> <groupId>org.apache.struts</groupId> <artifactId>struts2-osgi-plugin</artifactId> <packaging>jar</packaging> - <version>1.0-SNAPSHOT</version> + <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> + <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.1.0-SNAPSHOT</version> + <version>1.4.1</version> </dependency> - <dependency> + <dependency> <groupId>org.apache.felix</groupId> <artifactId>org.apache.felix.fileinstall</artifactId> - <version>0.9.0-SNAPSHOT</version> + <version>0.9.0</version> <exclusions> <exclusion> @@ -42,31 +60,35 @@ <dependency> <groupId>org.apache.struts</groupId> <artifactId>struts2-core</artifactId> - <version>2.1.3-SNAPSHOT</version> + <version>2.1.7-SNAPSHOT</version> </dependency> <dependency> - <groupId>org.twdata.pkgscanner</groupId> - <artifactId>package-scanner</artifactId> - <version>0.5.1</version> + <groupId>org.apache.velocity</groupId> + <artifactId>velocity</artifactId> + <version>1.5</version> </dependency> <dependency> - <groupId>velocity-tools</groupId> + <groupId>org.apache.velocity</groupId> <artifactId>velocity-tools</artifactId> - <version>1.1</version> + <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>velocity</groupId> - <artifactId>velocity</artifactId> - <version>1.4</version> - </dependency> - <dependency> - <groupId>velocity</groupId> - <artifactId>velocity-dep</artifactId> - <version>1.4</version> + <groupId>commons-digester</groupId> + <artifactId>commons-digester</artifactId> + <version>2.0</version> </dependency> <dependency> @@ -75,13 +97,5 @@ <scope>test</scope> <version>3.8.1</version> </dependency> - </dependencies> - - <repositories> - <repository> - <id>pkgscanner-repository</id> - <name>pkgscanner Repository for Maven</name> - <url>http://pkgscanner.googlecode.com/svn/repo/</url> - </repository> - </repositories> + </dependencies> </project> Modified: struts/sandbox/trunk/struts2-osgi-plugin/plugin/src/main/java/org/apache/struts2/osgi/BundlePackageLoader.java URL: http://svn.apache.org/viewvc/struts/sandbox/trunk/struts2-osgi-plugin/plugin/src/main/java/org/apache/struts2/osgi/BundlePackageLoader.java?rev=761766&r1=761765&r2=761766&view=diff ============================================================================== --- struts/sandbox/trunk/struts2-osgi-plugin/plugin/src/main/java/org/apache/struts2/osgi/BundlePackageLoader.java (original) +++ struts/sandbox/trunk/struts2-osgi-plugin/plugin/src/main/java/org/apache/struts2/osgi/BundlePackageLoader.java Fri Apr 3 19:13:06 2009 @@ -53,7 +53,7 @@ private BundleContext bundleContext; public BundleConfigurationProvider(String filename, Bundle bundle, BundleContext bundleContext) { - super(filename, true); + super(filename, false); this.bundle = bundle; this.bundleContext = bundleContext; } @@ -62,8 +62,7 @@ @Override protected Iterator<URL> getConfigurationUrls(String fileName) throws IOException { Enumeration<URL> e = bundle.getResources("struts.xml"); - Iterator<URL> iter = new EnumeratorIterator<URL>(e); - return iter; + return e.hasMoreElements() ? new EnumeratorIterator<URL>(e) : null; } /* @@ -76,14 +75,14 @@ return bundle.loadClass(className) != null; } catch (Exception e) { if (LOG.isDebugEnabled()) - LOG.debug("Unable to find class #1 in bundle #2", className, bundle.getSymbolicName()); + LOG.debug("Unable to find class [#0] in bundle [#1]", className, bundle.getSymbolicName()); //try to find a bean with that id try { return SpringOSGiUtil.isValidBean(bundleContext, className); } catch (Exception e1) { if (LOG.isDebugEnabled()) - LOG.debug("Unable to find bean #1", className); + LOG.debug("Unable to find bean [#0]", className); } return false; Modified: struts/sandbox/trunk/struts2-osgi-plugin/plugin/src/main/java/org/apache/struts2/osgi/DefaultBundleAccessor.java URL: http://svn.apache.org/viewvc/struts/sandbox/trunk/struts2-osgi-plugin/plugin/src/main/java/org/apache/struts2/osgi/DefaultBundleAccessor.java?rev=761766&r1=761765&r2=761766&view=diff ============================================================================== --- struts/sandbox/trunk/struts2-osgi-plugin/plugin/src/main/java/org/apache/struts2/osgi/DefaultBundleAccessor.java (original) +++ struts/sandbox/trunk/struts2-osgi-plugin/plugin/src/main/java/org/apache/struts2/osgi/DefaultBundleAccessor.java Fri Apr 3 19:13:06 2009 @@ -67,7 +67,7 @@ Bundle bundle = getCurrentBundle(); if (bundle != null) { cls = bundle.loadClass(className); - LOG.debug("Located class #1 in bundle #2", className, bundle.getSymbolicName()); + LOG.debug("Located class [#0] in bundle [#1]", className, bundle.getSymbolicName()); } if (cls == null) { @@ -78,7 +78,7 @@ cls = bean.getClass(); } catch (Exception e) { if (LOG.isDebugEnabled()) - LOG.debug("Unable to find bean #1", className); + LOG.debug("Unable to find bean [#0]", className); } } Added: struts/sandbox/trunk/struts2-osgi-plugin/plugin/src/main/java/org/apache/struts2/osgi/FelixOsgiHost.java URL: http://svn.apache.org/viewvc/struts/sandbox/trunk/struts2-osgi-plugin/plugin/src/main/java/org/apache/struts2/osgi/FelixOsgiHost.java?rev=761766&view=auto ============================================================================== --- struts/sandbox/trunk/struts2-osgi-plugin/plugin/src/main/java/org/apache/struts2/osgi/FelixOsgiHost.java (added) +++ struts/sandbox/trunk/struts2-osgi-plugin/plugin/src/main/java/org/apache/struts2/osgi/FelixOsgiHost.java Fri Apr 3 19:13:06 2009 @@ -0,0 +1,333 @@ +/* + * $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.felix.framework.Felix; +import org.apache.felix.framework.util.FelixConstants; +import org.apache.felix.main.Main; +import org.apache.felix.main.AutoActivator; +import org.apache.felix.shell.ShellService; +import org.apache.commons.lang.xwork.StringUtils; +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleContext; +import org.osgi.framework.BundleException; +import org.osgi.framework.Constants; +import org.osgi.framework.BundleActivator; +import org.osgi.util.tracker.ServiceTracker; + +import java.util.Map; +import java.util.Collections; +import java.util.HashMap; +import java.util.Properties; +import java.util.Set; +import java.util.HashSet; +import java.util.List; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.regex.Pattern; +import java.util.regex.Matcher; +import java.util.jar.JarEntry; +import java.util.jar.JarInputStream; +import java.util.jar.JarFile; +import java.util.jar.Manifest; +import java.util.jar.Attributes; +import java.io.IOException; +import java.io.InputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FilenameFilter; +import java.net.URL; +import java.net.URLDecoder; +import java.net.MalformedURLException; +import java.net.URISyntaxException; +import java.security.CodeSource; +import java.security.ProtectionDomain; + +import com.opensymphony.xwork2.inject.Inject; +import com.opensymphony.xwork2.util.logging.Logger; +import com.opensymphony.xwork2.util.logging.LoggerFactory; +import com.opensymphony.xwork2.util.finder.ResourceFinder; +import com.opensymphony.xwork2.util.URLUtil; +import com.opensymphony.xwork2.config.ConfigurationException; + +/** + * Apache felix implementation of an OsgiHost + * See http://felix.apache.org/site/apache-felix-framework-launching-and-embedding.html + */ +public class FelixOsgiHost implements OsgiHost { + private static final Logger LOG = LoggerFactory.getLogger(FelixOsgiHost.class); + + private static final String FELIX_FILEINSTALL_POLL = "felix.fileinstall.poll"; + private static final String FELIX_FILEINSTALL_DIR = "felix.fileinstall.dir"; + private static final String FELIX_FILEINSTALL_DEBUG = "felix.fileinstall.debug"; + + private Felix felix; + private Map<String, Bundle> bundles = Collections.synchronizedMap(new HashMap<String, Bundle>()); + private List<? extends BundleActivator> extraBundleActivators; + private boolean cleanBundleCache; + private static Pattern versionPattern = Pattern.compile("([\\d])+[\\.-]"); + + 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 + int bundlePaths = addAutoStartBundles(configProps); + + // Bundle cache + configProps.setProperty(Constants.FRAMEWORK_STORAGE, System.getProperty("java.io.tmpdir") + ".felix-cache"); + + // File Install + /*String bundlesDir = Thread.currentThread().getContextClassLoader().getResource("bundles").getPath(); + configProps.put(OsgiConfigurationProvider.FELIX_FILEINSTALL_POLL, "5000"); + configProps.put(OsgiConfigurationProvider.FELIX_FILEINSTALL_DIR, bundlesDir); + configProps.put(OsgiConfigurationProvider.FELIX_FILEINSTALL_DEBUG, "1");*/ + + if (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, "4"); + configProps.put(FelixConstants.BUNDLE_CLASSPATH, "."); + + try { + List<BundleActivator> list = new ArrayList<BundleActivator>(); + if (extraBundleActivators != null) + list.addAll(extraBundleActivators); + 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 Felix (OSGi)", ex); + } + + // Wait for all bundles to load + while (bundles.size() < bundlePaths) { + try { + Thread.sleep(500); + } catch (InterruptedException e) { + LOG.error("An error occured while waiting for bundle activation", e); + } + } + } + + private int addAutoStartBundles(Properties configProps) { + List<String> bundleJars = new ArrayList<String>(); + try { + ResourceFinder finder = new ResourceFinder(); + URL url = finder.find("bundles"); + if ("file".equals(url.getProtocol())) { + File bundlerDir = new File(url.toURI()); + File[] bundles = bundlerDir.listFiles(new FilenameFilter() { + @Override + 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 'bundles' directory"); + } + } + } catch (Exception e) { + if (LOG.isWarnEnabled()) + LOG.warn("Unable load bundles from the 'bundles' directory", e); + return 0; + } + + // Add shell and File Install bundles activation + bundleJars.add(getJarUrl(ShellService.class)); + //sb.append(getJarUrl(FileInstall.class)).append(" "); + bundleJars.add(getJarUrl(ServiceTracker.class)); + + //autostart bundles + configProps.put(AutoActivator.AUTO_START_PROP + ".1", StringUtils.join(bundleJars, " ")); + + return bundleJars.size(); + } + + private String getJarUrl(Class clazz) { + ProtectionDomain protectionDomain = clazz.getProtectionDomain(); + CodeSource codeSource = protectionDomain.getCodeSource(); + URL loc = codeSource.getLocation(); + return loc.toString(); + } + + private 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 + */ + private void addExportedPackages(Properties strutsConfigProps, Properties configProps) { + String[] rootPackages = StringUtils.split((String) strutsConfigProps.get("scanning.package.includes"), ","); + ResourceFinder finder = new ResourceFinder(StringUtils.EMPTY, OsgiConfigurationProvider.class.getClassLoader()); + 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 + */ + private 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 "." + */ + 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, "."); + } + + private 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(); + } + } + + /** + * Bundle activators that will be added to the container + */ + public void setExtraBundleActivators(List<? extends BundleActivator> extraBundleActivators) { + this.extraBundleActivators = extraBundleActivators; + } + + public Map<String, Bundle> getBundles() { + return bundles; + } + + public void destroy() throws Exception { + try { + felix.stop(); + } finally { + bundles = null; + } + } + + @Override + public void init() throws Exception { + startFelix(); + } + + @Inject("struts.osgi.clearBundleCache") + public void setCleanBundleCache(String cleanBundleCache) { + this.cleanBundleCache = "true".equalsIgnoreCase(cleanBundleCache); + } +} Modified: struts/sandbox/trunk/struts2-osgi-plugin/plugin/src/main/java/org/apache/struts2/osgi/OsgiConfigurationProvider.java URL: http://svn.apache.org/viewvc/struts/sandbox/trunk/struts2-osgi-plugin/plugin/src/main/java/org/apache/struts2/osgi/OsgiConfigurationProvider.java?rev=761766&r1=761765&r2=761766&view=diff ============================================================================== --- struts/sandbox/trunk/struts2-osgi-plugin/plugin/src/main/java/org/apache/struts2/osgi/OsgiConfigurationProvider.java (original) +++ struts/sandbox/trunk/struts2-osgi-plugin/plugin/src/main/java/org/apache/struts2/osgi/OsgiConfigurationProvider.java Fri Apr 3 19:13:06 2009 @@ -1,98 +1,66 @@ package org.apache.struts2.osgi; -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.net.MalformedURLException; -import java.net.URL; -import java.net.URLDecoder; -import java.security.CodeSource; -import java.security.ProtectionDomain; -import java.util.*; -import java.util.jar.JarEntry; -import java.util.jar.JarInputStream; - -import org.apache.felix.framework.Felix; -import org.apache.felix.framework.cache.BundleCache; -import org.apache.felix.framework.util.FelixConstants; -import org.apache.felix.framework.util.StringMap; -import org.apache.felix.main.AutoActivator; -import org.apache.felix.shell.ShellService; +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.Inject; +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.osgi.framework.Bundle; import org.osgi.framework.BundleActivator; import org.osgi.framework.BundleContext; import org.osgi.framework.BundleEvent; -import org.osgi.framework.BundleException; import org.osgi.framework.BundleListener; -import org.osgi.framework.Constants; import org.osgi.framework.InvalidSyntaxException; import org.osgi.framework.ServiceReference; -import org.twdata.pkgscanner.ExportPackage; -import org.twdata.pkgscanner.PackageScanner; -import static org.twdata.pkgscanner.PackageScanner.*; - -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.Inject; -import com.opensymphony.xwork2.util.logging.Logger; -import com.opensymphony.xwork2.util.logging.LoggerFactory; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +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 { - private static final String FELIX_LOG_LEVEL = "felix.log.level"; - private static final Logger LOG = LoggerFactory.getLogger(OsgiConfigurationProvider.class); - private static final String FELIX_FILEINSTALL_POLL = "felix.fileinstall.poll"; - private static final String FELIX_FILEINSTALL_DIR = "felix.fileinstall.dir"; - private static final String FELIX_FILEINSTALL_DEBUG = "felix.fileinstall.debug"; - - private Felix felix; - private Map<String,Bundle> bundles = Collections.synchronizedMap(new HashMap<String,Bundle>()); private Configuration configuration; + private ObjectFactory objectFactory; + private OsgiHost osgiHost; + private BundleContext bundleContext; private BundleAccessor bundleAccessor; private boolean bundlesChanged = false; - private ObjectFactory objectFactory; - - @Inject - public void setBundleAccessor(BundleAccessor acc) { - this.bundleAccessor = acc; - } - - @Inject - public void setObjectFactory(ObjectFactory factory) { - this.objectFactory = factory; - } - - @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); - } - public void destroy() { try { - felix.stop(); - } catch (BundleException e) { - LOG.error("Failed to stop Felix", e); + if (LOG.isTraceEnabled()) + LOG.trace("Stopping OSGi container"); + osgiHost.destroy(); + } catch (Exception e) { + LOG.error("Failed to stop OSGi container", e); } - bundles = null; } public void init(Configuration configuration) throws ConfigurationException { - loadOsgi(); + if (LOG.isTraceEnabled()) + LOG.trace("Starting OSGi container"); + try { + osgiHost.setExtraBundleActivators(Arrays.asList(new BundleRegistrationListener())); + osgiHost.init(); + } catch (Exception e) { + if (LOG.isErrorEnabled()) + LOG.error("Failed to start the OSGi container", e); + throw new ConfigurationException(e); + } this.configuration = configuration; } @@ -103,22 +71,25 @@ } catch (InvalidSyntaxException e) { throw new ConfigurationException(e); } - Map<String,String> packageToBundle = new HashMap<String,String>(); + Map<String, String> packageToBundle = new HashMap<String, String>(); Set<String> bundleNames = new HashSet<String>(); if (refs != null) { for (ServiceReference ref : refs) { if (!bundleNames.contains(ref.getBundle().getSymbolicName())) { bundleNames.add(ref.getBundle().getSymbolicName()); - LOG.info("Loading packages from bundle #1", ref.getBundle().getSymbolicName()); + + if (LOG.isDebugEnabled()) + LOG.debug("Loading packages from bundle [#0]", ref.getBundle().getSymbolicName()); + PackageLoader loader = (PackageLoader) bundleContext.getService(ref); - for (PackageConfig pkg : loader.loadPackages(ref.getBundle(), bundleContext, objectFactory, configuration.getPackageConfigs())) { + for (PackageConfig pkg : loader.loadPackages(ref.getBundle(), bundleContext, objectFactory, configuration.getPackageConfigs())) { configuration.addPackageConfig(pkg.getName(), pkg); packageToBundle.put(pkg.getName(), ref.getBundle().getSymbolicName()); } } } } - bundleAccessor.init(bundles, bundleContext, packageToBundle); + bundleAccessor.init(osgiHost.getBundles(), bundleContext, packageToBundle); bundlesChanged = false; } @@ -126,145 +97,34 @@ return bundlesChanged; } - protected void loadOsgi() { - //configuration properties - Properties systemProperties = getProperties("default.properties"); - - //struts, xwork and felix exported packages - Properties packages = getProperties("struts-osgi.properties"); - - Map<String, String> configMap = new StringMap(false); - - configMap.put(Constants.FRAMEWORK_SYSTEMPACKAGES, packages.getProperty("packages") - + "," + getScannedPackages(packages) - + getSystemPackages(systemProperties)); - - - // find bundles - Set<String> bundlePaths = new HashSet<String>(findInPackage("bundles")); - LOG.info("Loading Struts bundles "+bundlePaths); - - StringBuilder sb = new StringBuilder(); - for (String path : bundlePaths) { - sb.append(path).append(" "); - } - - // Add shell and File Install bundles activation - sb.append(getJarUrl(ShellService.class)).append(" "); - //sb.append(getJarUrl(FileInstall.class)).append(" "); - //sb.append(getJarUrl(ServiceTracker.class)); - - //autostart bundles - configMap.put(AutoActivator.AUTO_START_PROP + ".1", sb.toString()); - - // Bundle cache - configMap.put(BundleCache.CACHE_PROFILE_DIR_PROP, System.getProperty("java.io.tmpdir") + ".felix-cache"); - configMap.put(BundleCache.CACHE_DIR_PROP, "jim"); - - // File Install - String bundlesDir = Thread.currentThread().getContextClassLoader().getResource("bundles").getPath(); - configMap.put(OsgiConfigurationProvider.FELIX_FILEINSTALL_POLL, "5000"); - configMap.put(OsgiConfigurationProvider.FELIX_FILEINSTALL_DIR, bundlesDir); - configMap.put(OsgiConfigurationProvider.FELIX_FILEINSTALL_DEBUG, "1"); - - //other properties - configMap.put(FelixConstants.EMBEDDED_EXECUTION_PROP, "true"); - configMap.put(FelixConstants.SERVICE_URLHANDLERS_PROP, "false"); - configMap.put(OsgiConfigurationProvider.FELIX_LOG_LEVEL, "4"); - configMap.put(FelixConstants.BUNDLE_CLASSPATH, "."); - - try { - List<BundleActivator> list = new ArrayList<BundleActivator>(); - list.add(new BundleRegistration()); - list.add(new AutoActivator(configMap)); - // Now create an instance of the framework. - Map configMap2 = new StringMap(configMap, false); - felix = new Felix(configMap2, list); - felix.start(); - } - catch (Exception ex) { - throw new ConfigurationException("Couldn't start Felix (OSGi)", ex); - } - - // Wait for all bundles to load - while (bundles.size() < bundlePaths.size()) { - try { - Thread.sleep(500); - } catch (InterruptedException e) { - LOG.error("An error occured while waiting for bundle activation", e); - } - } - } - - private String getJarUrl(Class clazz) { - ProtectionDomain protectionDomain = clazz.getProtectionDomain(); - CodeSource codeSource = protectionDomain.getCodeSource(); - URL loc = codeSource.getLocation(); - return loc.toString(); - } - - private String getSystemPackages(Properties properties) { - String jreVersion = "jre-" + System.getProperty("java.version").substring(0, 3); - return properties.getProperty(jreVersion); + @Inject + public void setObjectFactory(ObjectFactory factory) { + this.objectFactory = factory; } - String getScannedPackages(Properties props) { - Collection<ExportPackage> exports = new PackageScanner() - .select( - jars( - include(toArray(props.getProperty("scanning.jar.includes"))), - exclude(toArray(props.getProperty("scanning.jar.excludes")))), - packages( - include(toArray(props.getProperty("scanning.package.includes"))), - exclude(toArray(props.getProperty("scanning.package.excludes"))))) - .scan(); - - StringBuilder sb = new StringBuilder(); - sb.append("Export-Package: "); - for (Iterator<ExportPackage> i = exports.iterator(); i.hasNext(); ) { - ExportPackage pkg = i.next(); - sb.append(pkg.getPackageName()); - if (pkg.getVersion() != null) { - sb.append(";version=").append(pkg.getVersion()); - } - if (i.hasNext()) { - sb.append(","); - } - } - return sb.toString(); + @Inject("felix") + public void setOsgiHost(OsgiHost osgiHost) { + this.osgiHost = osgiHost; } - String[] toArray(String val) { - if (val != null) { - return val.split("\\s*,\\s*"); - } else { - return new String[]{}; - } + @Inject + public void setBundleAccessor(BundleAccessor acc) { + this.bundleAccessor = acc; } - private Properties getProperties(String fileName) { - URL propertiesURL = OsgiConfigurationProvider.class.getClassLoader().getResource( - fileName); - Properties properties = new Properties(); - InputStream is = null; - try { - is = propertiesURL.openConnection().getInputStream(); - properties.load(is); - is.close(); - } catch (Exception ex2) { - // Try to close input stream if we have one. - try { - if (is != null) - is.close(); - } catch (IOException ex3) { - // Nothing we can do. - } - } - return properties; + @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); } - class BundleRegistration implements BundleActivator, BundleListener { - + /** + * Listens to bundle events and adds bundles to the bundle list when one is activated + */ + class BundleRegistrationListener implements BundleActivator, BundleListener { public void start(BundleContext context) throws Exception { context.addBundleListener(this); bundleContext = context; @@ -275,124 +135,13 @@ public void bundleChanged(BundleEvent evt) { if (evt.getType() == BundleEvent.STARTED && evt.getBundle().getSymbolicName() != null) { - LOG.info("Started bundle #1", evt.getBundle().getSymbolicName()); + if (LOG.isDebugEnabled()) + LOG.debug("Started bundle [#0]", evt.getBundle().getSymbolicName()); - bundles.put(evt.getBundle().getSymbolicName(), evt.getBundle()); + osgiHost.getBundles().put(evt.getBundle().getSymbolicName(), evt.getBundle()); bundlesChanged = true; } } } - /** - * Scans for classes starting at the package provided and descending into subpackages. - * Each class is offered up to the Test as it is discovered, and if the Test returns - * true the class is retained. Accumulated classes can be fetched by calling - * - * @param packageName the name of the package from which to start scanning for - * classes, e.g. {...@code net.sourceforge.stripes} - */ - public List<String> findInPackage(String packageName) { - packageName = packageName.replace('.', '/'); - Enumeration<URL> urls; - List<String> paths = new ArrayList<String>(); - - try { - urls = Thread.currentThread().getContextClassLoader().getResources(packageName); - } - catch (IOException ioe) { - LOG.warn("Could not read package: " + packageName, ioe); - return paths; - } - - while (urls.hasMoreElements()) { - try { - String urlPath = urls.nextElement().getFile(); - urlPath = URLDecoder.decode(urlPath, "UTF-8"); - - // If it's a file in a directory, trim the stupid file: spec - if ( urlPath.startsWith("file:") ) { - urlPath = urlPath.substring(5); - } - - // Else it's in a JAR, grab the path to the jar - if (urlPath.indexOf('!') > 0) { - urlPath = urlPath.substring(0, urlPath.indexOf('!')); - } - - //log.info("Scanning for classes in [" + urlPath + "] matching criteria: " + test); - File file = new File(urlPath); - if ( file.isDirectory() ) { - loadImplementationsInDirectory(paths, packageName, file); - } - else { - loadImplementationsInJar(paths, packageName, file); - } - } - catch (IOException ioe) { - LOG.warn("could not read entries", ioe); - } - } - return paths; - } - - - /** - * Finds matches in a physical directory on a filesystem. Examines all - * files within a directory - if the File object is not a directory, and ends with <i>.class</i> - * the file is loaded and tested to see if it is acceptable according to the Test. Operates - * recursively to find classes within a folder structure matching the package structure. - * - * @param parent the package name up to this directory in the package hierarchy. E.g. if - * /classes is in the classpath and we wish to examine files in /classes/org/apache then - * the values of <i>parent</i> would be <i>org/apache</i> - * @param location a File object representing a directory - */ - private void loadImplementationsInDirectory(List<String> paths, String parent, File location) { - File[] files = location.listFiles(); - StringBuilder builder = null; - - for (File file : files) { - builder = new StringBuilder(100); - builder.append(parent).append("/").append(file.getName()); - String packageOrClass = ( parent == null ? file.getName() : builder.toString() ); - - if (file.isDirectory()) { - loadImplementationsInDirectory(paths, packageOrClass, file); - } - else if (file.getName().endsWith(".jar")) { - try { - paths.add(file.toURI().toURL().toString()); - } catch (MalformedURLException e) { - LOG.error("Invalid file path", e); - } - } - } - } - - /** - * Finds matching classes within a jar files that contains a folder structure - * matching the package structure. If the File is not a JarFile or does not exist a warning - * will be logged, but no error will be raised. - * - * @param parent the parent package under which classes must be in order to be considered - * @param jarfile the jar file to be examined for classes - */ - private void loadImplementationsInJar(List<String> paths, String parent, File jarfile) { - - try { - JarEntry entry; - JarInputStream jarStream = new JarInputStream(new FileInputStream(jarfile)); - - while ( (entry = jarStream.getNextJarEntry() ) != null) { - String name = entry.getName(); - if (!entry.isDirectory() && name.startsWith(parent) && name.endsWith(".jar")) { - paths.add(jarfile.toURI().toURL()+"!"+entry.getName()); - } - } - } - catch (IOException ioe) { - LOG.error("Could not search jar file #1 due to an IOException", ioe, jarfile.toString()); - } - } - } Added: struts/sandbox/trunk/struts2-osgi-plugin/plugin/src/main/java/org/apache/struts2/osgi/OsgiHost.java URL: http://svn.apache.org/viewvc/struts/sandbox/trunk/struts2-osgi-plugin/plugin/src/main/java/org/apache/struts2/osgi/OsgiHost.java?rev=761766&view=auto ============================================================================== --- struts/sandbox/trunk/struts2-osgi-plugin/plugin/src/main/java/org/apache/struts2/osgi/OsgiHost.java (added) +++ struts/sandbox/trunk/struts2-osgi-plugin/plugin/src/main/java/org/apache/struts2/osgi/OsgiHost.java Fri Apr 3 19:13:06 2009 @@ -0,0 +1,34 @@ +/* + * $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 java.util.List; +import java.util.Map; + +public interface OsgiHost { + void destroy() throws Exception; + void init() throws Exception; + void setExtraBundleActivators(List<? extends BundleActivator> extraBundleActivators); + Map<String, Bundle> getBundles(); +} Modified: struts/sandbox/trunk/struts2-osgi-plugin/plugin/src/main/java/org/apache/struts2/osgi/loaders/StaticContentBundleResourceLoader.java URL: http://svn.apache.org/viewvc/struts/sandbox/trunk/struts2-osgi-plugin/plugin/src/main/java/org/apache/struts2/osgi/loaders/StaticContentBundleResourceLoader.java?rev=761766&r1=761765&r2=761766&view=diff ============================================================================== --- struts/sandbox/trunk/struts2-osgi-plugin/plugin/src/main/java/org/apache/struts2/osgi/loaders/StaticContentBundleResourceLoader.java (original) +++ struts/sandbox/trunk/struts2-osgi-plugin/plugin/src/main/java/org/apache/struts2/osgi/loaders/StaticContentBundleResourceLoader.java Fri Apr 3 19:13:06 2009 @@ -11,7 +11,6 @@ * */ public class StaticContentBundleResourceLoader extends DefaultStaticContentLoader { - @Override protected InputStream findInputStream(String path) throws IOException { return DefaultBundleAccessor.getInstance().loadResourceFromAllBundlesAsStream(path); } Modified: struts/sandbox/trunk/struts2-osgi-plugin/plugin/src/main/resources/struts-osgi.properties URL: http://svn.apache.org/viewvc/struts/sandbox/trunk/struts2-osgi-plugin/plugin/src/main/resources/struts-osgi.properties?rev=761766&r1=761765&r2=761766&view=diff ============================================================================== --- struts/sandbox/trunk/struts2-osgi-plugin/plugin/src/main/resources/struts-osgi.properties (original) +++ struts/sandbox/trunk/struts2-osgi-plugin/plugin/src/main/resources/struts-osgi.properties Fri Apr 3 19:13:06 2009 @@ -15,17 +15,10 @@ # specific language governing permissions and limitations # under the License. -packages = org.osgi.framework; version=1.4.0, \ - org.osgi.service.packageadmin; version=1.2.0, \ - org.osgi.service.startlevel; version=1.0.0, \ - org.osgi.service.url; version=1.0.0 +scanning.package.includes = com.opensymphony.xwork2, \ + org.apache.struts2, \ + ognl, \ + freemarker, \ + org.apache.velocity, \ + javax.servlet -scanning.jar.includes = *.jar -scanning.jar.excludes = -scanning.package.includes = com.opensymphony.xwork2*, \ - org.apache.struts2*, \ - ognl*, \ - freemarker*, \ - org.apache.velocity*, \ - javax.servlet* -scanning.package.excludes = Modified: struts/sandbox/trunk/struts2-osgi-plugin/plugin/src/main/resources/struts-plugin.xml URL: http://svn.apache.org/viewvc/struts/sandbox/trunk/struts2-osgi-plugin/plugin/src/main/resources/struts-plugin.xml?rev=761766&r1=761765&r2=761766&view=diff ============================================================================== --- struts/sandbox/trunk/struts2-osgi-plugin/plugin/src/main/resources/struts-plugin.xml (original) +++ struts/sandbox/trunk/struts2-osgi-plugin/plugin/src/main/resources/struts-plugin.xml Fri Apr 3 19:13:06 2009 @@ -8,10 +8,13 @@ <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="osgi" type="com.opensymphony.xwork2.config.PackageProvider" class="org.apache.struts2.osgi.OsgiConfigurationProviderTest" /> + <bean name="osgi" type="com.opensymphony.xwork2.config.PackageProvider" class="org.apache.struts2.osgi.OsgiConfigurationProvider" /> + <bean name="felix" type="org.apache.struts2.osgi.OsgiHost" class="org.apache.struts2.osgi.FelixOsgiHost" /> <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.osgi.clearBundleCache" value="true" /> </struts> Added: struts/sandbox/trunk/struts2-osgi-plugin/plugin/src/test/java/org/apache/struts2/osgi/FelixOsgiHostTest.java URL: http://svn.apache.org/viewvc/struts/sandbox/trunk/struts2-osgi-plugin/plugin/src/test/java/org/apache/struts2/osgi/FelixOsgiHostTest.java?rev=761766&view=auto ============================================================================== --- struts/sandbox/trunk/struts2-osgi-plugin/plugin/src/test/java/org/apache/struts2/osgi/FelixOsgiHostTest.java (added) +++ struts/sandbox/trunk/struts2-osgi-plugin/plugin/src/test/java/org/apache/struts2/osgi/FelixOsgiHostTest.java Fri Apr 3 19:13:06 2009 @@ -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")); + } +}