Java tutorial
/** * Copyright (c) 2009 Stephen Evanchik * 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: * Stephen Evanchik - initial implementation */ package info.evanchik.eclipse.felix; import info.evanchik.eclipse.felix.internal.BundleEntry; import info.evanchik.eclipse.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.Arrays; 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; /** * @author Stephen Evanchik (evanchsa@gmail.com) * */ public class FelixLaunchConfiguration extends AbstractPDELaunchConfiguration { public static String FELIX_FRAMEWORK_LAUNCHER = "org.apache.felix.main"; //$NON-NLS-1$ public static String FELIX_FRAMEWORK = "org.apache.felix.framework"; //$NON-NLS-1$ public static String FELIX_SYSTEM_PROPERTIES_KEY = "felix.system.properties"; //$NON-NLS-1$ public static String FELIX_CONFIG_PROPERTIES_KEY = "felix.config.properties"; //$NON-NLS-1$ public static String FELIX_AUTO_START_PREFIX = "felix.auto.start."; //$NON-NLS-1$ public static String FELIX_AUTO_INSTALL_PREFIX = "felix.auto.install."; //$NON-NLS-1$ public static String FELIX_CONFIG_PROPERTIES_FILE = "config.properties"; //$NON-NLS-1$ public static String FELIX_SYSTEM_PROPERTIES_FILE = "system.properties"; //$NON-NLS-1$ public static int DEFAULT_START_LEVEL = 4; public static 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$ } } } } }