blob: f9df59c43b41bd4b928d37a1d86be56f7effe8c7 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2003, 2006 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.osgi.framework.launcher;
import java.lang.reflect.Constructor;
import java.util.*;
import org.eclipse.osgi.framework.adaptor.FrameworkAdaptor;
import org.eclipse.osgi.framework.internal.core.*;
import org.eclipse.osgi.util.NLS;
/**
* <p>This class provides an entry point for launching the OSGi framework.
* It configures the OSGi framework according to the command line arguments:
* <ul>
* <li><b>-con[sole][:<i>port</i>]</b><br>
* Starts the OSGi framework with a console window. Any command line arguments not recognized are passed
* to the console for it to execute. If a port is specified the console will listen on that
* port for commands. If no port is specified, the console will use System.in and System.out.
* </li>
* <li><b>-adaptor[:adaptor-name][adaptor-args]</b>
* <pre>
* [adaptor-name] := "" | fully qualified class name of the FrameworkAdapter
* [adaptor-args] := *( ":" [value])
* [value] := [token] | [quoted-string]
*
* This allows
*
* -adaptor::"bundledir=c:\jarbundles":reset DefaultAdaptor is chosen with args[] {"bundledir=c:\jarbundles", "reset"}
* -adaptor:com.foo.MyAdaptor com.foo.MyAdaptor chosen with args[] {}
* </pre>
* <p>-adaptor specifies the implementation class for the FrameworkAdapter to be used.
* args contains a list of FrameworkAdaptor arguments, separated by ":". FrameworkAdaptor arguments
* format is defined by the adaptor implementation class. They
* are passed to the adaptor class as an array of Strings.
* Example arguments used by the DefaultAdaptor are:
* <ul>
* <li>"bundledir=<i>directory"</i>. The directory to be used by the adaptor to store data.
* <li>reset</i>. Perform the reset action to clear the bundledir.
* <p>Actions can be defined by an adaptor. Multiple actions can be specified,
* separated by ":".
* </ul>
* <p>It is up to the adaptor implementation to define reasonable defaults if it's required
* arguments are not specified.
* <p>If -adaptor is not specified, or if no adaptor classname is specified, DefaultAdaptor will be
* used, which is file based and stores the files in the \bundles directory
* relative to the current directory.
* </ul>
* <li>-app[lication]:application-args
* <pre>
* [application-args] := *( ":" [value])
* [value] := [token] | [quoted-string]
* </pre>
* <p>This argument allows arguments to be passed to specific applications at launch time. This is for eclipse
* plugins installed as applications. The arguments are as Eclipse currently needs them - one list of key=value pairs
* which are parsed by the applications. The application peels off only the args that apply to it. Others are ignored.
* </li>
* <p>
* Any other command line arguments are passed on to the console window
* of the framework if started with the -console option. If the console is not started,
* any unrecognized arguments will be ignored and a message displayed.
* <p>
* If none of the options above are specified, the OSGi framework is started:
* <ul>
* <li>with the Default FrameworkAdaptor
* <li>without a console window
* <li>without the remote agent
* </ul>
* @since 3.1
*/
public class Launcher {
/** default console port */
protected String consolePort = ""; //$NON-NLS-1$
/** flag to indicate whether or not to start the console */
protected boolean console = false;
/** string containing the classname of the adaptor to be used in this framework instance */
protected String adaptorClassName = "org.eclipse.osgi.baseadaptor.BaseAdaptor"; //$NON-NLS-1$
protected final String osgiConsoleClazz = "org.eclipse.osgi.framework.internal.core.FrameworkConsole"; //$NON-NLS-1$
/** array of adaptor arguments to be passed to FrameworkAdaptor.initialize() */
protected String[] adaptorArgs = null;
/* Components that can be installed and activated optionally. */
private static final String OSGI_CONSOLE_COMPONENT_NAME = "OSGi Console"; //$NON-NLS-1$
private static final String OSGI_CONSOLE_COMPONENT = "console.jar"; //$NON-NLS-1$
/**
* main method for Launcher. This method creates an Launcher object
* and kicks off the actual launch of a Framework instance.
*
* @param args The command line arguments
*/
public static void main(String args[]) {
new Launcher().doIt(args);
}
/**
* Default constructor. Nothing at all to do here.
*/
public Launcher() {
// do nothing;
}
/**
* Performs the actual launch based on the command line arguments
*
* @param args The command line arguments
*/
protected void doIt(String[] args) {
// set the compatibility boot delegation flag to false to get "standard" OSGi behavior WRT boot delegation (bug 178477)
if (FrameworkProperties.getProperty(Constants.OSGI_COMPATIBILITY_BOOTDELEGATION) == null)
FrameworkProperties.setProperty(Constants.OSGI_COMPATIBILITY_BOOTDELEGATION, "false"); //$NON-NLS-1$
String[] consoleArgs = parseArgs(args);
FrameworkAdaptor adaptor = null;
try {
adaptor = doAdaptor();
} catch (Exception e) {
System.out.println(Msg.LAUNCHER_ADAPTOR_ERROR);
e.printStackTrace();
return;
}
OSGi osgi = doOSGi(adaptor);
if (osgi != null) {
if (console) {
doConsole(osgi, consoleArgs);
} else {
osgi.launch();
}
}
}
/**
* Parses the command line arguments and remembers them so they can be processed later.
*
* @param args The command line arguments
* @return String [] Any arguments that should be passed to the console
*/
protected String[] parseArgs(String[] args) {
Vector consoleArgsVector = new Vector();
for (int i = 0; i < args.length; i++) {
boolean match = false;
// Have to check for args that may be contained in double quotes but broken up by spaces - for example
// -adaptor::"bundledir=c:/my bundle dir":reset should all be one arg, but java breaks it into 3 args,
// ignoring the quotes. Must put it back together into one arg.
String fullarg = args[i];
int quoteidx = fullarg.indexOf("\""); //$NON-NLS-1$
if (quoteidx > 0) {
if (quoteidx == fullarg.lastIndexOf("\"")) { //$NON-NLS-1$
boolean stillparsing = true;
i++;
while (i < args.length && stillparsing) {
fullarg = fullarg + " " + args[i]; //$NON-NLS-1$
i++;
if (quoteidx < fullarg.lastIndexOf("\"")) { //$NON-NLS-1$
stillparsing = false;
}
}
}
} else {
// IDE can't pass double quotes due to known eclipse bug (see Bugzilla 93201). Allowing for use of single quotes.
quoteidx = fullarg.indexOf("'"); //$NON-NLS-1$
if (quoteidx > 0) {
if (quoteidx == fullarg.lastIndexOf("'")) { //$NON-NLS-1$
boolean stillparsing = true;
i++;
while (i < args.length && stillparsing) {
fullarg = fullarg + " " + args[i]; //$NON-NLS-1$
i++;
if (quoteidx < fullarg.lastIndexOf("'")) { //$NON-NLS-1$
stillparsing = false;
}
}
}
fullarg = fullarg.replace('\'', '\"');
}
}
Tokenizer tok = new Tokenizer(fullarg);
if (tok.hasMoreTokens()) {
String command = tok.getString(" "); //$NON-NLS-1$
StringTokenizer subtok = new StringTokenizer(command, ":"); //$NON-NLS-1$
String subcommand = subtok.nextToken().toLowerCase();
if (matchCommand("-console", subcommand, 4)) { //$NON-NLS-1$
_console(command);
match = true;
}
if (matchCommand("-adaptor", subcommand, 2)) { //$NON-NLS-1$
_adaptor(command);
match = true;
}
if (match == false) {
// if the command doesn't match any of the known commands, save it to pass
// to the console
consoleArgsVector.addElement(fullarg);
}
}
}
// convert arguments to be passed to console into a string array for the Console
String[] consoleArgsArray = new String[consoleArgsVector.size()];
Enumeration e = consoleArgsVector.elements();
for (int i = 0; i < consoleArgsArray.length; i++) {
consoleArgsArray[i] = (String) e.nextElement();
}
return consoleArgsArray;
}
public static boolean matchCommand(String command, String input, int minLength) {
if (minLength <= 0) {
minLength = command.length();
}
int length = input.length();
if (minLength > length) {
length = minLength;
}
return (command.regionMatches(0, input, 0, length));
}
/**
* Remembers that the -console option has been requested.
*/
protected void _console(String command) {
console = true;
StringTokenizer tok = new StringTokenizer(command, ":"); //$NON-NLS-1$
// first token is always "-console"
tok.nextToken();
if (tok.hasMoreTokens()) {
consolePort = tok.nextToken();
}
}
/**
* Remembers that the -adaptor option has been requested. Parses off the adaptor class
* file name, the adaptor file name, and the size if they are there.
*
* @param command The rest of the -adaptor parameter string that contains the class file name,
* and possibly the adaptor file and file size.
*/
protected void _adaptor(String command) {
Tokenizer tok = new Tokenizer(command);
// first token is always "-adaptor"
tok.getToken(":"); //$NON-NLS-1$
tok.getChar(); // advance to next token
// and next token is either adaptor class name or ":" if we should use the default adaptor
String adp = tok.getToken(":"); //$NON-NLS-1$
if (adp != null && adp.length() > 0) {
adaptorClassName = adp;
}
// The following tokens are arguments to be processed by the adaptor implementation class.
// They may be enclosed in quotes.
// Store them in a vector until we know how many there are.
Vector v = new Vector();
parseloop: while (true) {
tok.getChar(); // advance to next token
String arg = tok.getString(":"); //$NON-NLS-1$
if (arg == null)
break parseloop;
v.addElement(arg);
}
// now that we know how many args there are, move args from vector to String[]
if (v != null) {
int numArgs = v.size();
adaptorArgs = new String[numArgs];
Enumeration e = v.elements();
for (int i = 0; i < numArgs; i++) {
adaptorArgs[i] = (String) e.nextElement();
}
}
}
/**
* Processes the -adaptor command line argument.
*
* Parses the arguments to get the adaptor class name, the adaptor dir or filename,
* and the adaptor file size.
*
* @return a FrameworkAdaptor object
*/
protected FrameworkAdaptor doAdaptor() throws Exception {
Class adaptorClass = Class.forName(adaptorClassName);
Class[] constructorArgs = new Class[] {String[].class};
Constructor constructor = adaptorClass.getConstructor(constructorArgs);
return (FrameworkAdaptor) constructor.newInstance(new Object[] {adaptorArgs});
}
/**
* Creates the OSGi framework object.
*
* @param adaptor The FrameworkAdaptor object
*/
private OSGi doOSGi(FrameworkAdaptor adaptor) {
return new OSGi(adaptor);
}
/**
* Invokes the OSGi Console on another thread
*
* @param osgi The current OSGi instance for the console to attach to
* @param consoleArgs An String array containing commands from the command line
* for the console to execute
*/
private void doConsole(OSGi osgi, String[] consoleArgs) {
Constructor consoleConstructor;
Object osgiconsole;
Class[] parameterTypes;
Object[] parameters;
try {
Class osgiconsoleClass = Class.forName(osgiConsoleClazz);
if (consolePort.length() == 0) {
parameterTypes = new Class[] {OSGi.class, String[].class};
parameters = new Object[] {osgi, consoleArgs};
} else {
parameterTypes = new Class[] {OSGi.class, int.class, String[].class};
parameters = new Object[] {osgi, new Integer(consolePort), consoleArgs};
}
consoleConstructor = osgiconsoleClass.getConstructor(parameterTypes);
osgiconsole = consoleConstructor.newInstance(parameters);
Thread t = new Thread(((Runnable) osgiconsole), OSGI_CONSOLE_COMPONENT_NAME);
t.start();
} catch (NumberFormatException nfe) {
System.err.println(NLS.bind(Msg.LAUNCHER_INVALID_PORT, consolePort));
} catch (Exception ex) {
informAboutMissingComponent(OSGI_CONSOLE_COMPONENT_NAME, OSGI_CONSOLE_COMPONENT);
}
}
/**
* Informs the user about a missing component.
*
* @param component The name of the component
* @param jar The jar file that contains the component
*/
void informAboutMissingComponent(String component, String jar) {
System.out.println();
System.out.print(NLS.bind(Msg.LAUNCHER_COMPONENT_MISSING, component));
System.out.println(NLS.bind(Msg.LAUNCHER_COMPONENT_JAR, jar));
System.out.println();
}
}