| /* |
| * 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.felix.main; |
| |
| import java.io.File; |
| import java.util.*; |
| import org.osgi.framework.*; |
| import org.osgi.service.startlevel.*; |
| |
| public class AutoProcessor |
| { |
| /** |
| * The property name used for the bundle directory. |
| **/ |
| public static final String AUTO_DEPLOY_DIR_PROPERY = "felix.auto.deploy.dir"; |
| /** |
| * The default name used for the bundle directory. |
| **/ |
| public static final String AUTO_DEPLOY_DIR_VALUE = "bundle"; |
| /** |
| * The property name used to specify auto-deploy actions. |
| **/ |
| public static final String AUTO_DEPLOY_ACTION_PROPERY = "felix.auto.deploy.action"; |
| /** |
| * The name used for the auto-deploy install action. |
| **/ |
| public static final String AUTO_DEPLOY_INSTALL_VALUE = "install"; |
| /** |
| * The name used for the auto-deploy start action. |
| **/ |
| public static final String AUTO_DEPLOY_START_VALUE = "start"; |
| /** |
| * The name used for the auto-deploy update action. |
| **/ |
| public static final String AUTO_DEPLOY_UPDATE_VALUE = "update"; |
| /** |
| * The name used for the auto-deploy uninstall action. |
| **/ |
| public static final String AUTO_DEPLOY_UNINSTALL_VALUE = "uninstall"; |
| /** |
| * The property name prefix for the launcher's auto-install property. |
| **/ |
| public static final String AUTO_INSTALL_PROP = "felix.auto.install"; |
| /** |
| * The property name prefix for the launcher's auto-start property. |
| **/ |
| public static final String AUTO_START_PROP = "felix.auto.start"; |
| |
| /** |
| * Used to instigate auto-deploy directory process and auto-install/auto-start |
| * configuration property processing during. |
| * @param configMap Map of configuration properties. |
| * @param context The system bundle context. |
| **/ |
| public static void process(Map configMap, BundleContext context) |
| { |
| configMap = (configMap == null) ? new HashMap() : configMap; |
| processAutoDeploy(configMap, context); |
| processAutoProperties(configMap, context); |
| } |
| |
| /** |
| * <p> |
| * Processes bundles in the auto-deploy directory, installing and then |
| * starting each one. |
| * </p> |
| */ |
| private static void processAutoDeploy(Map configMap, BundleContext context) |
| { |
| // Determine if auto deploy actions to perform. |
| String action = (String) configMap.get(AUTO_DEPLOY_ACTION_PROPERY); |
| action = (action == null) ? "" : action; |
| List actionList = new ArrayList(); |
| StringTokenizer st = new StringTokenizer(action, ","); |
| while (st.hasMoreTokens()) |
| { |
| String s = st.nextToken().trim().toLowerCase(); |
| if (s.equals(AUTO_DEPLOY_INSTALL_VALUE) |
| || s.equals(AUTO_DEPLOY_START_VALUE) |
| || s.equals(AUTO_DEPLOY_UPDATE_VALUE) |
| || s.equals(AUTO_DEPLOY_UNINSTALL_VALUE)) |
| { |
| actionList.add(s); |
| } |
| } |
| |
| // Perform auto-deploy actions. |
| if (actionList.size() > 0) |
| { |
| // Get list of already installed bundles as a map. |
| Map installedBundleMap = new HashMap(); |
| Bundle[] bundles = context.getBundles(); |
| for (int i = 0; i < bundles.length; i++) |
| { |
| installedBundleMap.put(bundles[i].getLocation(), bundles[i]); |
| } |
| |
| // Get the auto deploy directory. |
| String autoDir = (String) configMap.get(AUTO_DEPLOY_DIR_PROPERY); |
| autoDir = (autoDir == null) ? AUTO_DEPLOY_DIR_VALUE : autoDir; |
| // Look in the specified bundle directory to create a list |
| // of all JAR files to install. |
| File[] files = new File(autoDir).listFiles(); |
| List jarList = new ArrayList(); |
| if (files != null) |
| { |
| Arrays.sort(files); |
| for (int i = 0; i < files.length; i++) |
| { |
| if (files[i].getName().endsWith(".jar")) |
| { |
| jarList.add(files[i]); |
| } |
| } |
| } |
| |
| // Install bundle JAR files and remember the bundle objects. |
| final List startBundleList = new ArrayList(); |
| for (int i = 0; i < jarList.size(); i++) |
| { |
| // Look up the bundle by location, removing it from |
| // the map of installed bundles so the remaining bundles |
| // indicate which bundles may need to be uninstalled. |
| Bundle b = (Bundle) installedBundleMap.remove( |
| ((File) jarList.get(i)).toURI().toString()); |
| try |
| { |
| // If the bundle is not already installed, then install it |
| // if the 'install' action is present. |
| if ((b == null) && actionList.contains(AUTO_DEPLOY_INSTALL_VALUE)) |
| { |
| b = context.installBundle( |
| ((File) jarList.get(i)).toURI().toString()); |
| } |
| // If the bundle is already installed, then update it |
| // if the 'update' action is present. |
| else if (actionList.contains(AUTO_DEPLOY_UPDATE_VALUE)) |
| { |
| b.update(); |
| } |
| |
| // If we have found and/or successfully installed a bundle, |
| // then add it to the list of bundles to potentially start. |
| if (b != null) |
| { |
| startBundleList.add(b); |
| } |
| } |
| catch (BundleException ex) |
| { |
| System.err.println("Auto-deploy install: " |
| + ex + ((ex.getCause() != null) ? " - " + ex.getCause() : "")); |
| } |
| } |
| |
| // Uninstall all bundles not in the auto-deploy directory if |
| // the 'uninstall' action is present. |
| if (actionList.contains(AUTO_DEPLOY_UNINSTALL_VALUE)) |
| { |
| for (Iterator it = installedBundleMap.entrySet().iterator(); it.hasNext(); ) |
| { |
| Map.Entry entry = (Map.Entry) it.next(); |
| Bundle b = (Bundle) entry.getValue(); |
| if (b.getBundleId() != 0) |
| { |
| try |
| { |
| b.uninstall(); |
| } |
| catch (BundleException ex) |
| { |
| System.err.println("Auto-deploy uninstall: " |
| + ex + ((ex.getCause() != null) ? " - " + ex.getCause() : "")); |
| } |
| } |
| } |
| } |
| |
| // Start all installed and/or updated bundles if the 'start' |
| // action is present. |
| if (actionList.contains(AUTO_DEPLOY_START_VALUE)) |
| { |
| for (int i = 0; i < startBundleList.size(); i++) |
| { |
| try |
| { |
| if (!isFragment((Bundle) startBundleList.get(i))) |
| { |
| ((Bundle) startBundleList.get(i)).start(); |
| } |
| } |
| catch (BundleException ex) |
| { |
| System.err.println("Auto-deploy start: " |
| + ex + ((ex.getCause() != null) ? " - " + ex.getCause() : "")); |
| } |
| } |
| } |
| } |
| } |
| |
| /** |
| * <p> |
| * Processes the auto-install and auto-start properties from the |
| * specified configuration properties. |
| * </p> |
| */ |
| private static void processAutoProperties(Map configMap, BundleContext context) |
| { |
| // Retrieve the Start Level service, since it will be needed |
| // to set the start level of the installed bundles. |
| StartLevel sl = (StartLevel) context.getService( |
| context.getServiceReference(org.osgi.service.startlevel.StartLevel.class.getName())); |
| |
| // Retrieve all auto-install and auto-start properties and install |
| // their associated bundles. The auto-install property specifies a |
| // space-delimited list of bundle URLs to be automatically installed |
| // into each new profile, while the auto-start property specifies |
| // bundles to be installed and started. The start level to which the |
| // bundles are assigned is specified by appending a ".n" to the |
| // property name, where "n" is the desired start level for the list |
| // of bundles. If no start level is specified, the default start |
| // level is assumed. |
| for (Iterator i = configMap.keySet().iterator(); i.hasNext(); ) |
| { |
| String key = ((String) i.next()).toLowerCase(); |
| |
| // Ignore all keys that are not an auto property. |
| if (!key.startsWith(AUTO_INSTALL_PROP) && !key.startsWith(AUTO_START_PROP)) |
| { |
| continue; |
| } |
| |
| // If the auto property does not have a start level, |
| // then assume it is the default bundle start level, otherwise |
| // parse the specified start level. |
| int startLevel = sl.getInitialBundleStartLevel(); |
| if (!key.equals(AUTO_INSTALL_PROP) && !key.equals(AUTO_START_PROP)) |
| { |
| try |
| { |
| startLevel = Integer.parseInt(key.substring(key.lastIndexOf('.') + 1)); |
| } |
| catch (NumberFormatException ex) |
| { |
| System.err.println("Invalid property: " + key); |
| } |
| } |
| |
| // Parse and install the bundles associated with the key. |
| StringTokenizer st = new StringTokenizer((String) configMap.get(key), "\" ", true); |
| for (String location = nextLocation(st); location != null; location = nextLocation(st)) |
| { |
| try |
| { |
| Bundle b = context.installBundle(location, null); |
| sl.setBundleStartLevel(b, startLevel); |
| } |
| catch (Exception ex) |
| { |
| System.err.println("Auto-properties install: " + location + " (" |
| + ex + ((ex.getCause() != null) ? " - " + ex.getCause() : "") + ")"); |
| if (ex.getCause() != null) |
| ex.printStackTrace(); |
| } |
| } |
| } |
| |
| // Now loop through the auto-start bundles and start them. |
| for (Iterator i = configMap.keySet().iterator(); i.hasNext(); ) |
| { |
| String key = ((String) i.next()).toLowerCase(); |
| if (key.startsWith(AUTO_START_PROP)) |
| { |
| StringTokenizer st = new StringTokenizer((String) configMap.get(key), "\" ", true); |
| for (String location = nextLocation(st); location != null; location = nextLocation(st)) |
| { |
| // Installing twice just returns the same bundle. |
| try |
| { |
| Bundle b = context.installBundle(location, null); |
| if (b != null) |
| { |
| b.start(); |
| } |
| } |
| catch (Exception ex) |
| { |
| System.err.println("Auto-properties start: " + location + " (" |
| + ex + ((ex.getCause() != null) ? " - " + ex.getCause() : "") + ")"); |
| } |
| } |
| } |
| } |
| } |
| |
| private static String nextLocation(StringTokenizer st) |
| { |
| String retVal = null; |
| |
| if (st.countTokens() > 0) |
| { |
| String tokenList = "\" "; |
| StringBuffer tokBuf = new StringBuffer(10); |
| String tok = null; |
| boolean inQuote = false; |
| boolean tokStarted = false; |
| boolean exit = false; |
| while ((st.hasMoreTokens()) && (!exit)) |
| { |
| tok = st.nextToken(tokenList); |
| if (tok.equals("\"")) |
| { |
| inQuote = ! inQuote; |
| if (inQuote) |
| { |
| tokenList = "\""; |
| } |
| else |
| { |
| tokenList = "\" "; |
| } |
| |
| } |
| else if (tok.equals(" ")) |
| { |
| if (tokStarted) |
| { |
| retVal = tokBuf.toString(); |
| tokStarted=false; |
| tokBuf = new StringBuffer(10); |
| exit = true; |
| } |
| } |
| else |
| { |
| tokStarted = true; |
| tokBuf.append(tok.trim()); |
| } |
| } |
| |
| // Handle case where end of token stream and |
| // still got data |
| if ((!exit) && (tokStarted)) |
| { |
| retVal = tokBuf.toString(); |
| } |
| } |
| |
| return retVal; |
| } |
| |
| private static boolean isFragment(Bundle bundle) |
| { |
| return bundle.getHeaders().get(Constants.FRAGMENT_HOST) != null; |
| } |
| } |