]> git.argeo.org Git - gpl/argeo-slc.git/blob - plugins/org.argeo.slc.ide.ui/src/main/java/org/argeo/slc/ide/ui/launch/osgi/OsgiLaunchHelper.java
11135f208528fdb37eda7a336425c23e4f372665
[gpl/argeo-slc.git] / plugins / org.argeo.slc.ide.ui / src / main / java / org / argeo / slc / ide / ui / launch / osgi / OsgiLaunchHelper.java
1 package org.argeo.slc.ide.ui.launch.osgi;
2
3 import java.io.File;
4 import java.io.IOException;
5 import java.io.InputStream;
6 import java.util.ArrayList;
7 import java.util.Date;
8 import java.util.HashMap;
9 import java.util.Iterator;
10 import java.util.List;
11 import java.util.Map;
12 import java.util.Properties;
13 import java.util.Set;
14 import java.util.StringTokenizer;
15 import java.util.TreeMap;
16 import java.util.TreeSet;
17
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;
47
48 /**
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.
52 */
53 @SuppressWarnings("restriction")
54 public class OsgiLaunchHelper implements OsgiLauncherConstants {
55 private static Boolean debug = true;
56
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";
61
62 /** Sets default values on this configuration. */
63 public static void setDefaults(ILaunchConfigurationWorkingCopy wc,
64 Boolean isEclipse) {
65 try {
66 if (isEclipse) {
67 wc.setAttribute(IPDELauncherConstants.USE_DEFAULT, false);
68 wc.setAttribute(IPDELauncherConstants.USE_PRODUCT, false);
69 }
70
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);
74
75 // Defaults
76 String originalVmArgs = wc.getAttribute(
77 IJavaLaunchConfigurationConstants.ATTR_VM_ARGUMENTS, "");
78 wc.setAttribute(ATTR_DEFAULT_VM_ARGS, originalVmArgs);
79
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());
86 }
87 }
88
89 /** Find the working directory based on this properties file. */
90 public static String findWorkingDirectory(IFile propertiesFile) {
91 try {
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) {
107 e.printStackTrace();
108 throw new RuntimeException("Cannot create working directory", e);
109 }
110 }
111
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);
117
118 }
119
120 /** Expects properties file to be set as mapped resources */
121 @SuppressWarnings("unchecked")
122 public static void updateLaunchConfiguration(
123 ILaunchConfigurationWorkingCopy wc, Boolean isEclipse) {
124 try {
125 if (debug)
126 debug("##\n## Launch " + wc.getName() + " - " + new Date()
127 + "\n##");
128
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);
133
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);
139
140 if (applicationId != null)
141 wc.setAttribute(IPDELauncherConstants.APPLICATION,
142 applicationId);
143 else {
144 if (isEclipse)
145 throw new Exception("No application defined,"
146 + " please set the 'eclipse.application' property"
147 + " in the properties file");
148 }
149
150 // Define directories
151 File workingDir = getWorkingDirectory(wc);
152 File dataDir = new File(workingDir, DEFAULT_DATA_DIR);
153
154 // Update the launch configuration accordingly
155 updateLaunchConfiguration(wc, bundlesToStart,
156 systemPropertiesToAppend, dataDir.getAbsolutePath(),
157 isEclipse);
158
159 if (debug) {
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));
164 }
165 } catch (Exception e) {
166 e.printStackTrace();
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,
173 e.getMessage(), e));
174 return;
175 }
176 }
177
178 /**
179 * Actually modifies the launch configuration in order to reflect the
180 * current state read from the properties file and the launch configuration
181 * UI.
182 */
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());
195 tBuf.append(',');
196 }
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);
203 continue models;
204 }
205 wBuf.append(model.getBundleDescription().getSymbolicName());
206 wBuf.append(',');
207 }
208 wkSpaceBundles = wBuf.toString();
209 } else {
210 targetBundles = wc.getAttribute(targetBundlesAttr(isEclipse), "");
211 wkSpaceBundles = wc.getAttribute(workspaceBundlesAttr(isEclipse),
212 "");
213 }
214 wc.setAttribute(targetBundlesAttr(isEclipse),
215 convertBundleList(bundlesToStart, targetBundles));
216
217 wc.setAttribute(workspaceBundlesAttr(isEclipse),
218 convertBundleList(bundlesToStart, wkSpaceBundles));
219
220 // Update other default information
221 wc.setAttribute(IPDELauncherConstants.DEFAULT_AUTO_START, false);
222
223 wc.setAttribute(IPDELauncherConstants.USE_CUSTOM_FEATURES, false);
224 if (!isEclipse)
225 wc.setAttribute(IPDELauncherConstants.USE_DEFAULT, true);
226
227 // VM arguments (system properties)
228 String defaultVmArgs = wc.getAttribute(
229 OsgiLauncherConstants.ATTR_DEFAULT_VM_ARGS, "");
230 StringBuffer vmArgs = new StringBuffer(defaultVmArgs);
231
232 // Data dir system property
233 if (dataDir != null) {
234 addSysProperty(vmArgs, OsgiLauncherConstants.ARGEO_OSGI_DATA_DIR,
235 dataDir);
236 if (isEclipse) {
237 wc.setAttribute(IPDELauncherConstants.LOCATION, dataDir);
238 }
239 }
240
241 // Add locations of JVMs
242 if (wc.getAttribute(ATTR_ADD_JVM_PATHS, false))
243 addVms(vmArgs);
244
245 // Add other system properties
246 for (String key : systemPropertiesToAppend.keySet())
247 addSysProperty(vmArgs, key, systemPropertiesToAppend.get(key));
248
249 vmArgs.append(" ").append(wc.getAttribute(ATTR_ADDITIONAL_VM_ARGS, ""));
250
251 wc.setAttribute(IJavaLaunchConfigurationConstants.ATTR_VM_ARGUMENTS,
252 vmArgs.toString());
253
254 // Program arguments
255 StringBuffer progArgs = new StringBuffer("");
256 if (dataDir != null) {
257 progArgs.append("-data ");
258 progArgs.append(surroundSpaces(dataDir));
259
260 if (wc.getAttribute(ATTR_CLEAR_DATA_DIRECTORY, false)) {
261 File dataDirFile = new File(dataDir);
262 deleteDir(dataDirFile);
263 dataDirFile.mkdirs();
264 }
265 }
266 String additionalProgramArgs = wc.getAttribute(
267 OsgiLauncherConstants.ATTR_ADDITIONAL_PROGRAM_ARGS, "");
268 progArgs.append(' ').append(additionalProgramArgs);
269 wc.setAttribute(
270 IJavaLaunchConfigurationConstants.ATTR_PROGRAM_ARGUMENTS,
271 progArgs.toString());
272 }
273
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;
278 }
279
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;
284 }
285
286 /**
287 * Interprets special properties and register the others as system
288 * properties to append.
289 *
290 * @return the application id defined by
291 * {@link OsgiLauncherConstants#ECLIPSE_APPLICATION}, or null if not
292 * found
293 */
294 protected static String interpretProperties(Properties properties,
295 Map<String, Integer> bundlesToStart,
296 Map<String, String> systemPropertiesToAppend) {
297 computeBundlesToStart(bundlesToStart, properties, null);
298
299 String applicationId = null;
300 propKeys: for (Object keyObj : properties.keySet()) {
301 String key = keyObj.toString();
302 if (OsgiLauncherConstants.ARGEO_OSGI_START.equals(key))
303 continue propKeys;
304 if (key.startsWith(OsgiLauncherConstants.ARGEO_OSGI_START + "."))
305 continue propKeys;
306 else if (OsgiLauncherConstants.ARGEO_OSGI_BUNDLES.equals(key))
307 continue propKeys;
308 else if (OsgiLauncherConstants.ARGEO_OSGI_LOCATIONS.equals(key))
309 continue propKeys;
310 else if (OsgiLauncherConstants.OSGI_BUNDLES.equals(key))
311 continue propKeys;
312 else if (OsgiLauncherConstants.ECLIPSE_APPLICATION.equals(key))
313 applicationId = properties.getProperty(key);
314 else
315 systemPropertiesToAppend.put(key, properties.getProperty(key));
316 }
317 return applicationId;
318 }
319
320 /** Adds a regular system property. */
321 protected static void addSysProperty(StringBuffer vmArgs, String key,
322 String value) {
323 surroundSpaces(value);
324 String str = "-D" + key + "=" + value;
325 vmArgs.append(' ').append(str);
326 }
327
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);
341
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);
349 }
350 }
351 }
352
353 }
354
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());
360 }
361
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 + '\"';
366 else
367 return str;
368 }
369
370 /**
371 * Reformat the bundle list in order to reflect which bundles have to be
372 * started.
373 */
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()) {
380
381 String bundleId = stComa.nextToken();
382 if (bundleId.indexOf('*') >= 0)
383 throw new RuntimeException(
384 "Bundle id "
385 + bundleId
386 + " not properly formatted, clean your workspace projects");
387
388 int indexAt = bundleId.indexOf('@');
389 if (indexAt >= 0) {
390 bundleId = bundleId.substring(0, indexAt);
391 }
392
393 // We can now rely on bundleId value
394
395 if (bundleId.endsWith(".source")) {
396 // debug("Skip source bundle " + bundleId);
397 continue bundles;
398 } else if (bundleId
399 .equals(IPDEBuildConstants.BUNDLE_SIMPLE_CONFIGURATOR)) {
400 // skip simple configurator in order to avoid side-effects
401 continue bundles;
402 }
403 bundleIds.add(bundleId);
404 }
405
406 StringBuffer bufBundles = new StringBuffer(1024);
407 boolean first = true;
408 for (String bundleId : bundleIds) {
409 if (first)
410 first = false;
411 else
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)
419 .append(":true");
420 modified = true;
421 debug("Will start " + bundleId + " at level " + startLevelStr);
422 }
423
424 if (!modified)
425 bufBundles.append(bundleId);
426
427 }
428 String output = bufBundles.toString();
429 return output;
430 }
431
432 // UTILITIES
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())
438 deleteDir(file);
439 else
440 file.delete();
441 }
442 dir.delete();
443 }
444
445 /** Loads a properties file. */
446 private static Properties readProperties(IFile file) throws CoreException {
447 Properties props = new Properties();
448
449 InputStream in = null;
450 try {
451 in = file.getContents();
452 props.load(in);
453 } catch (Exception e) {
454 throw new CoreException(new Status(IStatus.ERROR,
455 SlcIdeUiPlugin.ID, "Cannot read properties file", e));
456 } finally {
457 if (in != null)
458 try {
459 in.close();
460 } catch (IOException e) {
461 // silent
462 }
463 }
464 return props;
465 }
466
467 /** Determines the start levels for the bundles */
468 private static void computeBundlesToStart(
469 Map<String, Integer> bundlesToStart, Properties properties,
470 Integer defaultStartLevel) {
471
472 // default (and previously, only behaviour)
473 appendBundlesToStart(bundlesToStart, defaultStartLevel,
474 properties.getProperty(OsgiLauncherConstants.ARGEO_OSGI_START,
475 ""));
476
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)) {
483 Integer startLevel;
484 String suffix = key.substring(prefix.length());
485 String[] tokens = suffix.split("\\.");
486 if (tokens.length > 0 && !tokens[0].trim().equals(""))
487 try {
488 // first token is start level
489 startLevel = new Integer(tokens[0]);
490 } catch (NumberFormatException e) {
491 startLevel = defaultStartLevel;
492 }
493 else
494 startLevel = defaultStartLevel;
495
496 // append bundle names
497 String bundleNames = properties.getProperty(key);
498 appendBundlesToStart(bundlesToStart, startLevel, bundleNames);
499 }
500 }
501 }
502
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(""))
507 return;
508
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);
513 }
514 }
515
516 /*
517 * HACKED UTILITIES
518 */
519 // Hacked from
520 // org.eclipse.pde.internal.ui.launcher.LaunchArgumentsHelper.getWorkingDirectory(ILaunchConfiguration)
521 private static File getWorkingDirectory(ILaunchConfiguration configuration)
522 throws CoreException {
523 String working;
524 try {
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$
530 }
531 File dir;
532 try {
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));
539 }
540 if (!dir.exists())
541 dir.mkdirs();
542 return dir;
543 }
544
545 // Hacked from
546 // org.eclipse.pde.internal.ui.launcher.LaunchArgumentsHelper.getSubstitutedString(String)
547 private static String getSubstitutedString(String text)
548 throws CoreException {
549 if (text == null)
550 return ""; //$NON-NLS-1$
551 IStringVariableManager mgr = VariablesPlugin.getDefault()
552 .getStringVariableManager();
553 return mgr.performStringSubstitution(text);
554 }
555
556 /**
557 * Not used anymore, but kept because this routine may be useful in the
558 * future.
559 */
560 protected void addSelectedProjects(StringBuffer name, ISelection selection,
561 List<String> bundlesToStart) {
562 Assert.isNotNull(selection);
563
564 Map<String, IPluginModelBase> bundleProjects = new HashMap<String, IPluginModelBase>();
565 for (IPluginModelBase modelBase : PluginRegistry.getWorkspaceModels()) {
566 IProject bundleProject = modelBase.getUnderlyingResource()
567 .getProject();
568 bundleProjects.put(bundleProject.getName(), modelBase);
569 }
570
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
578 .getName());
579
580 BundleDescription bundleDescription = null;
581 if (modelBase.isFragmentModel()) {
582 BundleDescription[] hosts = modelBase
583 .getBundleDescription().getHost().getHosts();
584 for (BundleDescription bd : hosts) {
585 if (debug)
586 System.out.println("Host for "
587 + modelBase.getBundleDescription()
588 .getSymbolicName() + ": "
589 + bd.getSymbolicName());
590 bundleDescription = bd;
591 }
592 } else {
593 bundleDescription = modelBase.getBundleDescription();
594 }
595
596 if (bundleDescription != null) {
597 String symbolicName = bundleDescription
598 .getSymbolicName();
599 String bundleName = bundleDescription.getName();
600
601 bundlesToStart.add(symbolicName);
602
603 if (name.length() > 0)
604 name.append(" ");
605 if (bundleName != null)
606 name.append(bundleName);
607 else
608 name.append(symbolicName);
609 }
610 }
611 }
612 }
613 }
614
615 static void debug(Object obj) {
616 if (debug)
617 System.out.println(obj);
618 }
619
620 }