]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.team.ui/src/org/simantics/team/internal/StagingLauncher.java
Migrated source code from Simantics SVN
[simantics/platform.git] / bundles / org.simantics.team.ui / src / org / simantics / team / internal / StagingLauncher.java
diff --git a/bundles/org.simantics.team.ui/src/org/simantics/team/internal/StagingLauncher.java b/bundles/org.simantics.team.ui/src/org/simantics/team/internal/StagingLauncher.java
new file mode 100644 (file)
index 0000000..7744d30
--- /dev/null
@@ -0,0 +1,452 @@
+package org.simantics.team.internal;\r
+\r
+import java.io.File;\r
+import java.io.FileWriter;\r
+import java.io.IOException;\r
+import java.io.InputStream;\r
+import java.io.PrintStream;\r
+import java.net.URLDecoder;\r
+import java.util.ArrayList;\r
+import java.util.Arrays;\r
+import java.util.HashSet;\r
+import java.util.LinkedList;\r
+import java.util.List;\r
+import java.util.Properties;\r
+import java.util.Set;\r
+\r
+import org.eclipse.core.runtime.Platform;\r
+import org.eclipse.equinox.frameworkadmin.BundleInfo;\r
+import org.eclipse.equinox.internal.frameworkadmin.equinox.EquinoxConstants;\r
+import org.eclipse.equinox.internal.frameworkadmin.utils.Utils;\r
+import org.eclipse.equinox.internal.provisional.frameworkadmin.ConfigData;\r
+import org.eclipse.equinox.internal.provisional.frameworkadmin.FrameworkAdmin;\r
+import org.eclipse.equinox.internal.provisional.frameworkadmin.FrameworkAdminRuntimeException;\r
+import org.eclipse.equinox.internal.provisional.frameworkadmin.LauncherData;\r
+import org.eclipse.equinox.internal.provisional.frameworkadmin.Manipulator;\r
+import org.osgi.framework.Bundle;\r
+import org.osgi.framework.BundleContext;\r
+import org.osgi.framework.BundleException;\r
+import org.osgi.framework.FrameworkUtil;\r
+import org.osgi.framework.InvalidSyntaxException;\r
+import org.osgi.framework.ServiceReference;\r
+import org.simantics.application.db.SocketUtils;\r
+import org.simantics.databoard.binding.error.BindingConstructionException;\r
+import org.simantics.db.Resource;\r
+import org.simantics.db.Session;\r
+import org.simantics.db.exception.DatabaseException;\r
+import org.simantics.utils.FileUtils;\r
+import org.simantics.utils.strings.EString;\r
+\r
+@SuppressWarnings("restriction")\r
+public final class StagingLauncher {\r
+    private static final boolean DEBUG = true;\r
+    private static final boolean DEBUG_EXEC = true;\r
+    private static final boolean REMOTE_DEBUG_DISABLED = true;\r
+    public static StagingResult launch(\r
+            final Config stagingConfig,\r
+            String serverAddress,\r
+            String targetResourceId)\r
+    throws InvalidSyntaxException, IllegalArgumentException,\r
+            FrameworkAdminRuntimeException, IOException, BindingConstructionException, DatabaseException {\r
+        Bundle dsBundle = Platform.getBundle("org.eclipse.equinox.ds");\r
+        try {\r
+            dsBundle.start(/*Bundle.START_TRANSIENT*/);\r
+            if (DEBUG)\r
+                System.out.println("state="+dsBundle.getState());\r
+        } catch (BundleException ex) {\r
+            throw new StagingException("Could not start org.eclipse.equinox.ds.", ex);\r
+        }\r
+        Bundle faBundle = FrameworkUtil.getBundle(FrameworkAdmin.class);\r
+        if (null == faBundle)\r
+            throw new StagingException("Bundle for FrameworkAdmin not available.");\r
+        BundleContext faContext = faBundle.getBundleContext();\r
+        if (null == faContext)\r
+            throw new StagingException("Context for FrameworkAdmin not available.");\r
+        ServiceReference<FrameworkAdmin> ref = faContext.getServiceReference(FrameworkAdmin.class);\r
+        if (ref == null)\r
+            throw new StagingException("Reference for FrameworkAdmin not available.");\r
+        FrameworkAdmin admin = (FrameworkAdmin)faContext.getService(ref);\r
+        if (null == admin)\r
+            throw new StagingException("FrameworkAdmin not available.");\r
+        try {\r
+            Manipulator rmanip = admin.getRunningManipulator();\r
+            if (rmanip == null)\r
+                throw new StagingException("No FrameworkAdmin Manipulator available for the currently running environment.");\r
+            if (DEBUG)\r
+                System.out.println("FrameworkAdmin Manipulator of the running environment:\n" + rmanip);\r
+            Properties system = System.getProperties();\r
+            ConfigData rcd = rmanip.getConfigData();\r
+            LauncherData rld = rmanip.getLauncherData();\r
+            Manipulator manip = admin.getManipulator();\r
+            manip.setConfigData(rcd);\r
+            manip.setLauncherData(rld);\r
+            ConfigData cd = manip.getConfigData();\r
+            LauncherData ld = manip.getLauncherData();\r
+            Properties config = new Properties();\r
+            StringBuilder osgiBundles = new StringBuilder(1024);\r
+            StringBuilder bundlesInfo = new StringBuilder(1024);\r
+            bundlesInfo.append("#version=1\n");\r
+            for (BundleInfo bi : cd.getBundles()) {\r
+                boolean started = isMarkedAsStarted(bi);\r
+                bundlesInfo\r
+                .append(bi.getSymbolicName())\r
+                .append(",")\r
+                .append(bi.getVersion())\r
+                .append(",")\r
+                //.append(bi.getLocation().toString())\r
+                .append(URLDecoder.decode(bi.getLocation().toString(), "UTF-8"))\r
+                .append(",")\r
+                .append(bi.getStartLevel())\r
+                .append(",")\r
+                .append(started)\r
+                .append("\n");\r
+                if (DEBUG) {\r
+                    System.out.println("bundle: " + bi.getSymbolicName() + "\n\t" + bi.getLocation().toString() + "\n\t" + started);\r
+                    if (started)\r
+                        System.out.println("IS STARTED: bundle: " + bi.getSymbolicName() + "\n\t" + bi.getLocation().toString());\r
+                    if (!started && bi.isMarkedAsStarted())\r
+                        System.out.println("NOT STARTED, BUT WAS MARKED AS STARTED: bundle: " + bi.getSymbolicName() + "\n\t" + bi.getLocation().toString());\r
+                }\r
+                if (isStartUpBundle(bi)) {\r
+                    if (osgiBundles.length() > 0) {\r
+                        osgiBundles.append(",");\r
+                    }\r
+                    osgiBundles\r
+                    .append("reference:")\r
+                    .append(URLDecoder.decode(bi.getLocation().toString(), "UTF-8"))\r
+                    .append("@")\r
+                    .append(bi.getStartLevel())\r
+                    .append(":start");\r
+                }\r
+            }\r
+            //File cwd = new File(system.getProperty("user.dir"));\r
+            File javaHome = new File(system.getProperty("java.home"));\r
+            String installArea = system.getProperty("osgi.install.area");\r
+            config.setProperty("eclipse.application", "org.simantics.workbench.application");\r
+            config.setProperty("eclipse.product", "org.simantics.devs3.ui.product");\r
+            //config.setProperty("eclipse.consoleLog", "");\r
+            config.setProperty("org.eclipse.update.reconcile", "false");\r
+            config.setProperty("osgi.bundles", osgiBundles.toString());\r
+            config.setProperty("osgi.bundles.defaultStartLevel", "4");\r
+            //config.setProperty("osgi.clean", "true");\r
+            //config.setProperty("osgi.configuration.area", "@default");\r
+            config.setProperty("osgi.configuration.cascaded", "false");\r
+            //config.setProperty("osgi.console", "");\r
+            //config.setProperty("osgi.debug", "");\r
+            config.setProperty("osgi.framework", URLDecoder.decode(ld.getFwJar().toURI().toString(), "UTF-8"));\r
+            //config.setProperty("osgi.noShutdown", "");\r
+            config.setProperty("osgi.install.area", installArea);\r
+            config.setProperty("osgi.instance.area", stagingConfig.workspaceRoot.getAbsolutePath());\r
+//              cd.setProperty("osgi.instance.area", "@none");\r
+            config.setProperty("osgi.user.area", "@none");\r
+            // Eclipse 3.6, not sure what this is for but modern apps have it.\r
+            config.setProperty("equinox.use.ds", "true");\r
+            // Ignore INFO level log messages\r
+            config.setProperty("eclipse.log.level", "WARNING");\r
+            File configDir = new File(stagingConfig.workspaceRoot, "configuration");\r
+            File simpleConfiguratorDir = new File(configDir, "org.eclipse.equinox.simpleconfigurator");\r
+            simpleConfiguratorDir.mkdirs();\r
+            File bundlesInfoFile = new File(simpleConfiguratorDir, "bundles.info");\r
+            config.setProperty("org.eclipse.equinox.simpleconfigurator.configUrl", URLDecoder.decode(bundlesInfoFile.toURI().toString(), "UTF-8"));\r
+            File logFile = new File(stagingConfig.workspaceRoot, "db-client.log");\r
+            writeProperties(config, new File(configDir, "config.ini"), "This configuration file was written by: " + StagingLauncher.class.getCanonicalName());\r
+            writeFile(bundlesInfo.toString(), bundlesInfoFile);\r
+\r
+            ld.setJvm(new File(new File(javaHome, "bin"), "java"));\r
+            ld.setFwConfigLocation(configDir);\r
+            ld.setFwPersistentDataLocation(configDir, false);\r
+            ld.setLauncher(null);\r
+            int maxHeap = 768; /*prefs.getInt(Activator.PLUGIN_ID, ImportPreferences.PREF_IMPORT_PROCESS_MAX_HEAP,\r
+                    ImportPreferences.getDefaultImportProcessMaxHeap(), preferenceScopes);\r
+            // Just a safety if preferences have not been properly initialized for some reason.\r
+            if (maxHeap == 0)\r
+                maxHeap = ImportPreferences.getDefaultImportProcessMaxHeap();*/\r
+            ld.addJvmArg("-Xmx" + maxHeap + "m");\r
+            ld.addJvmArg("-Xms" + maxHeap + "m");\r
+            // Enable assertions to have faster failure in db client routines with improper input.\r
+            ld.addJvmArg("-ea");\r
+            // For supporting OSGi dev mode (launched from IDE)\r
+            if (Platform.inDevelopmentMode()) {\r
+                // TODO: %osgi.dev is not escaped, it can contain whitespace\r
+                // but this doesn't seem to matter ?\r
+                ld.addJvmArg("-Dosgi.dev=" + system.getProperty("osgi.dev"));\r
+            }\r
+            ld.addJvmArg("-Dosgi.arch=" + rcd.getProperty("osgi.arch"));\r
+            ld.addJvmArg("-Dosgi.os=" + rcd.getProperty("osgi.os"));\r
+            ld.addJvmArg("-Dosgi.ws=" + rcd.getProperty("osgi.ws"));\r
+            ld.addJvmArg("-Dosgi.nl=" + rcd.getProperty("osgi.nl"));\r
+\r
+            // WORKAROUND for a problem in org.eclipse.ecf fragment org.eclipse.ecf.ssl.\r
+            // See https://bugs.eclipse.org/bugs/show_bug.cgi?id=316500\r
+            // Either one of these works, THESE:\r
+            //ld.addJvmArg("-Dosgi.java.profile.bootdelegation=override");\r
+            //ld.addJvmArg("-Dorg.osgi.framework.bootdelegation=sun.*, com.sun.*, javax.*");\r
+            // OR:\r
+            ld.addJvmArg("-Dosgi.compatibility.bootdelegation=true");\r
+\r
+            // Enable remote debugging when in osgi dev mode.\r
+            if (Platform.inDevelopmentMode())\r
+                if (!REMOTE_DEBUG_DISABLED) {\r
+                    ld.addJvmArg("-Xdebug");\r
+                    ld.addJvmArg("-Xnoagent");\r
+                    int port = SocketUtils.getFreeEphemeralPort();\r
+                    ld.addJvmArg("-Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=" + port);\r
+                }\r
+\r
+            ld.addJvmArg("-D" + Constants.PROP_DUMP_PROPERTIES);\r
+            ld.addJvmArg("-D" + Constants.PROP_LOGFILE + "=" + logFile.toURI().toString());\r
+            String titleArgument = System.getProperty(Constants.PROP_WINDOW_TITLE);\r
+            if (null != titleArgument && !titleArgument.equals(""))\r
+                titleArgument = stagingConfig.titlePrefix + " " + titleArgument;\r
+            else\r
+                titleArgument = stagingConfig.titlePrefix;\r
+            ld.addJvmArg("-D" + Constants.PROP_WINDOW_TITLE + "=" + titleArgument);\r
+            if (null != stagingConfig.teamFolder)\r
+                ld.addJvmArg("-D" + Constants.PROP_TEAM_FOLDER + "=" + stagingConfig.teamFolder.getAbsolutePath()); \r
+            ld.addJvmArg("-D" + Constants.PROP_WORKSPACE_ROOT + "=" + stagingConfig.workspaceRoot.toURI().toString());\r
+            if (DEBUG)\r
+                System.out.println("JVM ARGS: " + Arrays.toString(ld.getJvmArgs()));\r
+\r
+            // Using this means that\r
+            //  * config.ini must not be written self\r
+            //  * parent program bundles.info can be reused (this could be done in any case)\r
+            //  * running platform configuration must not be copied completely, only selected parts of\r
+            //manip.save(false);\r
+\r
+            // #5849 workaround\r
+//            XSupport xs = stagingConfig.session.getService(XSupport.class);\r
+//            xs.initClusterIdMap(stagingConfig.workspaceRoot.getAbsolutePath());\r
+            if (null != stagingConfig.clusterMapFolder) {\r
+                String file = "clusterIdMap.dat";\r
+                File f = new File(stagingConfig.clusterMapFolder, file);\r
+                File tFolder = new File(stagingConfig.workspaceRoot, ".metadata/.plugins/org.simantics.db.procore");\r
+                tFolder.mkdirs();\r
+                FileUtils.copyFile(f, new File(tFolder, file));\r
+                String file2 = "nextId.dat";\r
+                File f2 = new File(stagingConfig.clusterMapFolder, file2);\r
+                File tFolder2 = new File(stagingConfig.workspaceRoot, "db");\r
+                FileUtils.copyFile(f2, new File(tFolder2, file2));\r
+            }\r
+            if (DEBUG)\r
+                System.out.println("LAUNCHING\n" + manip);\r
+            Process process;\r
+            {\r
+                LauncherData launcherData = ld;\r
+                if (DEBUG)\r
+                    System.out.println("Framework JAR: " + ld.getFwJar().toURI().toString());\r
+                Utils.checkAbsoluteFile(launcherData.getFwJar(), "fwJar"); //$NON-NLS-1$\r
+                File cwd = stagingConfig.workspaceRoot;\r
+                Utils.checkAbsoluteDir(cwd, "cwd"); //$NON-NLS-1$\r
+\r
+                List<String> cmdList = new LinkedList<String>();\r
+                if (launcherData.getJvm() != null)\r
+                    cmdList.add(launcherData.getJvm().getAbsolutePath());\r
+                else\r
+                    cmdList.add("java"); //$NON-NLS-1$\r
+\r
+                if (launcherData.getJvmArgs() != null)\r
+                    for (int i = 0; i < launcherData.getJvmArgs().length; i++)\r
+                        cmdList.add(launcherData.getJvmArgs()[i]);\r
+\r
+                cmdList.add("-jar"); //$NON-NLS-1$\r
+                cmdList.add("\"" + launcherData.getFwJar().getAbsolutePath() + "\"");\r
+\r
+                //EquinoxManipulatorImpl.checkConsistencyOfFwConfigLocAndFwPersistentDataLoc(launcherData);\r
+                cmdList.add(EquinoxConstants.OPTION_CONFIGURATION);\r
+                cmdList.add("\"" + launcherData.getFwPersistentDataLocation().getAbsolutePath() + "\"");\r
+\r
+                cmdList.add("-data");\r
+                cmdList.add(stagingConfig.workspaceRoot.getAbsolutePath());\r
+\r
+                if (launcherData.isClean())\r
+                    cmdList.add(EquinoxConstants.OPTION_CLEAN);\r
+\r
+                String[] cmdarray = cmdList.toArray(new String[cmdList.size()]);\r
+\r
+                if (DEBUG_EXEC)\r
+                    System.out.println("Launching import, CWD=" + cwd + "\n\t" + EString.implode(cmdarray, "\n\t"));\r
+\r
+                process = Runtime.getRuntime().exec(cmdarray, null, cwd);\r
+            }\r
+\r
+            long startTime = System.nanoTime();\r
+            int exitValue = Integer.MIN_VALUE;\r
+            InputStream is = process.getInputStream();\r
+            InputStream es = process.getErrorStream();\r
+            while (true) {\r
+                try {\r
+                    long endTime = System.nanoTime();\r
+                    //System.out.println("Checking for process exit value");\r
+                    exitValue = process.exitValue();\r
+                    System.out.println("finished in " + (endTime-startTime)*1e-6 + "ms");\r
+                    System.out.println("exit value: " + exitValue);\r
+                } catch (IllegalThreadStateException e) {\r
+                    try {\r
+                        int n = is.available(); \r
+                        if (n > 0) {\r
+                            byte[] bytes = new byte[n];\r
+                            int nr = is.read(bytes);\r
+                            if (nr > 0)\r
+                                System.out.println("DEBUG: STDOUT:" + org.simantics.team.Utils.bytesToStringASCII(bytes, 0, nr));\r
+                        }\r
+                        n = es.available();\r
+                        if (n > 0) {\r
+                            byte[] bytes = new byte[n];\r
+                            int nr = es.read(bytes);\r
+                            if (nr > 0)\r
+                                System.out.println("DEBUG: STDERR:" + org.simantics.team.Utils.bytesToStringASCII(bytes, 0, nr));\r
+                        }\r
+                        Thread.sleep(100);\r
+                    } catch (InterruptedException e1) {\r
+                        e1.printStackTrace();\r
+                    }\r
+                    continue;\r
+                }\r
+                String ins = FileUtils.getContents(is);\r
+                if (!ins.isEmpty())\r
+                    System.out.println("--- STDOUT ---\n" + ins);\r
+                String errs = FileUtils.getContents(es);\r
+                if (!errs.isEmpty())\r
+                    System.out.println("--- STDERR ---\n" + errs);\r
+                break;\r
+            }\r
+\r
+            // #5849 workaround\r
+//            xs.mergeClusterIdMap(stagingConfig.workspaceRoot.getAbsolutePath());\r
+\r
+            return new StagingResult(exitValue, logFile, null);\r
+        } finally {\r
+            faContext.ungetService(ref);\r
+        }\r
+    }\r
+\r
+    private static Set<String> startUpBundles = new HashSet<String>();\r
+    static {\r
+        //startUpBundles.add("org.eclipse.equinox.ds");\r
+        startUpBundles.add("org.eclipse.equinox.simpleconfigurator");\r
+    }\r
+\r
+    private static Set<String> startedBundles = new HashSet<String>();\r
+    static {\r
+        startedBundles.add("org.eclipse.core.runtime");\r
+    }\r
+\r
+    private static boolean isStartUpBundle(BundleInfo bi) {\r
+        return startUpBundles.contains(bi.getSymbolicName());\r
+    }\r
+\r
+    private static boolean isMarkedAsStarted(BundleInfo bi) {\r
+        // Prevent unnecessary bundles from being\r
+        // activated by only marking as started the plug-ins that\r
+        // are vital to the environment, which always have lower\r
+        // start level than the default 4.\r
+        return bi.isMarkedAsStarted() && (bi.getStartLevel() < 4 || startedBundles.contains(bi.getSymbolicName()));\r
+    }\r
+\r
+    private static void writeProperties(Properties properties, File target, String comment) throws IOException {\r
+        FileWriter writer = new FileWriter(target);\r
+        try {\r
+            properties.store(writer, comment);\r
+        } finally {\r
+            writer.close();\r
+        }\r
+    }\r
+\r
+    private static void writeFile(String string, File target) throws IOException {\r
+        FileWriter writer = new FileWriter(target);\r
+        try {\r
+            writer.write(string);\r
+        } finally {\r
+            writer.close();\r
+        }\r
+    }\r
+    static public final class StagingResult {\r
+\r
+        private final int exitValue;\r
+\r
+        private final File logFile;\r
+        \r
+        private final String messageLog;\r
+\r
+        public StagingResult(String message) {\r
+            this.exitValue = Integer.MIN_VALUE;\r
+            this.logFile = null;\r
+            this.messageLog = message;\r
+        }\r
+        public StagingResult(int exitValue, File logFile, String messageLog) {\r
+            this.exitValue = exitValue;\r
+            this.logFile = logFile;\r
+            this.messageLog = messageLog;\r
+        }\r
+\r
+        public int getExitValue() {\r
+            return exitValue;\r
+        }\r
+\r
+        public File getLogFile() {\r
+            return logFile;\r
+        }\r
+        \r
+        public String getMessageLog() {\r
+            return messageLog;\r
+        }\r
+\r
+    }\r
+    public static final class Config {\r
+        public final Session session;\r
+        public final Resource targetLibrary;\r
+        public final File workspaceRoot;\r
+        public final File clusterMapFolder;\r
+        public final File teamFolder;\r
+        public final PrintStream out;\r
+        // Outputs\r
+        public Resource           createdModel;\r
+        public Resource           createdState;\r
+        public List<String>       messages = new ArrayList<String>();\r
+        public String titlePrefix = "Staging";\r
+\r
+        public Config(Session session, Resource library, File workspaceRoot, File clusterMapFolder)\r
+        throws DatabaseException {\r
+            this(session, library, workspaceRoot, clusterMapFolder, null);\r
+        }\r
+\r
+        public Config( Session session, Resource library, File workspaceRoot, File clusterMapFolder, PrintStream out)\r
+        throws DatabaseException {\r
+            this.session = session;\r
+            this.targetLibrary = library;\r
+            this.workspaceRoot = workspaceRoot;\r
+            this.clusterMapFolder = clusterMapFolder;\r
+            this.teamFolder = org.simantics.team.Utils.getTeamFolder();\r
+            this.out = out;\r
+        }\r
+    }\r
+    static final class Constants {\r
+\r
+        /**\r
+         * Tells the launched application to dump all its system properties into the\r
+         * standard output / log.\r
+         */\r
+        public static final String PROP_DUMP_PROPERTIES = "dump.properties";\r
+\r
+        /**\r
+         * Controls where the launched application log is written. Specified as an URI.\r
+         */\r
+        public static final String PROP_LOGFILE = "staging.logfile";\r
+\r
+        /**\r
+         * Prefix for the window title.\r
+         */\r
+        public static final String PROP_WINDOW_TITLE = "staging.window.title";\r
+\r
+        /**\r
+         * Tells the launched application where its team data comes from.\r
+         */\r
+        public static final String PROP_TEAM_FOLDER = "staging.team.folder";\r
+\r
+        /**\r
+         * Workspace root directory of the workbench process that launched the\r
+         * import process.\r
+         */\r
+        public static final String PROP_WORKSPACE_ROOT    = "import.workspace.root";\r
+    }\r
+}\r