1 package org
.argeo
.slc
.ide
.ui
.launch
.osgi
;
4 import java
.io
.IOException
;
5 import java
.io
.InputStream
;
6 import java
.util
.ArrayList
;
7 import java
.util
.HashMap
;
8 import java
.util
.Iterator
;
11 import java
.util
.Properties
;
13 import java
.util
.StringTokenizer
;
14 import java
.util
.TreeMap
;
15 import java
.util
.TreeSet
;
17 import org
.argeo
.slc
.ide
.ui
.SlcIdeUiPlugin
;
18 import org
.eclipse
.core
.resources
.IFile
;
19 import org
.eclipse
.core
.resources
.IFolder
;
20 import org
.eclipse
.core
.resources
.IProject
;
21 import org
.eclipse
.core
.resources
.IResource
;
22 import org
.eclipse
.core
.runtime
.Assert
;
23 import org
.eclipse
.core
.runtime
.CoreException
;
24 import org
.eclipse
.core
.runtime
.IPath
;
25 import org
.eclipse
.core
.runtime
.IStatus
;
26 import org
.eclipse
.core
.runtime
.Status
;
27 import org
.eclipse
.core
.variables
.IStringVariableManager
;
28 import org
.eclipse
.core
.variables
.VariablesPlugin
;
29 import org
.eclipse
.debug
.core
.ILaunchConfiguration
;
30 import org
.eclipse
.debug
.core
.ILaunchConfigurationWorkingCopy
;
31 import org
.eclipse
.jdt
.launching
.IJavaLaunchConfigurationConstants
;
32 import org
.eclipse
.jdt
.launching
.IVMInstall
;
33 import org
.eclipse
.jdt
.launching
.IVMInstall2
;
34 import org
.eclipse
.jdt
.launching
.IVMInstallType
;
35 import org
.eclipse
.jdt
.launching
.JavaRuntime
;
36 import org
.eclipse
.jface
.dialogs
.ErrorDialog
;
37 import org
.eclipse
.jface
.viewers
.ISelection
;
38 import org
.eclipse
.jface
.viewers
.IStructuredSelection
;
39 import org
.eclipse
.osgi
.service
.resolver
.BundleDescription
;
40 import org
.eclipse
.pde
.core
.plugin
.IPluginModelBase
;
41 import org
.eclipse
.pde
.core
.plugin
.PluginRegistry
;
42 import org
.eclipse
.pde
.internal
.build
.IPDEBuildConstants
;
43 import org
.eclipse
.pde
.launching
.IPDELauncherConstants
;
44 import org
.eclipse
.swt
.widgets
.Display
;
45 import org
.eclipse
.swt
.widgets
.Shell
;
48 * Most of the actual logic is concentrated in this class which manipulates
49 * {@link ILaunchConfigurationWorkingCopy}. Static method are used since the
50 * shortcut and launch configuration classes are already extending PDE classes.
52 @SuppressWarnings("restriction")
53 public class OsgiLaunchHelper
implements OsgiLauncherConstants
{
54 private static Boolean debug
= true;
56 private final static String DEFAULT_DATA_DIR
= "data";
57 private final static String DEFAULT_EXEC_DIR
= "exec";
58 private final static String DEFAULT_VMARGS
= "-Xmx256m";
59 private final static String DEFAULT_PROGRAM_ARGS
= "-console";
61 /** Sets default values on this configuration. */
62 public static void setDefaults(ILaunchConfigurationWorkingCopy wc
,
66 wc
.setAttribute(IPDELauncherConstants
.USE_DEFAULT
, false);
67 wc
.setAttribute(IPDELauncherConstants
.USE_PRODUCT
, false);
70 wc
.setAttribute(ATTR_ADD_JVM_PATHS
, false);
71 wc
.setAttribute(ATTR_ADDITIONAL_VM_ARGS
, DEFAULT_VMARGS
);
72 wc
.setAttribute(ATTR_ADDITIONAL_PROGRAM_ARGS
, DEFAULT_PROGRAM_ARGS
);
75 String originalVmArgs
= wc
.getAttribute(
76 IJavaLaunchConfigurationConstants
.ATTR_VM_ARGUMENTS
, "");
77 wc
.setAttribute(ATTR_DEFAULT_VM_ARGS
, originalVmArgs
);
79 // do NOT use custom features (both must be set)
80 wc
.setAttribute(IPDELauncherConstants
.USE_CUSTOM_FEATURES
, false);
81 wc
.setAttribute(IPDELauncherConstants
.USE_DEFAULT
, true);
83 // clear config area by default
84 wc
.setAttribute(IPDELauncherConstants
.CONFIG_CLEAR_AREA
, true);
85 } catch (CoreException e
) {
86 Shell shell
= Display
.getCurrent().getActiveShell();
87 ErrorDialog
.openError(shell
, "Error",
88 "Cannot execute initalize configuration", e
.getStatus());
92 /** Find the working directory based on this properties file. */
93 public static String
findWorkingDirectory(IFile propertiesFile
) {
95 IProject project
= propertiesFile
.getProject();
96 IPath parent
= propertiesFile
.getProjectRelativePath()
97 .removeLastSegments(1);
98 IFolder execFolder
= project
.getFolder(parent
99 .append(DEFAULT_EXEC_DIR
));
100 if (!execFolder
.exists())
101 execFolder
.create(true, true, null);
102 IFolder launchFolder
= project
.getFolder(execFolder
103 .getProjectRelativePath().append(
104 extractName(propertiesFile
)));
105 if (!launchFolder
.exists())
106 launchFolder
.create(true, true, null);
107 return "${workspace_loc:"
108 + launchFolder
.getFullPath().toString().substring(1) + "}";
109 } catch (Exception e
) {
111 throw new RuntimeException("Cannot create working directory", e
);
115 /** Extract the launch configuration name from the properties file. */
116 public static String
extractName(IFile propertiesFile
) {
117 IPath path
= propertiesFile
.getFullPath();
118 IPath pathNoExt
= path
.removeFileExtension();
119 return pathNoExt
.segment(pathNoExt
.segmentCount() - 1);
123 /** Expects properties file to be set as mapped resources */
124 public static void updateLaunchConfiguration(
125 ILaunchConfigurationWorkingCopy wc
, Boolean isEclipse
) {
127 // Finds the properties file and load it
128 IFile propertiesFile
= (IFile
) wc
.getMappedResources()[0];
129 propertiesFile
.refreshLocal(IResource
.DEPTH_ONE
, null);
130 Properties properties
= readProperties(propertiesFile
);
132 // Extract information from the properties file
133 Map
<String
, Integer
> bundlesToStart
= new TreeMap
<String
, Integer
>();
134 Map
<String
, String
> systemPropertiesToAppend
= new HashMap
<String
, String
>();
135 String applicationId
= interpretProperties(properties
,
136 bundlesToStart
, systemPropertiesToAppend
);
138 if (applicationId
!= null)
139 wc
.setAttribute(IPDELauncherConstants
.APPLICATION
,
143 throw new Exception("No application defined,"
144 + " please set the 'eclipse.application' property"
145 + " in the properties file");
148 // Define directories
149 File workingDir
= getWorkingDirectory(wc
);
150 File dataDir
= new File(workingDir
, DEFAULT_DATA_DIR
);
152 // Update the launch configuration accordingly
153 updateLaunchConfiguration(wc
, bundlesToStart
,
154 systemPropertiesToAppend
, dataDir
.getAbsolutePath(),
156 } catch (Exception e
) {
158 Shell shell
= SlcIdeUiPlugin
.getDefault().getWorkbench()
159 .getActiveWorkbenchWindow().getShell();
160 // Shell shell= Display.getCurrent().getActiveShell();
161 ErrorDialog
.openError(shell
, "Error",
162 "Cannot prepare launch configuration",
163 new Status(IStatus
.ERROR
, SlcIdeUiPlugin
.ID
,
170 * Actually modifies the launch configuration in order to reflect the
171 * current state read from the properties file and the launch configuration
174 protected static void updateLaunchConfiguration(
175 ILaunchConfigurationWorkingCopy wc
,
176 Map
<String
, Integer
> bundlesToStart
,
177 Map
<String
, String
> systemPropertiesToAppend
, String dataDir
,
178 Boolean isEclipse
) throws CoreException
{
179 // Convert bundle lists
180 final String targetBundles
;
181 final String wkSpaceBundles
;
182 if (wc
.getAttribute(ATTR_SYNC_BUNDLES
, true)) {
183 StringBuffer tBuf
= new StringBuffer();
184 for (IPluginModelBase model
: PluginRegistry
.getExternalModels()) {
185 tBuf
.append(model
.getBundleDescription().getSymbolicName());
188 targetBundles
= tBuf
.toString();
189 StringBuffer wBuf
= new StringBuffer();
190 models
: for (IPluginModelBase model
: PluginRegistry
191 .getWorkspaceModels()) {
192 if (model
.getBundleDescription() == null) {
193 System
.err
.println("No bundle description for " + model
);
196 wBuf
.append(model
.getBundleDescription().getSymbolicName());
199 wkSpaceBundles
= wBuf
.toString();
201 targetBundles
= wc
.getAttribute(targetBundlesAttr(isEclipse
), "");
202 wkSpaceBundles
= wc
.getAttribute(workspaceBundlesAttr(isEclipse
),
205 wc
.setAttribute(targetBundlesAttr(isEclipse
),
206 convertBundleList(bundlesToStart
, targetBundles
));
208 wc
.setAttribute(workspaceBundlesAttr(isEclipse
),
209 convertBundleList(bundlesToStart
, wkSpaceBundles
));
211 // Update other default information
212 wc
.setAttribute(IPDELauncherConstants
.DEFAULT_AUTO_START
, false);
214 // do NOT use custom features (both must be set)
215 wc
.setAttribute(IPDELauncherConstants
.USE_CUSTOM_FEATURES
, false);
216 wc
.setAttribute(IPDELauncherConstants
.USE_DEFAULT
, true);
218 // VM arguments (system properties)
219 String defaultVmArgs
= wc
.getAttribute(
220 OsgiLauncherConstants
.ATTR_DEFAULT_VM_ARGS
, "");
221 StringBuffer vmArgs
= new StringBuffer(defaultVmArgs
);
223 // Data dir system property
224 if (dataDir
!= null) {
225 addSysProperty(vmArgs
, OsgiLauncherConstants
.ARGEO_OSGI_DATA_DIR
,
228 wc
.setAttribute(IPDELauncherConstants
.LOCATION
, dataDir
);
232 // Add locations of JVMs
233 if (wc
.getAttribute(ATTR_ADD_JVM_PATHS
, false))
236 // Add other system properties
237 for (String key
: systemPropertiesToAppend
.keySet())
238 addSysProperty(vmArgs
, key
, systemPropertiesToAppend
.get(key
));
240 vmArgs
.append(" ").append(wc
.getAttribute(ATTR_ADDITIONAL_VM_ARGS
, ""));
242 wc
.setAttribute(IJavaLaunchConfigurationConstants
.ATTR_VM_ARGUMENTS
,
246 StringBuffer progArgs
= new StringBuffer("");
247 if (dataDir
!= null) {
248 progArgs
.append("-data ");
249 progArgs
.append(surroundSpaces(dataDir
));
251 if (wc
.getAttribute(ATTR_CLEAR_DATA_DIRECTORY
, false)) {
252 File dataDirFile
= new File(dataDir
);
253 deleteDir(dataDirFile
);
254 dataDirFile
.mkdirs();
257 String additionalProgramArgs
= wc
.getAttribute(
258 OsgiLauncherConstants
.ATTR_ADDITIONAL_PROGRAM_ARGS
, "");
259 progArgs
.append(' ').append(additionalProgramArgs
);
261 IJavaLaunchConfigurationConstants
.ATTR_PROGRAM_ARGUMENTS
,
262 progArgs
.toString());
265 /** The launch configuration attribute to use for target bundles */
266 protected static String
targetBundlesAttr(Boolean isEclipse
) {
267 return isEclipse ? IPDELauncherConstants
.SELECTED_TARGET_PLUGINS
268 : IPDELauncherConstants
.TARGET_BUNDLES
;
271 /** The launch configuration attribute to use for workspace bundles */
272 protected static String
workspaceBundlesAttr(Boolean isEclipse
) {
273 return isEclipse ? IPDELauncherConstants
.SELECTED_WORKSPACE_PLUGINS
274 : IPDELauncherConstants
.WORKSPACE_BUNDLES
;
278 * Interprets special properties and register the others as system
279 * properties to append.
281 * @return the application id defined by
282 * {@link OsgiLauncherConstants#ECLIPSE_APPLICATION}, or null if not
285 protected static String
interpretProperties(Properties properties
,
286 Map
<String
, Integer
> bundlesToStart
,
287 Map
<String
, String
> systemPropertiesToAppend
) {
288 // String argeoOsgiStart = properties
289 // .getProperty(OsgiLauncherConstants.ARGEO_OSGI_START);
290 // if (argeoOsgiStart != null) {
291 // StringTokenizer st = new StringTokenizer(argeoOsgiStart, ",");
292 // while (st.hasMoreTokens())
293 // bundlesToStart.add(st.nextToken());
296 computeBundlesToStart(bundlesToStart
, properties
, null);
298 String applicationId
= null;
299 propKeys
: for (Object keyObj
: properties
.keySet()) {
300 String key
= keyObj
.toString();
301 if (OsgiLauncherConstants
.ARGEO_OSGI_START
.equals(key
))
303 if (key
.startsWith(OsgiLauncherConstants
.ARGEO_OSGI_START
+ "."))
305 else if (OsgiLauncherConstants
.ARGEO_OSGI_BUNDLES
.equals(key
))
307 else if (OsgiLauncherConstants
.ARGEO_OSGI_LOCATIONS
.equals(key
))
309 else if (OsgiLauncherConstants
.OSGI_BUNDLES
.equals(key
))
311 else if (OsgiLauncherConstants
.ECLIPSE_APPLICATION
.equals(key
))
312 applicationId
= properties
.getProperty(key
);
314 systemPropertiesToAppend
.put(key
, properties
.getProperty(key
));
316 return applicationId
;
319 /** Adds a regular system property. */
320 protected static void addSysProperty(StringBuffer vmArgs
, String key
,
322 surroundSpaces(value
);
323 String str
= "-D" + key
+ "=" + value
;
324 vmArgs
.append(' ').append(str
);
327 /** Adds JVMS registered in the workspace as special system properties. */
328 protected static void addVms(StringBuffer vmArgs
) {
329 addVmSysProperty(vmArgs
, "default", JavaRuntime
.getDefaultVMInstall());
330 IVMInstallType
[] vmTypes
= JavaRuntime
.getVMInstallTypes();
331 for (IVMInstallType vmType
: vmTypes
) {
332 for (IVMInstall vmInstall
: vmType
.getVMInstalls()) {
333 // printVm("", vmInstall);
334 // properties based on name
335 addVmSysProperty(vmArgs
, vmInstall
.getName(), vmInstall
);
336 if (vmInstall
instanceof IVMInstall2
) {
337 // properties based on version
338 IVMInstall2 vmInstall2
= (IVMInstall2
) vmInstall
;
339 String version
= vmInstall2
.getJavaVersion();
340 addVmSysProperty(vmArgs
, version
, vmInstall
);
342 List
<String
> tokens
= new ArrayList
<String
>();
343 StringTokenizer st
= new StringTokenizer(version
, ".");
344 while (st
.hasMoreTokens())
345 tokens
.add(st
.nextToken());
346 if (tokens
.size() >= 2)
347 addVmSysProperty(vmArgs
,
348 tokens
.get(0) + "." + tokens
.get(1), vmInstall
);
355 /** Adds a special system property pointing to one of the registered JVMs. */
356 protected static void addVmSysProperty(StringBuffer vmArgs
, String suffix
,
357 IVMInstall vmInstall
) {
358 addSysProperty(vmArgs
, OsgiLauncherConstants
.VMS_PROPERTY_PREFIX
+ "."
359 + suffix
, vmInstall
.getInstallLocation().getPath());
362 /** Surround the string with quotes if it contains spaces. */
363 protected static String
surroundSpaces(String str
) {
364 if (str
.indexOf(' ') >= 0)
365 return '\"' + str
+ '\"';
371 * Reformat the bundle list in order to reflect which bundles have to be
374 protected static String
convertBundleList(
375 Map
<String
, Integer
> bundlesToStart
, String original
) {
377 debug("Original bundle list: " + original
);
379 StringTokenizer stComa
= new StringTokenizer(original
, ",");
380 // sort by bundle symbolic name
381 Set
<String
> bundleIds
= new TreeSet
<String
>();
382 bundles
: while (stComa
.hasMoreTokens()) {
384 String bundleId
= stComa
.nextToken();
385 if (bundleId
.indexOf('*') >= 0)
386 throw new RuntimeException(
389 + " not properly formatted, clean your workspace projects");
391 int indexAt
= bundleId
.indexOf('@');
393 bundleId
= bundleId
.substring(0, indexAt
);
396 // We can now rely on bundleId value
398 if (bundleId
.endsWith(".source")) {
399 debug("Skip source bundle " + bundleId
);
402 .equals(IPDEBuildConstants
.BUNDLE_SIMPLE_CONFIGURATOR
)) {
403 // skip simple configurator in order to avoid side-effects
406 bundleIds
.add(bundleId
);
409 StringBuffer bufBundles
= new StringBuffer(1024);
410 boolean first
= true;
411 for (String bundleId
: bundleIds
) {
415 bufBundles
.append(',');
416 boolean modified
= false;
417 if (bundlesToStart
.containsKey(bundleId
)) {
418 Integer startLevel
= bundlesToStart
.get(bundleId
);
419 String startLevelStr
= startLevel
!= null ? startLevel
420 .toString() : "default";
421 bufBundles
.append(bundleId
).append('@').append(startLevelStr
)
424 debug("Will start " + bundleId
+ " at level " + startLevelStr
);
428 bufBundles
.append(bundleId
);
431 String output
= bufBundles
.toString();
436 /** Recursively deletes a directory tree. */
437 private static void deleteDir(File dir
) {
438 File
[] files
= dir
.listFiles();
439 for (File file
: files
) {
440 if (file
.isDirectory())
448 /** Loads a properties file. */
449 private static Properties
readProperties(IFile file
) throws CoreException
{
450 Properties props
= new Properties();
452 InputStream in
= null;
454 in
= file
.getContents();
456 } catch (Exception e
) {
457 throw new CoreException(new Status(IStatus
.ERROR
,
458 SlcIdeUiPlugin
.ID
, "Cannot read properties file", e
));
463 } catch (IOException e
) {
470 /** Determines the start levels for the bundles */
471 private static void computeBundlesToStart(
472 Map
<String
, Integer
> bundlesToStart
, Properties properties
,
473 Integer defaultStartLevel
) {
475 // default (and previously, only behaviour)
476 appendBundlesToStart(bundlesToStart
, defaultStartLevel
,
477 properties
.getProperty(OsgiLauncherConstants
.ARGEO_OSGI_START
,
480 // list argeo.osgi.start.* system properties
481 Iterator
<Object
> keys
= properties
.keySet().iterator();
482 final String prefix
= OsgiLauncherConstants
.ARGEO_OSGI_START
+ ".";
483 while (keys
.hasNext()) {
484 String key
= (String
) keys
.next();
485 if (key
.startsWith(prefix
)) {
487 String suffix
= key
.substring(prefix
.length());
488 String
[] tokens
= suffix
.split("\\.");
489 if (tokens
.length
> 0 && !tokens
[0].trim().equals(""))
491 // first token is start level
492 startLevel
= new Integer(tokens
[0]);
493 } catch (NumberFormatException e
) {
494 startLevel
= defaultStartLevel
;
497 startLevel
= defaultStartLevel
;
499 // append bundle names
500 String bundleNames
= properties
.getProperty(key
);
501 appendBundlesToStart(bundlesToStart
, startLevel
, bundleNames
);
506 /** Append a comma-separated list of bundles to the start levels. */
507 private static void appendBundlesToStart(
508 Map
<String
, Integer
> bundlesToStart
, Integer startLevel
, String str
) {
509 if (str
== null || str
.trim().equals(""))
512 String
[] bundleNames
= str
.split(",");
513 for (int i
= 0; i
< bundleNames
.length
; i
++) {
514 if (bundleNames
[i
] != null && !bundleNames
[i
].trim().equals(""))
515 bundlesToStart
.put(bundleNames
[i
], startLevel
);
523 // org.eclipse.pde.internal.ui.launcher.LaunchArgumentsHelper.getWorkingDirectory(ILaunchConfiguration)
524 private static File
getWorkingDirectory(ILaunchConfiguration configuration
)
525 throws CoreException
{
528 working
= configuration
.getAttribute(
529 IJavaLaunchConfigurationConstants
.ATTR_WORKING_DIRECTORY
,
530 new File(".").getCanonicalPath()); //$NON-NLS-1$
531 } catch (IOException e
) {
532 working
= "${workspace_loc}/../"; //$NON-NLS-1$
536 dir
= new File(getSubstitutedString(working
));
537 } catch (Exception e
) {
538 // the directory was most probably deleted
539 IFile propertiesFile
= (IFile
) configuration
.getMappedResources()[0];
540 working
= findWorkingDirectory(propertiesFile
);
541 dir
= new File(getSubstitutedString(working
));
549 // org.eclipse.pde.internal.ui.launcher.LaunchArgumentsHelper.getSubstitutedString(String)
550 private static String
getSubstitutedString(String text
)
551 throws CoreException
{
553 return ""; //$NON-NLS-1$
554 IStringVariableManager mgr
= VariablesPlugin
.getDefault()
555 .getStringVariableManager();
556 return mgr
.performStringSubstitution(text
);
560 * Not used anymore, but kept because this routine may be useful in the
563 protected void addSelectedProjects(StringBuffer name
, ISelection selection
,
564 List
<String
> bundlesToStart
) {
565 Assert
.isNotNull(selection
);
567 Map
<String
, IPluginModelBase
> bundleProjects
= new HashMap
<String
, IPluginModelBase
>();
568 for (IPluginModelBase modelBase
: PluginRegistry
.getWorkspaceModels()) {
569 IProject bundleProject
= modelBase
.getUnderlyingResource()
571 bundleProjects
.put(bundleProject
.getName(), modelBase
);
574 IStructuredSelection sSelection
= (IStructuredSelection
) selection
;
575 for (Iterator
<?
> it
= sSelection
.iterator(); it
.hasNext();) {
576 Object obj
= it
.next();
577 if (obj
instanceof IProject
) {
578 IProject project
= (IProject
) obj
;
579 if (bundleProjects
.containsKey(project
.getName())) {
580 IPluginModelBase modelBase
= bundleProjects
.get(project
583 BundleDescription bundleDescription
= null;
584 if (modelBase
.isFragmentModel()) {
585 BundleDescription
[] hosts
= modelBase
586 .getBundleDescription().getHost().getHosts();
587 for (BundleDescription bd
: hosts
) {
589 System
.out
.println("Host for "
590 + modelBase
.getBundleDescription()
591 .getSymbolicName() + ": "
592 + bd
.getSymbolicName());
593 bundleDescription
= bd
;
596 bundleDescription
= modelBase
.getBundleDescription();
599 if (bundleDescription
!= null) {
600 String symbolicName
= bundleDescription
602 String bundleName
= bundleDescription
.getName();
604 bundlesToStart
.add(symbolicName
);
606 if (name
.length() > 0)
608 if (bundleName
!= null)
609 name
.append(bundleName
);
611 name
.append(symbolicName
);
618 static void debug(Object obj
) {
620 System
.out
.println(obj
);