Java tutorial
/* * $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 cn.calm.osgi.conter; import java.io.File; import java.io.FilenameFilter; import java.io.IOException; import java.net.URL; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.TreeMap; import javax.servlet.ServletContext; import org.apache.commons.lang3.StringUtils; import org.apache.felix.framework.FrameworkFactory; import org.apache.felix.framework.util.FelixConstants; import org.osgi.framework.Bundle; import org.osgi.framework.BundleContext; import org.osgi.framework.Constants; import org.osgi.framework.launch.Framework; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import cn.calm.osgi.conter.util.AutoProcessor; import cn.calm.osgi.conter.util.FelixPropertiesUtil; import cn.calm.osgi.conter.util.Resource; /** * 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); Resource resource; Framework f; private ServletContext servletContext; protected void startFelix() { // load properties from felix embedded file Properties configProps = getProperties("default.properties"); // Copy framework properties from the system properties. FelixPropertiesUtil.copySystemProperties(configProps); replaceSystemPackages(configProps); // struts, xwork and felix exported packages Properties strutsConfigProps = getProperties("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); } configProps.put(FelixConstants.SERVICE_URLHANDLERS_PROP, "false"); configProps.put(FelixConstants.LOG_LEVEL_PROP, getServletContextParam("struts.osgi.logLevel", "3")); configProps.put(FelixConstants.BUNDLE_CLASSPATH, "."); configProps.put("org.osgi.framework.startlevel.beginning", getServletContextParam("struts.osgi.runLevel", "3")); // try { Map<String, String> map = new TreeMap<String, String>(new Comparator<String>() { public int compare(String obj1, String obj2) { // ??? return obj1.compareTo(obj2); } }); for (Object o : configProps.keySet()) { map.put(String.valueOf(o), configProps.getProperty(String.valueOf(o))); } FrameworkFactory ff = new FrameworkFactory(); f = ff.newFramework(map); // f.start(); f.init(); AutoProcessor.process(map, f.getBundleContext()); f.start(); } catch (Exception ex) { throw new RuntimeException("Couldn't start Apache Felix", ex); } // add the bundle context to the ServletContext servletContext.setAttribute(OSGI_BUNDLE_CONTEXT, f.getBundleContext()); } /** * Gets a param from the ServletContext, returning the default value if the * param is not set * * @param paramName * the name of the param to get from the ServletContext * @param defaultValue * value to return if the param is not set * @return */ private String getServletContextParam(String paramName, String defaultValue) { return StringUtils.defaultString(this.servletContext.getInitParameter(paramName), defaultValue); } protected void addAutoStartBundles(Properties configProps) { List<String> bundleJarsLevel1 = new ArrayList<String>(); configProps.put(AutoProcessor.AUTO_START_PROP + ".1", StringUtils.join(bundleJarsLevel1, " ")); // get a list of directories under /bundles with numeric names (the // runlevel) Map<String, String> runLevels = getRunLevelDirs("bundles"); if (runLevels.isEmpty()) { // there are no run level dirs, search for bundles in that dir List<String> bundles = getBundlesInDir("bundles"); if (!bundles.isEmpty()) configProps.put(AutoProcessor.AUTO_START_PROP + ".2", StringUtils.join(bundles, " ")); } else { for (String runLevel : runLevels.keySet()) { if ("1".endsWith(runLevel)) throw new RuntimeException( "Run level dirs must be greater than 1. Run level 1 is reserved for the Felix bundles"); List<String> bundles = getBundlesInDir(runLevels.get(runLevel)); configProps.put(AutoProcessor.AUTO_START_PROP + "." + runLevel, StringUtils.join(bundles, " ")); } } } /** * Return a list of directories under a directory whose name is a number */ protected Map<String, String> getRunLevelDirs(String dir) { Map<String, String> dirs = new HashMap<String, String>(); try { URL url = resource.find("bundles"); if (url != null) { if ("file".equals(url.getProtocol())) { File bundlesDir = new File(url.toURI()); String[] runLevelDirs = bundlesDir.list(new FilenameFilter() { public boolean accept(File file, String name) { try { return file.isDirectory() && Integer.valueOf(name) > 0; } catch (NumberFormatException ex) { // the name is not a number return false; } } }); if (runLevelDirs != null && runLevelDirs.length > 0) { // add all the dirs to the list for (String runLevel : runLevelDirs) dirs.put(runLevel, StringUtils.removeEnd(dir, "/") + "/" + runLevel); } else if (LOG.isDebugEnabled()) { LOG.debug("No run level directories found under the [#0] directory", dir); } } else if (LOG.isWarnEnabled()) LOG.warn("Unable to read [#0] directory", dir); } else if (LOG.isWarnEnabled()) LOG.warn("The [#0] directory was not found", dir); } catch (Exception e) { if (LOG.isWarnEnabled()) LOG.warn("Unable load bundles from the [#0] directory", e, dir); } return dirs; } protected List<String> getBundlesInDir(String dir) { List<String> bundleJars = new ArrayList<String>(); try { URL url = resource.find(dir); if (url != null) { if ("file".equals(url.getProtocol())) { File bundlesDir = new File(url.toURI()); File[] bundles = bundlesDir.listFiles(new FilenameFilter() { public boolean accept(File file, String name) { return StringUtils.endsWith(name, ".jar"); } }); if (bundles != null && bundles.length > 0) { // add all the bundles to the list for (File bundle : bundles) { String externalForm = bundle.toURI().toURL().toExternalForm(); if (LOG.isDebugEnabled()) { LOG.debug("Adding bundle [#0]", externalForm); } bundleJars.add(externalForm); } } else if (LOG.isDebugEnabled()) { LOG.debug("No bundles found under the [#0] directory", dir); } } else if (LOG.isWarnEnabled()) LOG.warn("Unable to read [#0] directory", dir); } else if (LOG.isWarnEnabled()) LOG.warn("The [#0] directory was not found", dir); } catch (Exception e) { if (LOG.isWarnEnabled()) LOG.warn("Unable load bundles from the [#0] directory", e, dir); } return bundleJars; } // // protected void 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(strutsConfigProps.getProperty("scanning.package.includes"), ","); List<String> exportedPackages = new ArrayList<String>(); // build a list of subpackages StringBuilder strB = new StringBuilder(); // org.osgi.framework;version="[1.4,2)" for (String rootPackage : rootPackages) { String sp[] = StringUtils.split(rootPackage, ";"); strB.delete(0, strB.length()); strB.append(sp[0]); strB.append(";version=\""); try { strB.append(sp[1]); } catch (Exception e) { strB.append("0.0.0"); } strB.append("\""); exportedPackages.add(strB.toString()); } // // // 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.removeEnd(systemPackages, ",") + "," + StringUtils.join(exportedPackages, ","); configProps.put(Constants.FRAMEWORK_SYSTEMPACKAGES, systemPackages); } System.out.println(exportedPackages); } // // /** // * Gets the version used to export the packages. it tries to get it from // * MANIFEST.MF, or the file name // */ // protected String getVersion(URL url) { // if ("jar".equals(url.getProtocol())) { // try { //// FileManager fileManager = ServletActionContext.getContext() //// .getInstance(FileManagerFactory.class).getFileManager(); // String file=url.toURI().toString(); // file=file.substring(10, file.lastIndexOf("!")); // JarFile jarFile = new JarFile(file); // 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) { try { return resource.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 : f.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 : f.getBundleContext().getBundles()) { if (bundle.getState() == Bundle.ACTIVE) bundles.put(bundle.getSymbolicName(), bundle); } return Collections.unmodifiableMap(bundles); } public BundleContext getBundleContext() { return f.getBundleContext(); } public void destroy() throws Exception { f.stop(); if (LOG.isTraceEnabled()) LOG.trace("Apache Felix has stopped"); } public void init(ServletContext servletContext) { this.servletContext = servletContext; resource = Resource.getResource(servletContext); startFelix(); } }