]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.team.ui/src/org/simantics/team/internal/StagingLauncher.java
Fixed all line endings of the repository
[simantics/platform.git] / bundles / org.simantics.team.ui / src / org / simantics / team / internal / StagingLauncher.java
1 package org.simantics.team.internal;
2
3 import java.io.File;
4 import java.io.FileWriter;
5 import java.io.IOException;
6 import java.io.InputStream;
7 import java.io.PrintStream;
8 import java.net.URLDecoder;
9 import java.util.ArrayList;
10 import java.util.Arrays;
11 import java.util.HashSet;
12 import java.util.LinkedList;
13 import java.util.List;
14 import java.util.Properties;
15 import java.util.Set;
16
17 import org.eclipse.core.runtime.Platform;
18 import org.eclipse.equinox.frameworkadmin.BundleInfo;
19 import org.eclipse.equinox.internal.frameworkadmin.equinox.EquinoxConstants;
20 import org.eclipse.equinox.internal.frameworkadmin.utils.Utils;
21 import org.eclipse.equinox.internal.provisional.frameworkadmin.ConfigData;
22 import org.eclipse.equinox.internal.provisional.frameworkadmin.FrameworkAdmin;
23 import org.eclipse.equinox.internal.provisional.frameworkadmin.FrameworkAdminRuntimeException;
24 import org.eclipse.equinox.internal.provisional.frameworkadmin.LauncherData;
25 import org.eclipse.equinox.internal.provisional.frameworkadmin.Manipulator;
26 import org.osgi.framework.Bundle;
27 import org.osgi.framework.BundleContext;
28 import org.osgi.framework.BundleException;
29 import org.osgi.framework.FrameworkUtil;
30 import org.osgi.framework.InvalidSyntaxException;
31 import org.osgi.framework.ServiceReference;
32 import org.simantics.application.db.SocketUtils;
33 import org.simantics.databoard.binding.error.BindingConstructionException;
34 import org.simantics.db.Resource;
35 import org.simantics.db.Session;
36 import org.simantics.db.exception.DatabaseException;
37 import org.simantics.utils.FileUtils;
38 import org.simantics.utils.strings.EString;
39
40 @SuppressWarnings("restriction")
41 public final class StagingLauncher {
42     private static final boolean DEBUG = true;
43     private static final boolean DEBUG_EXEC = true;
44     private static final boolean REMOTE_DEBUG_DISABLED = true;
45     public static StagingResult launch(
46             final Config stagingConfig,
47             String serverAddress,
48             String targetResourceId)
49     throws InvalidSyntaxException, IllegalArgumentException,
50             FrameworkAdminRuntimeException, IOException, BindingConstructionException, DatabaseException {
51         Bundle dsBundle = Platform.getBundle("org.eclipse.equinox.ds");
52         try {
53             dsBundle.start(/*Bundle.START_TRANSIENT*/);
54             if (DEBUG)
55                 System.out.println("state="+dsBundle.getState());
56         } catch (BundleException ex) {
57             throw new StagingException("Could not start org.eclipse.equinox.ds.", ex);
58         }
59         Bundle faBundle = FrameworkUtil.getBundle(FrameworkAdmin.class);
60         if (null == faBundle)
61             throw new StagingException("Bundle for FrameworkAdmin not available.");
62         BundleContext faContext = faBundle.getBundleContext();
63         if (null == faContext)
64             throw new StagingException("Context for FrameworkAdmin not available.");
65         ServiceReference<FrameworkAdmin> ref = faContext.getServiceReference(FrameworkAdmin.class);
66         if (ref == null)
67             throw new StagingException("Reference for FrameworkAdmin not available.");
68         FrameworkAdmin admin = (FrameworkAdmin)faContext.getService(ref);
69         if (null == admin)
70             throw new StagingException("FrameworkAdmin not available.");
71         try {
72             Manipulator rmanip = admin.getRunningManipulator();
73             if (rmanip == null)
74                 throw new StagingException("No FrameworkAdmin Manipulator available for the currently running environment.");
75             if (DEBUG)
76                 System.out.println("FrameworkAdmin Manipulator of the running environment:\n" + rmanip);
77             Properties system = System.getProperties();
78             ConfigData rcd = rmanip.getConfigData();
79             LauncherData rld = rmanip.getLauncherData();
80             Manipulator manip = admin.getManipulator();
81             manip.setConfigData(rcd);
82             manip.setLauncherData(rld);
83             ConfigData cd = manip.getConfigData();
84             LauncherData ld = manip.getLauncherData();
85             Properties config = new Properties();
86             StringBuilder osgiBundles = new StringBuilder(1024);
87             StringBuilder bundlesInfo = new StringBuilder(1024);
88             bundlesInfo.append("#version=1\n");
89             for (BundleInfo bi : cd.getBundles()) {
90                 boolean started = isMarkedAsStarted(bi);
91                 bundlesInfo
92                 .append(bi.getSymbolicName())
93                 .append(",")
94                 .append(bi.getVersion())
95                 .append(",")
96                 //.append(bi.getLocation().toString())
97                 .append(URLDecoder.decode(bi.getLocation().toString(), "UTF-8"))
98                 .append(",")
99                 .append(bi.getStartLevel())
100                 .append(",")
101                 .append(started)
102                 .append("\n");
103                 if (DEBUG) {
104                     System.out.println("bundle: " + bi.getSymbolicName() + "\n\t" + bi.getLocation().toString() + "\n\t" + started);
105                     if (started)
106                         System.out.println("IS STARTED: bundle: " + bi.getSymbolicName() + "\n\t" + bi.getLocation().toString());
107                     if (!started && bi.isMarkedAsStarted())
108                         System.out.println("NOT STARTED, BUT WAS MARKED AS STARTED: bundle: " + bi.getSymbolicName() + "\n\t" + bi.getLocation().toString());
109                 }
110                 if (isStartUpBundle(bi)) {
111                     if (osgiBundles.length() > 0) {
112                         osgiBundles.append(",");
113                     }
114                     osgiBundles
115                     .append("reference:")
116                     .append(URLDecoder.decode(bi.getLocation().toString(), "UTF-8"))
117                     .append("@")
118                     .append(bi.getStartLevel())
119                     .append(":start");
120                 }
121             }
122             //File cwd = new File(system.getProperty("user.dir"));
123             File javaHome = new File(system.getProperty("java.home"));
124             String installArea = system.getProperty("osgi.install.area");
125             config.setProperty("eclipse.application", "org.simantics.workbench.application");
126             config.setProperty("eclipse.product", "org.simantics.devs3.ui.product");
127             //config.setProperty("eclipse.consoleLog", "");
128             config.setProperty("org.eclipse.update.reconcile", "false");
129             config.setProperty("osgi.bundles", osgiBundles.toString());
130             config.setProperty("osgi.bundles.defaultStartLevel", "4");
131             //config.setProperty("osgi.clean", "true");
132             //config.setProperty("osgi.configuration.area", "@default");
133             config.setProperty("osgi.configuration.cascaded", "false");
134             //config.setProperty("osgi.console", "");
135             //config.setProperty("osgi.debug", "");
136             config.setProperty("osgi.framework", URLDecoder.decode(ld.getFwJar().toURI().toString(), "UTF-8"));
137             //config.setProperty("osgi.noShutdown", "");
138             config.setProperty("osgi.install.area", installArea);
139             config.setProperty("osgi.instance.area", stagingConfig.workspaceRoot.getAbsolutePath());
140 //              cd.setProperty("osgi.instance.area", "@none");
141             config.setProperty("osgi.user.area", "@none");
142             // Eclipse 3.6, not sure what this is for but modern apps have it.
143             config.setProperty("equinox.use.ds", "true");
144             // Ignore INFO level log messages
145             config.setProperty("eclipse.log.level", "WARNING");
146             File configDir = new File(stagingConfig.workspaceRoot, "configuration");
147             File simpleConfiguratorDir = new File(configDir, "org.eclipse.equinox.simpleconfigurator");
148             simpleConfiguratorDir.mkdirs();
149             File bundlesInfoFile = new File(simpleConfiguratorDir, "bundles.info");
150             config.setProperty("org.eclipse.equinox.simpleconfigurator.configUrl", URLDecoder.decode(bundlesInfoFile.toURI().toString(), "UTF-8"));
151             File logFile = new File(stagingConfig.workspaceRoot, "db-client.log");
152             writeProperties(config, new File(configDir, "config.ini"), "This configuration file was written by: " + StagingLauncher.class.getCanonicalName());
153             writeFile(bundlesInfo.toString(), bundlesInfoFile);
154
155             ld.setJvm(new File(new File(javaHome, "bin"), "java"));
156             ld.setFwConfigLocation(configDir);
157             ld.setFwPersistentDataLocation(configDir, false);
158             ld.setLauncher(null);
159             int maxHeap = 768; /*prefs.getInt(Activator.PLUGIN_ID, ImportPreferences.PREF_IMPORT_PROCESS_MAX_HEAP,
160                     ImportPreferences.getDefaultImportProcessMaxHeap(), preferenceScopes);
161             // Just a safety if preferences have not been properly initialized for some reason.
162             if (maxHeap == 0)
163                 maxHeap = ImportPreferences.getDefaultImportProcessMaxHeap();*/
164             ld.addJvmArg("-Xmx" + maxHeap + "m");
165             ld.addJvmArg("-Xms" + maxHeap + "m");
166             // Enable assertions to have faster failure in db client routines with improper input.
167             ld.addJvmArg("-ea");
168             // For supporting OSGi dev mode (launched from IDE)
169             if (Platform.inDevelopmentMode()) {
170                 // TODO: %osgi.dev is not escaped, it can contain whitespace
171                 // but this doesn't seem to matter ?
172                 ld.addJvmArg("-Dosgi.dev=" + system.getProperty("osgi.dev"));
173             }
174             ld.addJvmArg("-Dosgi.arch=" + rcd.getProperty("osgi.arch"));
175             ld.addJvmArg("-Dosgi.os=" + rcd.getProperty("osgi.os"));
176             ld.addJvmArg("-Dosgi.ws=" + rcd.getProperty("osgi.ws"));
177             ld.addJvmArg("-Dosgi.nl=" + rcd.getProperty("osgi.nl"));
178
179             // WORKAROUND for a problem in org.eclipse.ecf fragment org.eclipse.ecf.ssl.
180             // See https://bugs.eclipse.org/bugs/show_bug.cgi?id=316500
181             // Either one of these works, THESE:
182             //ld.addJvmArg("-Dosgi.java.profile.bootdelegation=override");
183             //ld.addJvmArg("-Dorg.osgi.framework.bootdelegation=sun.*, com.sun.*, javax.*");
184             // OR:
185             ld.addJvmArg("-Dosgi.compatibility.bootdelegation=true");
186
187             // Enable remote debugging when in osgi dev mode.
188             if (Platform.inDevelopmentMode())
189                 if (!REMOTE_DEBUG_DISABLED) {
190                     ld.addJvmArg("-Xdebug");
191                     ld.addJvmArg("-Xnoagent");
192                     int port = SocketUtils.getFreeEphemeralPort();
193                     ld.addJvmArg("-Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=" + port);
194                 }
195
196             ld.addJvmArg("-D" + Constants.PROP_DUMP_PROPERTIES);
197             ld.addJvmArg("-D" + Constants.PROP_LOGFILE + "=" + logFile.toURI().toString());
198             String titleArgument = System.getProperty(Constants.PROP_WINDOW_TITLE);
199             if (null != titleArgument && !titleArgument.equals(""))
200                 titleArgument = stagingConfig.titlePrefix + " " + titleArgument;
201             else
202                 titleArgument = stagingConfig.titlePrefix;
203             ld.addJvmArg("-D" + Constants.PROP_WINDOW_TITLE + "=" + titleArgument);
204             if (null != stagingConfig.teamFolder)
205                 ld.addJvmArg("-D" + Constants.PROP_TEAM_FOLDER + "=" + stagingConfig.teamFolder.getAbsolutePath()); 
206             ld.addJvmArg("-D" + Constants.PROP_WORKSPACE_ROOT + "=" + stagingConfig.workspaceRoot.toURI().toString());
207             if (DEBUG)
208                 System.out.println("JVM ARGS: " + Arrays.toString(ld.getJvmArgs()));
209
210             // Using this means that
211             //  * config.ini must not be written self
212             //  * parent program bundles.info can be reused (this could be done in any case)
213             //  * running platform configuration must not be copied completely, only selected parts of
214             //manip.save(false);
215
216             // #5849 workaround
217 //            XSupport xs = stagingConfig.session.getService(XSupport.class);
218 //            xs.initClusterIdMap(stagingConfig.workspaceRoot.getAbsolutePath());
219             if (null != stagingConfig.clusterMapFolder) {
220                 String file = "clusterIdMap.dat";
221                 File f = new File(stagingConfig.clusterMapFolder, file);
222                 File tFolder = new File(stagingConfig.workspaceRoot, ".metadata/.plugins/org.simantics.db.procore");
223                 tFolder.mkdirs();
224                 FileUtils.copyFile(f, new File(tFolder, file));
225                 String file2 = "nextId.dat";
226                 File f2 = new File(stagingConfig.clusterMapFolder, file2);
227                 File tFolder2 = new File(stagingConfig.workspaceRoot, "db");
228                 FileUtils.copyFile(f2, new File(tFolder2, file2));
229             }
230             if (DEBUG)
231                 System.out.println("LAUNCHING\n" + manip);
232             Process process;
233             {
234                 LauncherData launcherData = ld;
235                 if (DEBUG)
236                     System.out.println("Framework JAR: " + ld.getFwJar().toURI().toString());
237                 Utils.checkAbsoluteFile(launcherData.getFwJar(), "fwJar"); //$NON-NLS-1$
238                 File cwd = stagingConfig.workspaceRoot;
239                 Utils.checkAbsoluteDir(cwd, "cwd"); //$NON-NLS-1$
240
241                 List<String> cmdList = new LinkedList<String>();
242                 if (launcherData.getJvm() != null)
243                     cmdList.add(launcherData.getJvm().getAbsolutePath());
244                 else
245                     cmdList.add("java"); //$NON-NLS-1$
246
247                 if (launcherData.getJvmArgs() != null)
248                     for (int i = 0; i < launcherData.getJvmArgs().length; i++)
249                         cmdList.add(launcherData.getJvmArgs()[i]);
250
251                 cmdList.add("-jar"); //$NON-NLS-1$
252                 cmdList.add("\"" + launcherData.getFwJar().getAbsolutePath() + "\"");
253
254                 //EquinoxManipulatorImpl.checkConsistencyOfFwConfigLocAndFwPersistentDataLoc(launcherData);
255                 cmdList.add(EquinoxConstants.OPTION_CONFIGURATION);
256                 cmdList.add("\"" + launcherData.getFwPersistentDataLocation().getAbsolutePath() + "\"");
257
258                 cmdList.add("-data");
259                 cmdList.add(stagingConfig.workspaceRoot.getAbsolutePath());
260
261                 if (launcherData.isClean())
262                     cmdList.add(EquinoxConstants.OPTION_CLEAN);
263
264                 String[] cmdarray = cmdList.toArray(new String[cmdList.size()]);
265
266                 if (DEBUG_EXEC)
267                     System.out.println("Launching import, CWD=" + cwd + "\n\t" + EString.implode(cmdarray, "\n\t"));
268
269                 process = Runtime.getRuntime().exec(cmdarray, null, cwd);
270             }
271
272             long startTime = System.nanoTime();
273             int exitValue = Integer.MIN_VALUE;
274             InputStream is = process.getInputStream();
275             InputStream es = process.getErrorStream();
276             while (true) {
277                 try {
278                     long endTime = System.nanoTime();
279                     //System.out.println("Checking for process exit value");
280                     exitValue = process.exitValue();
281                     System.out.println("finished in " + (endTime-startTime)*1e-6 + "ms");
282                     System.out.println("exit value: " + exitValue);
283                 } catch (IllegalThreadStateException e) {
284                     try {
285                         int n = is.available(); 
286                         if (n > 0) {
287                             byte[] bytes = new byte[n];
288                             int nr = is.read(bytes);
289                             if (nr > 0)
290                                 System.out.println("DEBUG: STDOUT:" + org.simantics.team.Utils.bytesToStringASCII(bytes, 0, nr));
291                         }
292                         n = es.available();
293                         if (n > 0) {
294                             byte[] bytes = new byte[n];
295                             int nr = es.read(bytes);
296                             if (nr > 0)
297                                 System.out.println("DEBUG: STDERR:" + org.simantics.team.Utils.bytesToStringASCII(bytes, 0, nr));
298                         }
299                         Thread.sleep(100);
300                     } catch (InterruptedException e1) {
301                         e1.printStackTrace();
302                     }
303                     continue;
304                 }
305                 String ins = FileUtils.getContents(is);
306                 if (!ins.isEmpty())
307                     System.out.println("--- STDOUT ---\n" + ins);
308                 String errs = FileUtils.getContents(es);
309                 if (!errs.isEmpty())
310                     System.out.println("--- STDERR ---\n" + errs);
311                 break;
312             }
313
314             // #5849 workaround
315 //            xs.mergeClusterIdMap(stagingConfig.workspaceRoot.getAbsolutePath());
316
317             return new StagingResult(exitValue, logFile, null);
318         } finally {
319             faContext.ungetService(ref);
320         }
321     }
322
323     private static Set<String> startUpBundles = new HashSet<String>();
324     static {
325         //startUpBundles.add("org.eclipse.equinox.ds");
326         startUpBundles.add("org.eclipse.equinox.simpleconfigurator");
327     }
328
329     private static Set<String> startedBundles = new HashSet<String>();
330     static {
331         startedBundles.add("org.eclipse.core.runtime");
332     }
333
334     private static boolean isStartUpBundle(BundleInfo bi) {
335         return startUpBundles.contains(bi.getSymbolicName());
336     }
337
338     private static boolean isMarkedAsStarted(BundleInfo bi) {
339         // Prevent unnecessary bundles from being
340         // activated by only marking as started the plug-ins that
341         // are vital to the environment, which always have lower
342         // start level than the default 4.
343         return bi.isMarkedAsStarted() && (bi.getStartLevel() < 4 || startedBundles.contains(bi.getSymbolicName()));
344     }
345
346     private static void writeProperties(Properties properties, File target, String comment) throws IOException {
347         FileWriter writer = new FileWriter(target);
348         try {
349             properties.store(writer, comment);
350         } finally {
351             writer.close();
352         }
353     }
354
355     private static void writeFile(String string, File target) throws IOException {
356         FileWriter writer = new FileWriter(target);
357         try {
358             writer.write(string);
359         } finally {
360             writer.close();
361         }
362     }
363     static public final class StagingResult {
364
365         private final int exitValue;
366
367         private final File logFile;
368         
369         private final String messageLog;
370
371         public StagingResult(String message) {
372             this.exitValue = Integer.MIN_VALUE;
373             this.logFile = null;
374             this.messageLog = message;
375         }
376         public StagingResult(int exitValue, File logFile, String messageLog) {
377             this.exitValue = exitValue;
378             this.logFile = logFile;
379             this.messageLog = messageLog;
380         }
381
382         public int getExitValue() {
383             return exitValue;
384         }
385
386         public File getLogFile() {
387             return logFile;
388         }
389         
390         public String getMessageLog() {
391             return messageLog;
392         }
393
394     }
395     public static final class Config {
396         public final Session session;
397         public final Resource targetLibrary;
398         public final File workspaceRoot;
399         public final File clusterMapFolder;
400         public final File teamFolder;
401         public final PrintStream out;
402         // Outputs
403         public Resource           createdModel;
404         public Resource           createdState;
405         public List<String>       messages = new ArrayList<String>();
406         public String titlePrefix = "Staging";
407
408         public Config(Session session, Resource library, File workspaceRoot, File clusterMapFolder)
409         throws DatabaseException {
410             this(session, library, workspaceRoot, clusterMapFolder, null);
411         }
412
413         public Config( Session session, Resource library, File workspaceRoot, File clusterMapFolder, PrintStream out)
414         throws DatabaseException {
415             this.session = session;
416             this.targetLibrary = library;
417             this.workspaceRoot = workspaceRoot;
418             this.clusterMapFolder = clusterMapFolder;
419             this.teamFolder = org.simantics.team.Utils.getTeamFolder();
420             this.out = out;
421         }
422     }
423     static final class Constants {
424
425         /**
426          * Tells the launched application to dump all its system properties into the
427          * standard output / log.
428          */
429         public static final String PROP_DUMP_PROPERTIES = "dump.properties";
430
431         /**
432          * Controls where the launched application log is written. Specified as an URI.
433          */
434         public static final String PROP_LOGFILE = "staging.logfile";
435
436         /**
437          * Prefix for the window title.
438          */
439         public static final String PROP_WINDOW_TITLE = "staging.window.title";
440
441         /**
442          * Tells the launched application where its team data comes from.
443          */
444         public static final String PROP_TEAM_FOLDER = "staging.team.folder";
445
446         /**
447          * Workspace root directory of the workbench process that launched the
448          * import process.
449          */
450         public static final String PROP_WORKSPACE_ROOT    = "import.workspace.root";
451     }
452 }