]> 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
Restructure SLC
[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.HashMap;
8 import java.util.Iterator;
9 import java.util.List;
10 import java.util.Map;
11 import java.util.Properties;
12 import java.util.Set;
13 import java.util.StringTokenizer;
14 import java.util.TreeSet;
15
16 import org.argeo.slc.ide.ui.SlcIdeUiPlugin;
17 import org.eclipse.core.resources.IFile;
18 import org.eclipse.core.resources.IFolder;
19 import org.eclipse.core.resources.IProject;
20 import org.eclipse.core.resources.IResource;
21 import org.eclipse.core.runtime.Assert;
22 import org.eclipse.core.runtime.CoreException;
23 import org.eclipse.core.runtime.IPath;
24 import org.eclipse.core.runtime.IStatus;
25 import org.eclipse.core.runtime.Status;
26 import org.eclipse.core.variables.IStringVariableManager;
27 import org.eclipse.core.variables.VariablesPlugin;
28 import org.eclipse.debug.core.ILaunchConfiguration;
29 import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
30 import org.eclipse.jdt.launching.IJavaLaunchConfigurationConstants;
31 import org.eclipse.jdt.launching.IVMInstall;
32 import org.eclipse.jdt.launching.IVMInstall2;
33 import org.eclipse.jdt.launching.IVMInstallType;
34 import org.eclipse.jdt.launching.JavaRuntime;
35 import org.eclipse.jface.dialogs.ErrorDialog;
36 import org.eclipse.jface.viewers.ISelection;
37 import org.eclipse.jface.viewers.IStructuredSelection;
38 import org.eclipse.osgi.service.resolver.BundleDescription;
39 import org.eclipse.pde.core.plugin.IPluginModelBase;
40 import org.eclipse.pde.core.plugin.PluginRegistry;
41 import org.eclipse.pde.internal.build.IPDEBuildConstants;
42 import org.eclipse.pde.launching.IPDELauncherConstants;
43 import org.eclipse.swt.widgets.Display;
44 import org.eclipse.swt.widgets.Shell;
45
46 /**
47 * Most of the actual logic is concentrated in this class which manipulates
48 * {@link ILaunchConfigurationWorkingCopy}. Static method are used since the
49 * shortcut and launch configuration classes are already extending PDE classes.
50 */
51 @SuppressWarnings("restriction")
52 public class OsgiLaunchHelper implements OsgiLauncherConstants {
53 private static Boolean debug = true;
54
55 private final static String DEFAULT_DATA_DIR = "data";
56 private final static String DEFAULT_EXEC_DIR = "exec";
57 private final static String DEFAULT_VMARGS = "-Xmx256m";
58 private final static String DEFAULT_PROGRAM_ARGS = "-console";
59
60 /** Sets default values on this configuration. */
61 public static void setDefaults(ILaunchConfigurationWorkingCopy wc,
62 Boolean isEclipse) {
63 try {
64 if (isEclipse) {
65 wc.setAttribute(IPDELauncherConstants.USE_DEFAULT, false);
66 wc.setAttribute(IPDELauncherConstants.USE_PRODUCT, false);
67 }
68
69 wc.setAttribute(ATTR_ADD_JVM_PATHS, false);
70 wc.setAttribute(ATTR_ADDITIONAL_VM_ARGS, DEFAULT_VMARGS);
71 wc.setAttribute(ATTR_ADDITIONAL_PROGRAM_ARGS, DEFAULT_PROGRAM_ARGS);
72
73 // Defaults
74 String originalVmArgs = wc.getAttribute(
75 IJavaLaunchConfigurationConstants.ATTR_VM_ARGUMENTS, "");
76 wc.setAttribute(ATTR_DEFAULT_VM_ARGS, originalVmArgs);
77 wc.setAttribute(IPDELauncherConstants.CONFIG_CLEAR_AREA, true);
78 } catch (CoreException e) {
79 Shell shell = Display.getCurrent().getActiveShell();
80 ErrorDialog.openError(shell, "Error",
81 "Cannot execute initalize configuration", e.getStatus());
82 }
83 }
84
85 /** Find the working directory based on this properties file. */
86 public static String findWorkingDirectory(IFile propertiesFile) {
87 try {
88 IProject project = propertiesFile.getProject();
89 IPath parent = propertiesFile.getProjectRelativePath()
90 .removeLastSegments(1);
91 IFolder execFolder = project.getFolder(parent
92 .append(DEFAULT_EXEC_DIR));
93 if (!execFolder.exists())
94 execFolder.create(true, true, null);
95 IFolder launchFolder = project.getFolder(execFolder
96 .getProjectRelativePath().append(
97 extractName(propertiesFile)));
98 if (!launchFolder.exists())
99 launchFolder.create(true, true, null);
100 return "${workspace_loc:"
101 + launchFolder.getFullPath().toString().substring(1) + "}";
102 } catch (Exception e) {
103 e.printStackTrace();
104 throw new RuntimeException("Cannot create working directory", e);
105 }
106 }
107
108 /** Extract the launch configuration name from the properties file. */
109 public static String extractName(IFile propertiesFile) {
110 IPath path = propertiesFile.getFullPath();
111 IPath pathNoExt = path.removeFileExtension();
112 return pathNoExt.segment(pathNoExt.segmentCount() - 1);
113
114 }
115
116 /** Expects properties file to be set as mapped resources */
117 public static void updateLaunchConfiguration(
118 ILaunchConfigurationWorkingCopy wc, Boolean isEclipse) {
119 try {
120 // Finds the properties file and load it
121 IFile propertiesFile = (IFile) wc.getMappedResources()[0];
122 propertiesFile.refreshLocal(IResource.DEPTH_ONE, null);
123 Properties properties = readProperties(propertiesFile);
124
125 // Extract information from the properties file
126 List<String> bundlesToStart = new ArrayList<String>();
127 Map<String, String> systemPropertiesToAppend = new HashMap<String, String>();
128 String applicationId = interpretProperties(properties,
129 bundlesToStart, systemPropertiesToAppend);
130
131 if (applicationId != null)
132 wc.setAttribute(IPDELauncherConstants.APPLICATION,
133 applicationId);
134 else {
135 if (isEclipse)
136 throw new Exception("No application defined,"
137 + " please set the 'eclipse.application' property"
138 + " in the properties file");
139 }
140
141 // Define directories
142 File workingDir = getWorkingDirectory(wc);
143 File dataDir = new File(workingDir, DEFAULT_DATA_DIR);
144
145 // Update the launch configuration accordingly
146 updateLaunchConfiguration(wc, bundlesToStart,
147 systemPropertiesToAppend, dataDir.getAbsolutePath(),
148 isEclipse);
149 } catch (Exception e) {
150 e.printStackTrace();
151 Shell shell = SlcIdeUiPlugin.getDefault().getWorkbench()
152 .getActiveWorkbenchWindow().getShell();
153 // Shell shell= Display.getCurrent().getActiveShell();
154 ErrorDialog.openError(shell, "Error",
155 "Cannot prepare launch configuration",
156 new Status(IStatus.ERROR, SlcIdeUiPlugin.ID,
157 e.getMessage(), e));
158 return;
159 }
160 }
161
162 /**
163 * Actually modifies the launch configuration in order to reflect the
164 * current state read from the properties file and the launch configuration
165 * UI.
166 */
167 protected static void updateLaunchConfiguration(
168 ILaunchConfigurationWorkingCopy wc, List<String> bundlesToStart,
169 Map<String, String> systemPropertiesToAppend, String dataDir,
170 Boolean isEclipse) throws CoreException {
171 // Convert bundle lists
172 final String targetBundles;
173 final String wkSpaceBundles;
174 if (wc.getAttribute(ATTR_SYNC_BUNDLES, true)) {
175 StringBuffer tBuf = new StringBuffer();
176 for (IPluginModelBase model : PluginRegistry.getExternalModels()) {
177 tBuf.append(model.getBundleDescription().getSymbolicName());
178 tBuf.append(',');
179 }
180 targetBundles = tBuf.toString();
181 StringBuffer wBuf = new StringBuffer();
182 models: for (IPluginModelBase model : PluginRegistry
183 .getWorkspaceModels()) {
184 if (model.getBundleDescription() == null) {
185 System.err.println("No bundle description for " + model);
186 continue models;
187 }
188 wBuf.append(model.getBundleDescription().getSymbolicName());
189 wBuf.append(',');
190 }
191 wkSpaceBundles = wBuf.toString();
192 } else {
193 targetBundles = wc.getAttribute(targetBundlesAttr(isEclipse), "");
194 wkSpaceBundles = wc.getAttribute(workspaceBundlesAttr(isEclipse),
195 "");
196 }
197 wc.setAttribute(targetBundlesAttr(isEclipse),
198 convertBundleList(bundlesToStart, targetBundles));
199
200 wc.setAttribute(workspaceBundlesAttr(isEclipse),
201 convertBundleList(bundlesToStart, wkSpaceBundles));
202
203 // Update other default information
204 wc.setAttribute(IPDELauncherConstants.DEFAULT_AUTO_START, false);
205
206 // VM arguments (system properties)
207 String defaultVmArgs = wc.getAttribute(
208 OsgiLauncherConstants.ATTR_DEFAULT_VM_ARGS, "");
209 StringBuffer vmArgs = new StringBuffer(defaultVmArgs);
210
211 // Data dir system property
212 if (dataDir != null) {
213 addSysProperty(vmArgs, OsgiLauncherConstants.ARGEO_OSGI_DATA_DIR,
214 dataDir);
215 if (isEclipse) {
216 wc.setAttribute(IPDELauncherConstants.LOCATION, dataDir);
217 }
218 }
219
220 // Add locations of JVMs
221 if (wc.getAttribute(ATTR_ADD_JVM_PATHS, false))
222 addVms(vmArgs);
223
224 // Add other system properties
225 for (String key : systemPropertiesToAppend.keySet())
226 addSysProperty(vmArgs, key, systemPropertiesToAppend.get(key));
227
228 vmArgs.append(" ").append(wc.getAttribute(ATTR_ADDITIONAL_VM_ARGS, ""));
229
230 wc.setAttribute(IJavaLaunchConfigurationConstants.ATTR_VM_ARGUMENTS,
231 vmArgs.toString());
232
233 // Program arguments
234 StringBuffer progArgs = new StringBuffer("");
235 if (dataDir != null) {
236 progArgs.append("-data ");
237 progArgs.append(surroundSpaces(dataDir));
238
239 if (wc.getAttribute(ATTR_CLEAR_DATA_DIRECTORY, false)) {
240 File dataDirFile = new File(dataDir);
241 deleteDir(dataDirFile);
242 dataDirFile.mkdirs();
243 }
244 }
245 String additionalProgramArgs = wc.getAttribute(
246 OsgiLauncherConstants.ATTR_ADDITIONAL_PROGRAM_ARGS, "");
247 progArgs.append(' ').append(additionalProgramArgs);
248 wc.setAttribute(
249 IJavaLaunchConfigurationConstants.ATTR_PROGRAM_ARGUMENTS,
250 progArgs.toString());
251 }
252
253 /** The launch configuration attribute to use for target bundles */
254 protected static String targetBundlesAttr(Boolean isEclipse) {
255 return isEclipse ? IPDELauncherConstants.SELECTED_TARGET_PLUGINS
256 : IPDELauncherConstants.TARGET_BUNDLES;
257 }
258
259 /** The launch configuration attribute to use for workspace bundles */
260 protected static String workspaceBundlesAttr(Boolean isEclipse) {
261 return isEclipse ? IPDELauncherConstants.SELECTED_WORKSPACE_PLUGINS
262 : IPDELauncherConstants.WORKSPACE_BUNDLES;
263 }
264
265 /**
266 * Interprets special properties and register the others as system
267 * properties to append.
268 *
269 * @return the application id defined by
270 * {@link OsgiLauncherConstants#ECLIPSE_APPLICATION}, or null if not
271 * found
272 */
273 protected static String interpretProperties(Properties properties,
274 List<String> bundlesToStart,
275 Map<String, String> systemPropertiesToAppend) {
276 String argeoOsgiStart = properties
277 .getProperty(OsgiLauncherConstants.ARGEO_OSGI_START);
278 if (argeoOsgiStart != null) {
279 StringTokenizer st = new StringTokenizer(argeoOsgiStart, ",");
280 while (st.hasMoreTokens())
281 bundlesToStart.add(st.nextToken());
282 }
283
284 String applicationId = null;
285 propKeys: for (Object keyObj : properties.keySet()) {
286 String key = keyObj.toString();
287 if (OsgiLauncherConstants.ARGEO_OSGI_START.equals(key))
288 continue propKeys;
289 else if (OsgiLauncherConstants.ARGEO_OSGI_BUNDLES.equals(key))
290 continue propKeys;
291 else if (OsgiLauncherConstants.ARGEO_OSGI_LOCATIONS.equals(key))
292 continue propKeys;
293 else if (OsgiLauncherConstants.OSGI_BUNDLES.equals(key))
294 continue propKeys;
295 else if (OsgiLauncherConstants.ECLIPSE_APPLICATION.equals(key))
296 applicationId = properties.getProperty(key);
297 else
298 systemPropertiesToAppend.put(key, properties.getProperty(key));
299 }
300 return applicationId;
301 }
302
303 /** Adds a regular system property. */
304 protected static void addSysProperty(StringBuffer vmArgs, String key,
305 String value) {
306 surroundSpaces(value);
307 String str = "-D" + key + "=" + value;
308 vmArgs.append(' ').append(str);
309 }
310
311 /** Adds JVMS registered in the workspace as special system properties. */
312 protected static void addVms(StringBuffer vmArgs) {
313 addVmSysProperty(vmArgs, "default", JavaRuntime.getDefaultVMInstall());
314 IVMInstallType[] vmTypes = JavaRuntime.getVMInstallTypes();
315 for (IVMInstallType vmType : vmTypes) {
316 for (IVMInstall vmInstall : vmType.getVMInstalls()) {
317 // printVm("", vmInstall);
318 // properties based on name
319 addVmSysProperty(vmArgs, vmInstall.getName(), vmInstall);
320 if (vmInstall instanceof IVMInstall2) {
321 // properties based on version
322 IVMInstall2 vmInstall2 = (IVMInstall2) vmInstall;
323 String version = vmInstall2.getJavaVersion();
324 addVmSysProperty(vmArgs, version, vmInstall);
325
326 List<String> tokens = new ArrayList<String>();
327 StringTokenizer st = new StringTokenizer(version, ".");
328 while (st.hasMoreTokens())
329 tokens.add(st.nextToken());
330 if (tokens.size() >= 2)
331 addVmSysProperty(vmArgs,
332 tokens.get(0) + "." + tokens.get(1), vmInstall);
333 }
334 }
335 }
336
337 }
338
339 /** Adds a special system property pointing to one of the registered JVMs. */
340 protected static void addVmSysProperty(StringBuffer vmArgs, String suffix,
341 IVMInstall vmInstall) {
342 addSysProperty(vmArgs, OsgiLauncherConstants.VMS_PROPERTY_PREFIX + "."
343 + suffix, vmInstall.getInstallLocation().getPath());
344 }
345
346 /** Surround the string with quotes if it contains spaces. */
347 protected static String surroundSpaces(String str) {
348 if (str.indexOf(' ') >= 0)
349 return '\"' + str + '\"';
350 else
351 return str;
352 }
353
354 /**
355 * Reformat the bundle list in order to reflect which bundles have to be
356 * started.
357 */
358 protected static String convertBundleList(List<String> bundlesToStart,
359 String original) {
360 debug("Original bundle list: " + original);
361
362 StringTokenizer stComa = new StringTokenizer(original, ",");
363 // sort by bundle symbolic name
364 Set<String> bundleIds = new TreeSet<String>();
365 bundles: while (stComa.hasMoreTokens()) {
366
367 String bundleId = stComa.nextToken();
368 if (bundleId.indexOf('*') >= 0)
369 throw new RuntimeException(
370 "Bundle id "
371 + bundleId
372 + " not properly formatted, clean your workspace projects");
373
374 int indexAt = bundleId.indexOf('@');
375 if (indexAt >= 0) {
376 bundleId = bundleId.substring(0, indexAt);
377 }
378
379 // We can now rely on bundleId value
380
381 if (bundleId.endsWith(".source")) {
382 debug("Skip source bundle " + bundleId);
383 continue bundles;
384 } else if (bundleId
385 .equals(IPDEBuildConstants.BUNDLE_SIMPLE_CONFIGURATOR)) {
386 // skip simple configurator in order to avoid side-effects
387 continue bundles;
388 }
389 bundleIds.add(bundleId);
390 }
391
392 StringBuffer bufBundles = new StringBuffer(1024);
393 boolean first = true;
394 for (String bundleId : bundleIds) {
395 if (first)
396 first = false;
397 else
398 bufBundles.append(',');
399 boolean modified = false;
400 if (bundlesToStart.contains(bundleId)) {
401 bufBundles.append(bundleId).append('@').append("default:true");
402 modified = true;
403 debug("Will start " + bundleId);
404 }
405
406 if (!modified)
407 bufBundles.append(bundleId);
408
409 }
410 String output = bufBundles.toString();
411 return output;
412 }
413
414 // UTILITIES
415 /** Recursively deletes a directory tree. */
416 private static void deleteDir(File dir) {
417 File[] files = dir.listFiles();
418 for (File file : files) {
419 if (file.isDirectory())
420 deleteDir(file);
421 else
422 file.delete();
423 }
424 dir.delete();
425 }
426
427 /** Loads a properties file. */
428 private static Properties readProperties(IFile file) throws CoreException {
429 Properties props = new Properties();
430
431 InputStream in = null;
432 try {
433 in = file.getContents();
434 props.load(in);
435 } catch (Exception e) {
436 throw new CoreException(new Status(IStatus.ERROR,
437 SlcIdeUiPlugin.ID, "Cannot read properties file", e));
438 } finally {
439 if (in != null)
440 try {
441 in.close();
442 } catch (IOException e) {
443 // silent
444 }
445 }
446 return props;
447 }
448
449 // Hacked from
450 // org.eclipse.pde.internal.ui.launcher.LaunchArgumentsHelper.getWorkingDirectory(ILaunchConfiguration)
451 private static File getWorkingDirectory(ILaunchConfiguration configuration)
452 throws CoreException {
453 String working;
454 try {
455 working = configuration.getAttribute(
456 IJavaLaunchConfigurationConstants.ATTR_WORKING_DIRECTORY,
457 new File(".").getCanonicalPath()); //$NON-NLS-1$
458 } catch (IOException e) {
459 working = "${workspace_loc}/../"; //$NON-NLS-1$
460 }
461 File dir;
462 try {
463 dir = new File(getSubstitutedString(working));
464 } catch (Exception e) {
465 // the directory was most probably deleted
466 IFile propertiesFile = (IFile) configuration.getMappedResources()[0];
467 working = findWorkingDirectory(propertiesFile);
468 dir = new File(getSubstitutedString(working));
469 }
470 if (!dir.exists())
471 dir.mkdirs();
472 return dir;
473 }
474
475 // Hacked from
476 // org.eclipse.pde.internal.ui.launcher.LaunchArgumentsHelper.getSubstitutedString(String)
477 private static String getSubstitutedString(String text)
478 throws CoreException {
479 if (text == null)
480 return ""; //$NON-NLS-1$
481 IStringVariableManager mgr = VariablesPlugin.getDefault()
482 .getStringVariableManager();
483 return mgr.performStringSubstitution(text);
484 }
485
486 /**
487 * Not used anymore, but kept because this routine may be useful in the
488 * future.
489 */
490 protected void addSelectedProjects(StringBuffer name, ISelection selection,
491 List<String> bundlesToStart) {
492 Assert.isNotNull(selection);
493
494 Map<String, IPluginModelBase> bundleProjects = new HashMap<String, IPluginModelBase>();
495 for (IPluginModelBase modelBase : PluginRegistry.getWorkspaceModels()) {
496 IProject bundleProject = modelBase.getUnderlyingResource()
497 .getProject();
498 bundleProjects.put(bundleProject.getName(), modelBase);
499 }
500
501 IStructuredSelection sSelection = (IStructuredSelection) selection;
502 for (Iterator<?> it = sSelection.iterator(); it.hasNext();) {
503 Object obj = it.next();
504 if (obj instanceof IProject) {
505 IProject project = (IProject) obj;
506 if (bundleProjects.containsKey(project.getName())) {
507 IPluginModelBase modelBase = bundleProjects.get(project
508 .getName());
509
510 BundleDescription bundleDescription = null;
511 if (modelBase.isFragmentModel()) {
512 BundleDescription[] hosts = modelBase
513 .getBundleDescription().getHost().getHosts();
514 for (BundleDescription bd : hosts) {
515 if (debug)
516 System.out.println("Host for "
517 + modelBase.getBundleDescription()
518 .getSymbolicName() + ": "
519 + bd.getSymbolicName());
520 bundleDescription = bd;
521 }
522 } else {
523 bundleDescription = modelBase.getBundleDescription();
524 }
525
526 if (bundleDescription != null) {
527 String symbolicName = bundleDescription
528 .getSymbolicName();
529 String bundleName = bundleDescription.getName();
530
531 bundlesToStart.add(symbolicName);
532
533 if (name.length() > 0)
534 name.append(" ");
535 if (bundleName != null)
536 name.append(bundleName);
537 else
538 name.append(symbolicName);
539 }
540 }
541 }
542 }
543 }
544
545 static void debug(Object obj) {
546 if (debug)
547 System.out.println(obj);
548 }
549
550 }