1 package org
.argeo
.slc
.ide
.ui
.launch
.osgi
;
4 import java
.io
.IOException
;
5 import java
.io
.InputStream
;
6 import java
.util
.ArrayList
;
8 import java
.util
.HashMap
;
9 import java
.util
.Iterator
;
10 import java
.util
.List
;
12 import java
.util
.Properties
;
14 import java
.util
.StringTokenizer
;
15 import java
.util
.TreeMap
;
16 import java
.util
.TreeSet
;
18 import org
.argeo
.slc
.ide
.ui
.SlcIdeUiPlugin
;
19 import org
.eclipse
.core
.resources
.IFile
;
20 import org
.eclipse
.core
.resources
.IFolder
;
21 import org
.eclipse
.core
.resources
.IProject
;
22 import org
.eclipse
.core
.resources
.IResource
;
23 import org
.eclipse
.core
.runtime
.Assert
;
24 import org
.eclipse
.core
.runtime
.CoreException
;
25 import org
.eclipse
.core
.runtime
.IPath
;
26 import org
.eclipse
.core
.runtime
.IStatus
;
27 import org
.eclipse
.core
.runtime
.Status
;
28 import org
.eclipse
.core
.variables
.IStringVariableManager
;
29 import org
.eclipse
.core
.variables
.VariablesPlugin
;
30 import org
.eclipse
.debug
.core
.ILaunchConfiguration
;
31 import org
.eclipse
.debug
.core
.ILaunchConfigurationWorkingCopy
;
32 import org
.eclipse
.jdt
.launching
.IJavaLaunchConfigurationConstants
;
33 import org
.eclipse
.jdt
.launching
.IVMInstall
;
34 import org
.eclipse
.jdt
.launching
.IVMInstall2
;
35 import org
.eclipse
.jdt
.launching
.IVMInstallType
;
36 import org
.eclipse
.jdt
.launching
.JavaRuntime
;
37 import org
.eclipse
.jface
.dialogs
.ErrorDialog
;
38 import org
.eclipse
.jface
.viewers
.ISelection
;
39 import org
.eclipse
.jface
.viewers
.IStructuredSelection
;
40 import org
.eclipse
.osgi
.service
.resolver
.BundleDescription
;
41 import org
.eclipse
.pde
.core
.plugin
.IPluginModelBase
;
42 import org
.eclipse
.pde
.core
.plugin
.PluginRegistry
;
43 import org
.eclipse
.pde
.internal
.build
.IPDEBuildConstants
;
44 import org
.eclipse
.pde
.launching
.IPDELauncherConstants
;
45 import org
.eclipse
.swt
.widgets
.Display
;
46 import org
.eclipse
.swt
.widgets
.Shell
;
49 * Most of the actual logic is concentrated in this class which manipulates
50 * {@link ILaunchConfigurationWorkingCopy}. Static method are used since the
51 * shortcut and launch configuration classes are already extending PDE classes.
53 @SuppressWarnings("restriction")
54 public class OsgiLaunchHelper
implements OsgiLauncherConstants
{
55 private static Boolean debug
= true;
57 private final static String DEFAULT_DATA_DIR
= "data";
58 private final static String DEFAULT_EXEC_DIR
= "exec";
59 private final static String DEFAULT_VMARGS
= "-Xmx256m";
60 private final static String DEFAULT_PROGRAM_ARGS
= "-console";
62 /** Sets default values on this configuration. */
63 public static void setDefaults(ILaunchConfigurationWorkingCopy wc
,
67 wc
.setAttribute(IPDELauncherConstants
.USE_DEFAULT
, false);
68 wc
.setAttribute(IPDELauncherConstants
.USE_PRODUCT
, false);
71 wc
.setAttribute(ATTR_ADD_JVM_PATHS
, false);
72 wc
.setAttribute(ATTR_ADDITIONAL_VM_ARGS
, DEFAULT_VMARGS
);
73 wc
.setAttribute(ATTR_ADDITIONAL_PROGRAM_ARGS
, DEFAULT_PROGRAM_ARGS
);
76 String originalVmArgs
= wc
.getAttribute(
77 IJavaLaunchConfigurationConstants
.ATTR_VM_ARGUMENTS
, "");
78 wc
.setAttribute(ATTR_DEFAULT_VM_ARGS
, originalVmArgs
);
80 // clear config area by default
81 wc
.setAttribute(IPDELauncherConstants
.CONFIG_CLEAR_AREA
, true);
82 } catch (CoreException e
) {
83 Shell shell
= Display
.getCurrent().getActiveShell();
84 ErrorDialog
.openError(shell
, "Error",
85 "Cannot execute initalize configuration", e
.getStatus());
89 /** Find the working directory based on this properties file. */
90 public static String
findWorkingDirectory(IFile propertiesFile
) {
92 IProject project
= propertiesFile
.getProject();
93 IPath parent
= propertiesFile
.getProjectRelativePath()
94 .removeLastSegments(1);
95 IFolder execFolder
= project
.getFolder(parent
96 .append(DEFAULT_EXEC_DIR
));
97 if (!execFolder
.exists())
98 execFolder
.create(true, true, null);
99 IFolder launchFolder
= project
.getFolder(execFolder
100 .getProjectRelativePath().append(
101 extractName(propertiesFile
)));
102 if (!launchFolder
.exists())
103 launchFolder
.create(true, true, null);
104 return "${workspace_loc:"
105 + launchFolder
.getFullPath().toString().substring(1) + "}";
106 } catch (Exception e
) {
108 throw new RuntimeException("Cannot create working directory", e
);
112 /** Extract the launch configuration name from the properties file. */
113 public static String
extractName(IFile propertiesFile
) {
114 IPath path
= propertiesFile
.getFullPath();
115 IPath pathNoExt
= path
.removeFileExtension();
116 return pathNoExt
.segment(pathNoExt
.segmentCount() - 1);
120 /** Expects properties file to be set as mapped resources */
121 @SuppressWarnings("unchecked")
122 public static void updateLaunchConfiguration(
123 ILaunchConfigurationWorkingCopy wc
, Boolean isEclipse
) {
126 debug("##\n## Launch " + wc
.getName() + " - " + new Date()
129 // Finds the properties file and load it
130 IFile propertiesFile
= (IFile
) wc
.getMappedResources()[0];
131 propertiesFile
.refreshLocal(IResource
.DEPTH_ONE
, null);
132 Properties properties
= readProperties(propertiesFile
);
134 // Extract information from the properties file
135 Map
<String
, Integer
> bundlesToStart
= new TreeMap
<String
, Integer
>();
136 Map
<String
, String
> systemPropertiesToAppend
= new HashMap
<String
, String
>();
137 String applicationId
= interpretProperties(properties
,
138 bundlesToStart
, systemPropertiesToAppend
);
140 if (applicationId
!= null)
141 wc
.setAttribute(IPDELauncherConstants
.APPLICATION
,
145 throw new Exception("No application defined,"
146 + " please set the 'eclipse.application' property"
147 + " in the properties file");
150 // Define directories
151 File workingDir
= getWorkingDirectory(wc
);
152 File dataDir
= new File(workingDir
, DEFAULT_DATA_DIR
);
154 // Update the launch configuration accordingly
155 updateLaunchConfiguration(wc
, bundlesToStart
,
156 systemPropertiesToAppend
, dataDir
.getAbsolutePath(),
160 Map
<String
, ?
> attrs
= new TreeMap
<String
, Object
>(
161 (Map
<String
, String
>) wc
.getAttributes());
162 for (String key
: attrs
.keySet())
163 OsgiLaunchHelper
.debug(key
+ "=" + attrs
.get(key
));
165 } catch (Exception e
) {
167 Shell shell
= SlcIdeUiPlugin
.getDefault().getWorkbench()
168 .getActiveWorkbenchWindow().getShell();
169 // Shell shell= Display.getCurrent().getActiveShell();
170 ErrorDialog
.openError(shell
, "Error",
171 "Cannot prepare launch configuration",
172 new Status(IStatus
.ERROR
, SlcIdeUiPlugin
.ID
,
179 * Actually modifies the launch configuration in order to reflect the
180 * current state read from the properties file and the launch configuration
183 protected static void updateLaunchConfiguration(
184 ILaunchConfigurationWorkingCopy wc
,
185 Map
<String
, Integer
> bundlesToStart
,
186 Map
<String
, String
> systemPropertiesToAppend
, String dataDir
,
187 Boolean isEclipse
) throws CoreException
{
188 // Convert bundle lists
189 final String targetBundles
;
190 final String wkSpaceBundles
;
191 if (wc
.getAttribute(ATTR_SYNC_BUNDLES
, true)) {
192 StringBuffer tBuf
= new StringBuffer();
193 for (IPluginModelBase model
: PluginRegistry
.getExternalModels()) {
194 tBuf
.append(model
.getBundleDescription().getSymbolicName());
197 targetBundles
= tBuf
.toString();
198 StringBuffer wBuf
= new StringBuffer();
199 models
: for (IPluginModelBase model
: PluginRegistry
200 .getWorkspaceModels()) {
201 if (model
.getBundleDescription() == null) {
202 System
.err
.println("No bundle description for " + model
);
205 wBuf
.append(model
.getBundleDescription().getSymbolicName());
208 wkSpaceBundles
= wBuf
.toString();
210 targetBundles
= wc
.getAttribute(targetBundlesAttr(isEclipse
), "");
211 wkSpaceBundles
= wc
.getAttribute(workspaceBundlesAttr(isEclipse
),
214 wc
.setAttribute(targetBundlesAttr(isEclipse
),
215 convertBundleList(bundlesToStart
, targetBundles
));
217 wc
.setAttribute(workspaceBundlesAttr(isEclipse
),
218 convertBundleList(bundlesToStart
, wkSpaceBundles
));
220 // Update other default information
221 wc
.setAttribute(IPDELauncherConstants
.DEFAULT_AUTO_START
, false);
223 wc
.setAttribute(IPDELauncherConstants
.USE_CUSTOM_FEATURES
, false);
225 wc
.setAttribute(IPDELauncherConstants
.USE_DEFAULT
, true);
227 // VM arguments (system properties)
228 String defaultVmArgs
= wc
.getAttribute(
229 OsgiLauncherConstants
.ATTR_DEFAULT_VM_ARGS
, "");
230 StringBuffer vmArgs
= new StringBuffer(defaultVmArgs
);
232 // Data dir system property
233 if (dataDir
!= null) {
234 addSysProperty(vmArgs
, OsgiLauncherConstants
.ARGEO_OSGI_DATA_DIR
,
237 wc
.setAttribute(IPDELauncherConstants
.LOCATION
, dataDir
);
241 // Add locations of JVMs
242 if (wc
.getAttribute(ATTR_ADD_JVM_PATHS
, false))
245 // Add other system properties
246 for (String key
: systemPropertiesToAppend
.keySet())
247 addSysProperty(vmArgs
, key
, systemPropertiesToAppend
.get(key
));
249 vmArgs
.append(" ").append(wc
.getAttribute(ATTR_ADDITIONAL_VM_ARGS
, ""));
251 wc
.setAttribute(IJavaLaunchConfigurationConstants
.ATTR_VM_ARGUMENTS
,
255 StringBuffer progArgs
= new StringBuffer("");
256 if (dataDir
!= null) {
257 progArgs
.append("-data ");
258 progArgs
.append(surroundSpaces(dataDir
));
260 if (wc
.getAttribute(ATTR_CLEAR_DATA_DIRECTORY
, false)) {
261 File dataDirFile
= new File(dataDir
);
262 deleteDir(dataDirFile
);
263 dataDirFile
.mkdirs();
266 String additionalProgramArgs
= wc
.getAttribute(
267 OsgiLauncherConstants
.ATTR_ADDITIONAL_PROGRAM_ARGS
, "");
268 progArgs
.append(' ').append(additionalProgramArgs
);
270 IJavaLaunchConfigurationConstants
.ATTR_PROGRAM_ARGUMENTS
,
271 progArgs
.toString());
274 /** The launch configuration attribute to use for target bundles */
275 protected static String
targetBundlesAttr(Boolean isEclipse
) {
276 return isEclipse ? IPDELauncherConstants
.SELECTED_TARGET_PLUGINS
277 : IPDELauncherConstants
.TARGET_BUNDLES
;
280 /** The launch configuration attribute to use for workspace bundles */
281 protected static String
workspaceBundlesAttr(Boolean isEclipse
) {
282 return isEclipse ? IPDELauncherConstants
.SELECTED_WORKSPACE_PLUGINS
283 : IPDELauncherConstants
.WORKSPACE_BUNDLES
;
287 * Interprets special properties and register the others as system
288 * properties to append.
290 * @return the application id defined by
291 * {@link OsgiLauncherConstants#ECLIPSE_APPLICATION}, or null if not
294 protected static String
interpretProperties(Properties properties
,
295 Map
<String
, Integer
> bundlesToStart
,
296 Map
<String
, String
> systemPropertiesToAppend
) {
297 computeBundlesToStart(bundlesToStart
, properties
, null);
299 String applicationId
= null;
300 propKeys
: for (Object keyObj
: properties
.keySet()) {
301 String key
= keyObj
.toString();
302 if (OsgiLauncherConstants
.ARGEO_OSGI_START
.equals(key
))
304 if (key
.startsWith(OsgiLauncherConstants
.ARGEO_OSGI_START
+ "."))
306 else if (OsgiLauncherConstants
.ARGEO_OSGI_BUNDLES
.equals(key
))
308 else if (OsgiLauncherConstants
.ARGEO_OSGI_LOCATIONS
.equals(key
))
310 else if (OsgiLauncherConstants
.OSGI_BUNDLES
.equals(key
))
312 else if (OsgiLauncherConstants
.ECLIPSE_APPLICATION
.equals(key
))
313 applicationId
= properties
.getProperty(key
);
315 systemPropertiesToAppend
.put(key
, properties
.getProperty(key
));
317 return applicationId
;
320 /** Adds a regular system property. */
321 protected static void addSysProperty(StringBuffer vmArgs
, String key
,
323 surroundSpaces(value
);
324 String str
= "-D" + key
+ "=" + value
;
325 vmArgs
.append(' ').append(str
);
328 /** Adds JVMS registered in the workspace as special system properties. */
329 protected static void addVms(StringBuffer vmArgs
) {
330 addVmSysProperty(vmArgs
, "default", JavaRuntime
.getDefaultVMInstall());
331 IVMInstallType
[] vmTypes
= JavaRuntime
.getVMInstallTypes();
332 for (IVMInstallType vmType
: vmTypes
) {
333 for (IVMInstall vmInstall
: vmType
.getVMInstalls()) {
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
) {
376 StringTokenizer stComa
= new StringTokenizer(original
, ",");
377 // sort by bundle symbolic name
378 Set
<String
> bundleIds
= new TreeSet
<String
>();
379 bundles
: while (stComa
.hasMoreTokens()) {
381 String bundleId
= stComa
.nextToken();
382 if (bundleId
.indexOf('*') >= 0)
383 throw new RuntimeException(
386 + " not properly formatted, clean your workspace projects");
388 int indexAt
= bundleId
.indexOf('@');
390 bundleId
= bundleId
.substring(0, indexAt
);
393 // We can now rely on bundleId value
395 if (bundleId
.endsWith(".source")) {
396 // debug("Skip source bundle " + bundleId);
399 .equals(IPDEBuildConstants
.BUNDLE_SIMPLE_CONFIGURATOR
)) {
400 // skip simple configurator in order to avoid side-effects
403 bundleIds
.add(bundleId
);
406 StringBuffer bufBundles
= new StringBuffer(1024);
407 boolean first
= true;
408 for (String bundleId
: bundleIds
) {
412 bufBundles
.append(',');
413 boolean modified
= false;
414 if (bundlesToStart
.containsKey(bundleId
)) {
415 Integer startLevel
= bundlesToStart
.get(bundleId
);
416 String startLevelStr
= startLevel
!= null ? startLevel
417 .toString() : "default";
418 bufBundles
.append(bundleId
).append('@').append(startLevelStr
)
421 debug("Will start " + bundleId
+ " at level " + startLevelStr
);
425 bufBundles
.append(bundleId
);
428 String output
= bufBundles
.toString();
433 /** Recursively deletes a directory tree. */
434 private static void deleteDir(File dir
) {
435 File
[] files
= dir
.listFiles();
436 for (File file
: files
) {
437 if (file
.isDirectory())
445 /** Loads a properties file. */
446 private static Properties
readProperties(IFile file
) throws CoreException
{
447 Properties props
= new Properties();
449 InputStream in
= null;
451 in
= file
.getContents();
453 } catch (Exception e
) {
454 throw new CoreException(new Status(IStatus
.ERROR
,
455 SlcIdeUiPlugin
.ID
, "Cannot read properties file", e
));
460 } catch (IOException e
) {
467 /** Determines the start levels for the bundles */
468 private static void computeBundlesToStart(
469 Map
<String
, Integer
> bundlesToStart
, Properties properties
,
470 Integer defaultStartLevel
) {
472 // default (and previously, only behaviour)
473 appendBundlesToStart(bundlesToStart
, defaultStartLevel
,
474 properties
.getProperty(OsgiLauncherConstants
.ARGEO_OSGI_START
,
477 // list argeo.osgi.start.* system properties
478 Iterator
<Object
> keys
= properties
.keySet().iterator();
479 final String prefix
= OsgiLauncherConstants
.ARGEO_OSGI_START
+ ".";
480 while (keys
.hasNext()) {
481 String key
= (String
) keys
.next();
482 if (key
.startsWith(prefix
)) {
484 String suffix
= key
.substring(prefix
.length());
485 String
[] tokens
= suffix
.split("\\.");
486 if (tokens
.length
> 0 && !tokens
[0].trim().equals(""))
488 // first token is start level
489 startLevel
= new Integer(tokens
[0]);
490 } catch (NumberFormatException e
) {
491 startLevel
= defaultStartLevel
;
494 startLevel
= defaultStartLevel
;
496 // append bundle names
497 String bundleNames
= properties
.getProperty(key
);
498 appendBundlesToStart(bundlesToStart
, startLevel
, bundleNames
);
503 /** Append a comma-separated list of bundles to the start levels. */
504 private static void appendBundlesToStart(
505 Map
<String
, Integer
> bundlesToStart
, Integer startLevel
, String str
) {
506 if (str
== null || str
.trim().equals(""))
509 String
[] bundleNames
= str
.split(",");
510 for (int i
= 0; i
< bundleNames
.length
; i
++) {
511 if (bundleNames
[i
] != null && !bundleNames
[i
].trim().equals(""))
512 bundlesToStart
.put(bundleNames
[i
], startLevel
);
520 // org.eclipse.pde.internal.ui.launcher.LaunchArgumentsHelper.getWorkingDirectory(ILaunchConfiguration)
521 private static File
getWorkingDirectory(ILaunchConfiguration configuration
)
522 throws CoreException
{
525 working
= configuration
.getAttribute(
526 IJavaLaunchConfigurationConstants
.ATTR_WORKING_DIRECTORY
,
527 new File(".").getCanonicalPath()); //$NON-NLS-1$
528 } catch (IOException e
) {
529 working
= "${workspace_loc}/../"; //$NON-NLS-1$
533 dir
= new File(getSubstitutedString(working
));
534 } catch (Exception e
) {
535 // the directory was most probably deleted
536 IFile propertiesFile
= (IFile
) configuration
.getMappedResources()[0];
537 working
= findWorkingDirectory(propertiesFile
);
538 dir
= new File(getSubstitutedString(working
));
546 // org.eclipse.pde.internal.ui.launcher.LaunchArgumentsHelper.getSubstitutedString(String)
547 private static String
getSubstitutedString(String text
)
548 throws CoreException
{
550 return ""; //$NON-NLS-1$
551 IStringVariableManager mgr
= VariablesPlugin
.getDefault()
552 .getStringVariableManager();
553 return mgr
.performStringSubstitution(text
);
557 * Not used anymore, but kept because this routine may be useful in the
560 protected void addSelectedProjects(StringBuffer name
, ISelection selection
,
561 List
<String
> bundlesToStart
) {
562 Assert
.isNotNull(selection
);
564 Map
<String
, IPluginModelBase
> bundleProjects
= new HashMap
<String
, IPluginModelBase
>();
565 for (IPluginModelBase modelBase
: PluginRegistry
.getWorkspaceModels()) {
566 IProject bundleProject
= modelBase
.getUnderlyingResource()
568 bundleProjects
.put(bundleProject
.getName(), modelBase
);
571 IStructuredSelection sSelection
= (IStructuredSelection
) selection
;
572 for (Iterator
<?
> it
= sSelection
.iterator(); it
.hasNext();) {
573 Object obj
= it
.next();
574 if (obj
instanceof IProject
) {
575 IProject project
= (IProject
) obj
;
576 if (bundleProjects
.containsKey(project
.getName())) {
577 IPluginModelBase modelBase
= bundleProjects
.get(project
580 BundleDescription bundleDescription
= null;
581 if (modelBase
.isFragmentModel()) {
582 BundleDescription
[] hosts
= modelBase
583 .getBundleDescription().getHost().getHosts();
584 for (BundleDescription bd
: hosts
) {
586 System
.out
.println("Host for "
587 + modelBase
.getBundleDescription()
588 .getSymbolicName() + ": "
589 + bd
.getSymbolicName());
590 bundleDescription
= bd
;
593 bundleDescription
= modelBase
.getBundleDescription();
596 if (bundleDescription
!= null) {
597 String symbolicName
= bundleDescription
599 String bundleName
= bundleDescription
.getName();
601 bundlesToStart
.add(symbolicName
);
603 if (name
.length() > 0)
605 if (bundleName
!= null)
606 name
.append(bundleName
);
608 name
.append(symbolicName
);
615 static void debug(Object obj
) {
617 System
.out
.println(obj
);