1 package org.simantics.team.internal;
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;
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;
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,
48 String targetResourceId)
49 throws InvalidSyntaxException, IllegalArgumentException,
50 FrameworkAdminRuntimeException, IOException, BindingConstructionException, DatabaseException {
51 Bundle dsBundle = Platform.getBundle("org.eclipse.equinox.ds");
53 dsBundle.start(/*Bundle.START_TRANSIENT*/);
55 System.out.println("state="+dsBundle.getState());
56 } catch (BundleException ex) {
57 throw new StagingException("Could not start org.eclipse.equinox.ds.", ex);
59 Bundle faBundle = FrameworkUtil.getBundle(FrameworkAdmin.class);
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);
67 throw new StagingException("Reference for FrameworkAdmin not available.");
68 FrameworkAdmin admin = (FrameworkAdmin)faContext.getService(ref);
70 throw new StagingException("FrameworkAdmin not available.");
72 Manipulator rmanip = admin.getRunningManipulator();
74 throw new StagingException("No FrameworkAdmin Manipulator available for the currently running environment.");
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);
92 .append(bi.getSymbolicName())
94 .append(bi.getVersion())
96 //.append(bi.getLocation().toString())
97 .append(URLDecoder.decode(bi.getLocation().toString(), "UTF-8"))
99 .append(bi.getStartLevel())
104 System.out.println("bundle: " + bi.getSymbolicName() + "\n\t" + bi.getLocation().toString() + "\n\t" + 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());
110 if (isStartUpBundle(bi)) {
111 if (osgiBundles.length() > 0) {
112 osgiBundles.append(",");
115 .append("reference:")
116 .append(URLDecoder.decode(bi.getLocation().toString(), "UTF-8"))
118 .append(bi.getStartLevel())
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);
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.
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.
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"));
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"));
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.*");
185 ld.addJvmArg("-Dosgi.compatibility.bootdelegation=true");
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);
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;
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());
208 System.out.println("JVM ARGS: " + Arrays.toString(ld.getJvmArgs()));
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
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");
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));
231 System.out.println("LAUNCHING\n" + manip);
234 LauncherData launcherData = ld;
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$
241 List<String> cmdList = new LinkedList<String>();
242 if (launcherData.getJvm() != null)
243 cmdList.add(launcherData.getJvm().getAbsolutePath());
245 cmdList.add("java"); //$NON-NLS-1$
247 if (launcherData.getJvmArgs() != null)
248 for (int i = 0; i < launcherData.getJvmArgs().length; i++)
249 cmdList.add(launcherData.getJvmArgs()[i]);
251 cmdList.add("-jar"); //$NON-NLS-1$
252 cmdList.add("\"" + launcherData.getFwJar().getAbsolutePath() + "\"");
254 //EquinoxManipulatorImpl.checkConsistencyOfFwConfigLocAndFwPersistentDataLoc(launcherData);
255 cmdList.add(EquinoxConstants.OPTION_CONFIGURATION);
256 cmdList.add("\"" + launcherData.getFwPersistentDataLocation().getAbsolutePath() + "\"");
258 cmdList.add("-data");
259 cmdList.add(stagingConfig.workspaceRoot.getAbsolutePath());
261 if (launcherData.isClean())
262 cmdList.add(EquinoxConstants.OPTION_CLEAN);
264 String[] cmdarray = cmdList.toArray(new String[cmdList.size()]);
267 System.out.println("Launching import, CWD=" + cwd + "\n\t" + EString.implode(cmdarray, "\n\t"));
269 process = Runtime.getRuntime().exec(cmdarray, null, cwd);
272 long startTime = System.nanoTime();
273 int exitValue = Integer.MIN_VALUE;
274 InputStream is = process.getInputStream();
275 InputStream es = process.getErrorStream();
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) {
285 int n = is.available();
287 byte[] bytes = new byte[n];
288 int nr = is.read(bytes);
290 System.out.println("DEBUG: STDOUT:" + org.simantics.team.Utils.bytesToStringASCII(bytes, 0, nr));
294 byte[] bytes = new byte[n];
295 int nr = es.read(bytes);
297 System.out.println("DEBUG: STDERR:" + org.simantics.team.Utils.bytesToStringASCII(bytes, 0, nr));
300 } catch (InterruptedException e1) {
301 e1.printStackTrace();
305 String ins = FileUtils.getContents(is);
307 System.out.println("--- STDOUT ---\n" + ins);
308 String errs = FileUtils.getContents(es);
310 System.out.println("--- STDERR ---\n" + errs);
315 // xs.mergeClusterIdMap(stagingConfig.workspaceRoot.getAbsolutePath());
317 return new StagingResult(exitValue, logFile, null);
319 faContext.ungetService(ref);
323 private static Set<String> startUpBundles = new HashSet<String>();
325 //startUpBundles.add("org.eclipse.equinox.ds");
326 startUpBundles.add("org.eclipse.equinox.simpleconfigurator");
329 private static Set<String> startedBundles = new HashSet<String>();
331 startedBundles.add("org.eclipse.core.runtime");
334 private static boolean isStartUpBundle(BundleInfo bi) {
335 return startUpBundles.contains(bi.getSymbolicName());
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()));
346 private static void writeProperties(Properties properties, File target, String comment) throws IOException {
347 FileWriter writer = new FileWriter(target);
349 properties.store(writer, comment);
355 private static void writeFile(String string, File target) throws IOException {
356 FileWriter writer = new FileWriter(target);
358 writer.write(string);
363 static public final class StagingResult {
365 private final int exitValue;
367 private final File logFile;
369 private final String messageLog;
371 public StagingResult(String message) {
372 this.exitValue = Integer.MIN_VALUE;
374 this.messageLog = message;
376 public StagingResult(int exitValue, File logFile, String messageLog) {
377 this.exitValue = exitValue;
378 this.logFile = logFile;
379 this.messageLog = messageLog;
382 public int getExitValue() {
386 public File getLogFile() {
390 public String getMessageLog() {
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;
403 public Resource createdModel;
404 public Resource createdState;
405 public List<String> messages = new ArrayList<String>();
406 public String titlePrefix = "Staging";
408 public Config(Session session, Resource library, File workspaceRoot, File clusterMapFolder)
409 throws DatabaseException {
410 this(session, library, workspaceRoot, clusterMapFolder, null);
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();
423 static final class Constants {
426 * Tells the launched application to dump all its system properties into the
427 * standard output / log.
429 public static final String PROP_DUMP_PROPERTIES = "dump.properties";
432 * Controls where the launched application log is written. Specified as an URI.
434 public static final String PROP_LOGFILE = "staging.logfile";
437 * Prefix for the window title.
439 public static final String PROP_WINDOW_TITLE = "staging.window.title";
442 * Tells the launched application where its team data comes from.
444 public static final String PROP_TEAM_FOLDER = "staging.team.folder";
447 * Workspace root directory of the workbench process that launched the
450 public static final String PROP_WORKSPACE_ROOT = "import.workspace.root";