blob: f9344005a9ec05aa5bf0fe9e16d9e1a7c2706796 [file] [log] [blame]
/*
* 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 property name used to specify auto-deploy start level.
**/
public static final String AUTO_DEPLOY_STARTLEVEL_PROPERY = "felix.auto.deploy.startlevel";
/**
* 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, performing the
* specified deploy actions.
* </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)
{
// 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()));
// Get start level for auto-deploy bundles.
int startLevel = sl.getInitialBundleStartLevel();
if (configMap.get(AUTO_DEPLOY_STARTLEVEL_PROPERY) != null)
{
try
{
startLevel = Integer.parseInt(
configMap.get(AUTO_DEPLOY_STARTLEVEL_PROPERY).toString());
}
catch (NumberFormatException ex)
{
// Ignore and keep default level.
}
}
// 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 ((b != null) && 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
// and also set its start level accordingly.
if ((b != null) && !isFragment(b))
{
startBundleList.add(b);
sl.setBundleStartLevel(b, startLevel);
}
}
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
{
((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;
}
}