blob: a9b0118f99530325d833a8e6b68ce68ac9fa9c5c [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2003 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Common Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/cpl-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.OSGi;
import org.eclipse.osgi.framework.util.Tokenizer;
/**
* <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>
*/
public class Launcher {
// :TODO Need to NLS enable this class.
/** default console port */
protected String consolePort = "";
/** 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.framework.internal.defaultadaptor.DefaultAdaptor";
protected final String IdeAdentClassName = "org.eclipse.osgi.framework.internal.core.ide.IdeAgent";
protected final String osgiConsoleClazz = "org.eclipse.osgi.framework.internal.core.FrameworkConsole";
/** array of adaptor arguments to be passed to FrameworkAdaptor.initialize() */
String[] adaptorArgs = null;
/** array of application arguments to be passed to Eclipse applications */
String[] applicationArgs = null;
/* Components that can be installed and activated optionally. */
private static final String OSGI_CONSOLE_COMPONENT_NAME = "OSGi Console";
private static final String OSGI_CONSOLE_COMPONENT = "osgiconsole.jar";
/**
* 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() {
}
/**
* Performs the actual launch based on the command line arguments
*
* @param args The command line arguments
*/
protected void doIt(String[] args) {
String[] consoleArgs = parseArgs(args);
FrameworkAdaptor adaptor = null;
try {
adaptor = doAdaptor();
} catch (Exception e) {
System.out.println("Unable to create FrameworkAdaptor: ");
e.printStackTrace();
return;
}
OSGi osgi = doOSGi(adaptor);
if (osgi != null) {
if (console) {
doConsole(osgi, consoleArgs);
} else {
osgi.launch();
}
}
doApplication();
}
/**
* 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
*/
private 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("\"");
if (quoteidx > 0) {
if (quoteidx == fullarg.lastIndexOf("\"")) {
boolean stillparsing = true;
i++;
while (i < args.length && stillparsing) {
fullarg = fullarg + " " + args[i];
i++;
if (quoteidx < fullarg.lastIndexOf("\"")) {
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("'");
if (quoteidx > 0) {
if (quoteidx == fullarg.lastIndexOf("'")) {
boolean stillparsing = true;
i++;
while (i < args.length && stillparsing) {
fullarg = fullarg + " " + args[i];
i++;
if (quoteidx < fullarg.lastIndexOf("'")) {
stillparsing = false;
}
}
}
fullarg = fullarg.replace('\'', '\"');
}
}
Tokenizer tok = new Tokenizer(fullarg);
if (tok.hasMoreTokens()) {
String command = tok.getString(" ");
StringTokenizer subtok = new StringTokenizer(command, ":");
String subcommand = subtok.nextToken().toLowerCase();
if (matchCommand("-console", subcommand, 4)) {
_console(command);
match = true;
}
if (matchCommand("-adaptor", subcommand, 2)) {
_adaptor(command);
match = true;
}
if (matchCommand("-application", subcommand, 3)) {
_application(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, ":");
// first token is always "-console"
String cmd = 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 tok 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"
String cmd = tok.getToken(":");
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(":");
if (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(":");
if (arg == null) {
break parseloop;
} else {
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();
}
}
}
/**
* Remembers that the -application option has been requested. Parses off the application parameters
* into a String []
*
* @param tok The rest of the -application parameter string that contains the application arguments
*/
protected void _application(String command) {
Tokenizer tok = new Tokenizer(command);
// first token is always "-adaptor"
String cmd = tok.getToken(":");
// 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(":");
if (arg == null) {
break parseloop;
} else {
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();
applicationArgs = new String[numArgs];
Enumeration e = v.elements();
for (int i = 0; i < numArgs; i++) {
applicationArgs[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});
}
/**
* Processes the -application command line argument.
*
*/
protected void doApplication() {
// nothing to do right now - these are argumnents for eclipse applications
}
/**
* Creates the OSGi framework object.
*
* @param The FrameworkAdaptor object
*/
protected 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
*/
protected 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("Invalid console 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("Warning: The requested component '" + component + "' is not included in this runtime.");
System.out.println(" Add '" + jar + "' to the classpath or rebuild the jxe with it.");
System.out.println();
}
}