blob: a2e95c414d3ef4d7fee0b04db91fcb326d9cdcc9 [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.karaf.eik.felix;
import org.apache.karaf.eik.felix.internal.BundleEntry;
import org.apache.karaf.eik.felix.internal.FelixLaunchHelper;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.StringTokenizer;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.FileLocator;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.variables.IStringVariableManager;
import org.eclipse.core.variables.VariablesPlugin;
import org.eclipse.debug.core.ILaunch;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IPackageFragmentRoot;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.launching.ExecutionArguments;
import org.eclipse.jdt.launching.IJavaLaunchConfigurationConstants;
import org.eclipse.pde.core.plugin.IPluginModelBase;
import org.eclipse.pde.core.plugin.PluginRegistry;
import org.eclipse.pde.ui.launcher.AbstractPDELaunchConfiguration;
import org.eclipse.pde.ui.launcher.IPDELauncherConstants;
import org.osgi.framework.Bundle;
public class FelixLaunchConfiguration extends AbstractPDELaunchConfiguration {
public static final String FELIX_FRAMEWORK_LAUNCHER = "org.apache.felix.main";
public static final String FELIX_FRAMEWORK = "org.apache.felix.framework";
public static final String FELIX_SYSTEM_PROPERTIES_KEY = "felix.system.properties";
public static final String FELIX_CONFIG_PROPERTIES_KEY = "felix.config.properties";
public static final String FELIX_AUTO_START_PREFIX = "felix.auto.start.";
public static final String FELIX_AUTO_INSTALL_PREFIX = "felix.auto.install.";
public static final String FELIX_CONFIG_PROPERTIES_FILE = "config.properties";
public static final String FELIX_SYSTEM_PROPERTIES_FILE = "system.properties";
public static final int DEFAULT_START_LEVEL = 4;
public static final boolean DEFAULT_AUTOSTART = true;
/**
* Implementation of join using a {@link StringBuffer}
*
* @param items
* the {@link Collection} of items that will be joined together
* @param glue
* the string to act as glue in the concatenation
* @return the concatenation of the specified items
*/
public static String join(Collection<String> items, String glue) {
final StringBuffer buffer = new StringBuffer();
for (String s : items) {
if (buffer.length() > 0) {
buffer.append(glue);
}
buffer.append(s);
}
return buffer.toString();
}
/**
* Gets the path to the specified bundle in the following manner:<br>
* <br>
* <ol>
* <li>If the bundle is found in the Plug-in Registry and is not a workspace
* resource, return the path to the bundle</li>
* <li>If the bundle is in the Plug-in Registry but is a workspace resource,
* return the path to the path to the output location that contains the
* package specified ({@code project/output folder})</li>
* <li>If the bundle is not found in the Plug-in Registry then look for it
* in the OSGi platform</li>
* </ol>
*
* @param bundleName
* the symbolic name of the bundle
* @param packageName
* the name of the package used to locate the output folder
* @return a fully qualified path to the requested bundle or null if it does
* not exist
* @throws CoreException
*/
private static String getBundlePath(String bundleName, String packageName) throws CoreException {
final IPluginModelBase model = PluginRegistry.findModel(bundleName);
if (model != null) {
final IResource resource = model.getUnderlyingResource();
if (!isWorkspaceModel(model)) {
return model.getInstallLocation();
}
final IProject project = resource.getProject();
if (project.hasNature(JavaCore.NATURE_ID)) {
final IJavaProject jProject = JavaCore.create(project);
final IClasspathEntry[] entries = jProject.getRawClasspath();
for (int i = 0; i < entries.length; i++) {
final int kind = entries[i].getEntryKind();
if (kind == IClasspathEntry.CPE_SOURCE || kind == IClasspathEntry.CPE_LIBRARY) {
final IPackageFragmentRoot[] roots = jProject.findPackageFragmentRoots(entries[i]);
for (int j = 0; j < roots.length; j++) {
if (roots[j].getPackageFragment(packageName).exists()) {
// if source folder, find the output folder
if (kind == IClasspathEntry.CPE_SOURCE) {
IPath path = entries[i].getOutputLocation();
if (path == null) {
path = jProject.getOutputLocation();
}
path = path.removeFirstSegments(1);
return project.getLocation().append(path).toOSString();
}
// else if is a library jar, then get the
// location of the jar itself
final IResource jar = roots[j].getResource();
if (jar != null) {
return jar.getLocation().toOSString();
}
}
}
}
}
}
}
final Bundle bundle = Platform.getBundle(bundleName);
if (bundle != null) {
try {
URL url = FileLocator.resolve(bundle.getEntry("/")); //$NON-NLS-1$
url = FileLocator.toFileURL(url);
String path = url.getFile();
if (path.startsWith("file:")) { //$NON-NLS-1$
path = path.substring(5);
}
path = new File(path).getAbsolutePath();
if (path.endsWith("!")) { //$NON-NLS-1$
path = path.substring(0, path.length() - 1);
}
return path;
} catch (IOException e) {
}
}
return null;
}
/**
* Uses the Eclipse Variables plugin to perform string substitution on the
* input string
*
* @param text
* the input string that potentially contains variables that need
* to be resolved
* @return the fully interpolated string
* @throws CoreException
*/
private static String getSubstitutedString(String text) throws CoreException {
if (text == null) {
return ""; //$NON-NLS-1$
}
final IStringVariableManager mgr = VariablesPlugin.getDefault().getStringVariableManager();
return mgr.performStringSubstitution(text);
}
/**
* Determines if the {@link IPluginModelBase} is a workspace resource
*
* @param model
* the {@code IPluginModelBase} to test
* @return true if it is a workspace resource, false otherwise
*/
private static boolean isWorkspaceModel(IPluginModelBase model) {
return (model.getUnderlyingResource() != null);
}
/**
* Helper method to write a properties file to the specified filename
*
* @param filename
* the fully qualfied filename
* @param props
* the {@link Properties} to be stored
* @throws CoreException
*/
private static void writeProperties(String filename, Properties props) throws CoreException {
try {
final OutputStream out = new FileOutputStream(new File(filename));
props.store(out, FelixLaunchConfiguration.class.getName());
out.close();
} catch (IOException e) {
}
}
// used to generate the dev classpath entries
// key is bundle ID, value is a model
protected Map<String, IPluginModelBase> fAllBundles;
// key is a model, value is startLevel:autoStart
private Map<IPluginModelBase, String> fModels;
/*
* Apache Felix requires that the launcher and the Felix Framework be on the
* initial classpath.
*
* @see
* org.eclipse.pde.ui.launcher.AbstractPDELaunchConfiguration#getClasspath
* (org.eclipse.debug.core.ILaunchConfiguration)
*/
@Override
public String[] getClasspath(ILaunchConfiguration configuration) throws CoreException {
final String felixLauncherPath = getBundlePath(FELIX_FRAMEWORK_LAUNCHER, FELIX_FRAMEWORK_LAUNCHER);
if (felixLauncherPath == null) {
}
final String felixFrameworkPath = getBundlePath(FELIX_FRAMEWORK, FELIX_FRAMEWORK);
if (felixFrameworkPath == null) {
}
final List<String> entries = new ArrayList<String>();
entries.add(felixLauncherPath);
entries.add(felixFrameworkPath);
/*
* Add any boostrap entries provided by the user
*/
final String bootstrap = configuration.getAttribute(IPDELauncherConstants.BOOTSTRAP_ENTRIES, ""); //$NON-NLS-1$
final StringTokenizer tok = new StringTokenizer(getSubstitutedString(bootstrap), ","); //$NON-NLS-1$
while (tok.hasMoreTokens()) {
entries.add(tok.nextToken().trim());
}
return entries.toArray(new String[entries.size()]);
}
@Override
public String getMainClass() {
return "org.apache.felix.main.Main"; //$NON-NLS-1$
}
/*
* Gets any additional program arguments from the user and writes all Felix
* configuration files
*
* @seeorg.eclipse.pde.ui.launcher.AbstractPDELaunchConfiguration#
* getProgramArguments(org.eclipse.debug.core.ILaunchConfiguration)
*/
@Override
public String[] getProgramArguments(ILaunchConfiguration configuration) throws CoreException {
String args = configuration.getAttribute(IJavaLaunchConfigurationConstants.ATTR_PROGRAM_ARGUMENTS, (String) null);
args = args == null ? "" : getSubstitutedString(args); //$NON-NLS-1$
writeConfigProperties(configuration);
writeSystemProperties(configuration);
writeStartupProperties(configuration);
return new ExecutionArguments("", args).getProgramArgumentsArray(); //$NON-NLS-1$
}
/*
* Adds the base VM arguments and root system properties used to load the
* Felix configuration files.
*
* @see
* org.eclipse.pde.ui.launcher.AbstractPDELaunchConfiguration#getVMArguments
* (org.eclipse.debug.core.ILaunchConfiguration)
*/
@Override
public String[] getVMArguments(ILaunchConfiguration configuration) throws CoreException {
String args = configuration.getAttribute(IJavaLaunchConfigurationConstants.ATTR_VM_ARGUMENTS, (String) null);
args = args == null ? "" : getSubstitutedString(args); //$NON-NLS-1$
final IPath configProperties = new Path(new File(getConfigDir(configuration), FELIX_CONFIG_PROPERTIES_FILE).getAbsolutePath());
final IPath systemProperties = new Path(new File(getConfigDir(configuration), FELIX_SYSTEM_PROPERTIES_FILE).getAbsolutePath());
final String[] vmArgs = new ExecutionArguments("", args).getVMArgumentsArray(); //$NON-NLS-1$
// arraycopy works for JDK 5 & 6
final String[] newVmArgs = new String[vmArgs.length + 2];
System.arraycopy(vmArgs, 0, newVmArgs, 0, vmArgs.length);
try {
// TODO: Properly merge arguments in
newVmArgs[vmArgs.length] = "-D" + FELIX_CONFIG_PROPERTIES_KEY + "=" + configProperties.toFile().toURI().toURL();
newVmArgs[vmArgs.length + 1] = "-D" + FELIX_SYSTEM_PROPERTIES_KEY + "=" + systemProperties.toFile().toURI().toURL();
} catch (MalformedURLException e) {
}
return newVmArgs;
}
/**
* Writes out the configuration properties including bundle start and
* install values
*
* @param configuration
* the launch configuration
* @throws CoreException
*/
private void writeConfigProperties(ILaunchConfiguration configuration) throws CoreException {
final File f = new File(getConfigDir(configuration), FELIX_CONFIG_PROPERTIES_FILE);
final boolean defaultAutostart = configuration.getAttribute(IPDELauncherConstants.DEFAULT_AUTO_START, DEFAULT_AUTOSTART);
final int defaultStart = configuration.getAttribute(IPDELauncherConstants.DEFAULT_START_LEVEL, DEFAULT_START_LEVEL);
final Properties properties = new Properties();
properties.put("osgi.bundles.defaultStartLevel", Integer.toString(defaultStart)); //$NON-NLS-1$
properties.put("org.osgi.framework.startlevel.beginning", Integer.toString(defaultStart)); //$NON-NLS-1$
final Map<String, List<String>> startEntries = new HashMap<String, List<String>>();
final Map<String, List<String>> installEntries = new HashMap<String, List<String>>();
for (IPluginModelBase model : fModels.keySet()) {
final String startString = fModels.get(model);
final int colonPos = startString.indexOf(":");
Integer startLevel;
String s = startString.substring(0, colonPos);
if ("default".equals(s)) {
startLevel = defaultStart;
} else {
startLevel = new Integer(s);
}
boolean autostart;
s = startString.substring(colonPos + 1);
if ("default".equals(s)) {
autostart = defaultAutostart;
} else {
autostart = new Boolean(s);
}
List<String> entries;
if (autostart) {
entries = startEntries.get(FELIX_AUTO_START_PREFIX + startLevel.toString());
if (entries == null) {
entries = new ArrayList<String>();
startEntries.put(FELIX_AUTO_START_PREFIX + startLevel.toString(), entries);
}
} else {
entries = startEntries.get(FELIX_AUTO_INSTALL_PREFIX + startLevel.toString());
if (entries == null) {
entries = new ArrayList<String>();
installEntries.put(FELIX_AUTO_INSTALL_PREFIX + startLevel.toString(), entries);
}
}
try {
final File bundle = new File(model.getBundleDescription().getLocation());
entries.add(bundle.toURI().toURL().toString());
} catch (MalformedURLException e) {
}
}
for (Map.Entry<String, List<String>> e : startEntries.entrySet()) {
properties.put(e.getKey(), join(e.getValue(), " "));
}
for (Map.Entry<String, List<String>> e : installEntries.entrySet()) {
properties.put(e.getKey(), join(e.getValue(), " "));
}
writeProperties(f.getAbsolutePath(), properties);
}
private void writeStartupProperties(ILaunchConfiguration configuration) throws CoreException {
final File f = new File(getConfigDir(configuration), "startup.properties"); //$NON-NLS-1$
final Properties properties = new Properties();
writeProperties(f.getAbsolutePath(), properties);
}
private void writeSystemProperties(ILaunchConfiguration configuration) throws CoreException {
final File f = new File(getConfigDir(configuration), FELIX_SYSTEM_PROPERTIES_FILE);
final Properties properties = new Properties();
writeProperties(f.getAbsolutePath(), properties);
}
/*
* Sets up the initial map of bundles.
*
* @see
* org.eclipse.pde.ui.launcher.AbstractPDELaunchConfiguration#preLaunchCheck
* (org.eclipse.debug.core.ILaunchConfiguration,
* org.eclipse.debug.core.ILaunch,
* org.eclipse.core.runtime.IProgressMonitor)
*/
@Override
protected void preLaunchCheck(ILaunchConfiguration configuration, ILaunch launch, IProgressMonitor monitor) throws CoreException {
final String workspace = configuration.getAttribute(IPDELauncherConstants.WORKSPACE_BUNDLES, ""); //$NON-NLS-1$
final String target = configuration.getAttribute(IPDELauncherConstants.TARGET_BUNDLES, ""); //$NON-NLS-1$
final List<BundleEntry> workspaceBundles = FelixLaunchHelper.getBundles(workspace);
final List<BundleEntry> targetBundles = FelixLaunchHelper.getBundles(target);
fModels = new HashMap<IPluginModelBase, String>();
for(BundleEntry e : workspaceBundles) {
final IPluginModelBase model = FelixLaunchHelper.resolveWorkspaceBundleEntry(e);
if(model == null) {
continue;
}
fModels.put(model, e.getAutostart() + ":" + e.getStartLevel()); //$NON-NLS-1$
}
if (configuration.getAttribute(IPDELauncherConstants.AUTOMATIC_ADD, true)) {
automaticallyAddWorkspaceBundles(configuration);
}
for(BundleEntry e : targetBundles) {
final IPluginModelBase model = FelixLaunchHelper.resolveTargetBundleEntry(e);
if(model == null) {
continue;
}
fModels.put(model, e.getAutostart() + ":" + e.getStartLevel()); //$NON-NLS-1$
}
fAllBundles = new HashMap<String, IPluginModelBase>(fModels.size());
final Iterator<IPluginModelBase> iter = fModels.keySet().iterator();
while (iter.hasNext()) {
final IPluginModelBase model = iter.next();
fAllBundles.put(model.getPluginBase().getId(), model);
}
super.preLaunchCheck(configuration, launch, monitor);
}
private void automaticallyAddWorkspaceBundles(ILaunchConfiguration configuration) throws CoreException {
final Set<IPluginModelBase> deselectedPlugins = FelixLaunchHelper.getDeselectedPluginSet(configuration);
final IPluginModelBase[] models = PluginRegistry.getWorkspaceModels();
for (int i = 0; i < models.length; i++) {
final String id = models[i].getPluginBase().getId();
if (id == null) {
continue;
}
if (!deselectedPlugins.contains(models[i])) {
if (!fModels.containsKey(models[i])) {
fModels.put(models[i], "default:default"); //$NON-NLS-1$
}
}
}
}
}