]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.project/src/org/simantics/project/management/ServerManager.java
Migrated source code from Simantics SVN
[simantics/platform.git] / bundles / org.simantics.project / src / org / simantics / project / management / ServerManager.java
diff --git a/bundles/org.simantics.project/src/org/simantics/project/management/ServerManager.java b/bundles/org.simantics.project/src/org/simantics/project/management/ServerManager.java
new file mode 100644 (file)
index 0000000..56cf325
--- /dev/null
@@ -0,0 +1,496 @@
+/*******************************************************************************\r
+ * Copyright (c) 2007, 2010 Association for Decentralized Information Management\r
+ * in Industry THTH ry.\r
+ * All rights reserved. This program and the accompanying materials\r
+ * are made available under the terms of the Eclipse Public License v1.0\r
+ * which accompanies this distribution, and is available at\r
+ * http://www.eclipse.org/legal/epl-v10.html\r
+ *\r
+ * Contributors:\r
+ *     VTT Technical Research Centre of Finland - initial API and implementation\r
+ *******************************************************************************/\r
+package org.simantics.project.management;\r
+\r
+import java.io.File;\r
+import java.io.FileOutputStream;\r
+import java.io.IOException;\r
+import java.io.InputStream;\r
+import java.net.BindException;\r
+import java.net.Socket;\r
+import java.util.Collections;\r
+import java.util.HashMap;\r
+import java.util.Map;\r
+import java.util.Properties;\r
+\r
+import org.apache.log4j.Logger;\r
+import org.simantics.databoard.util.StreamUtil;\r
+import org.simantics.db.Driver;\r
+import org.simantics.db.Driver.Management;\r
+import org.simantics.db.ReadGraph;\r
+import org.simantics.db.ServerEx;\r
+import org.simantics.db.ServerI;\r
+import org.simantics.db.ServiceLocator;\r
+import org.simantics.db.Session;\r
+import org.simantics.db.WriteOnlyGraph;\r
+import org.simantics.db.common.request.ReadRequest;\r
+import org.simantics.db.common.request.WriteOnlyRequest;\r
+import org.simantics.db.exception.DatabaseException;\r
+import org.simantics.db.service.ClusterUID;\r
+import org.simantics.db.service.XSupport;\r
+import org.simantics.graph.db.CoreInitialization;\r
+import org.simantics.layer0.DatabaseManagementResource;\r
+import org.simantics.layer0.Layer0;\r
+\r
+/**\r
+ * Server Manager handles starting and pooling of ProCore server instances.\r
+ *\r
+ * @author Toni Kalajainen <toni.kalajainen@vtt.fi>\r
+ */\r
+public class ServerManager {\r
+\r
+       /** Default properties with default user and password */\r
+       public static final Properties DEFAULT;\r
+\r
+       /** Driver for database." */\r
+       final Driver driver;\r
+\r
+       /** Actual Server Instances. This object is synchronized by itself as lock. */\r
+       Map<File, ServerHost> servers = Collections.synchronizedMap( new HashMap<File, ServerHost>() );\r
+\r
+       /**\r
+        * Create a new server manager.\r
+        *\r
+        * @param applicationDirectory location of org.simantics.db.build\r
+        * @throws IOException\r
+        */\r
+    public ServerManager(Driver driver) throws IOException {\r
+        this.driver = driver;\r
+    }\r
+    public Management getManagement(File dbFolder) throws DatabaseException {\r
+        // We are using ProCoreDriver and know it's address format and security model. Not good!\r
+        return driver.getManagement(dbFolder.getAbsolutePath(), null);\r
+    }\r
+       /**\r
+        * Create a new database that is initialized with given graphs.\r
+        * One of them must be layer0.\r
+        * Database directory is created if it did not exist.\r
+        *\r
+        * @param databaseDirectory place where database is installed\r
+        * @param initialGraphs initialGraphs to install\r
+        * @throws DatabaseException\r
+        */\r
+       public Session createDatabase(File databaseDirectory) throws DatabaseException {\r
+               try {\r
+                       Logger myLogger = Logger.getLogger(ServerManager.class);\r
+                       myLogger.debug("Creating database to "+ databaseDirectory);\r
+\r
+            Session session = null;\r
+            ServerEx server1 = getServer(databaseDirectory);\r
+            server1.start();\r
+                       try {\r
+                               // This will initialize the fixed URIs and corresponding resources.\r
+                               // These are needed by the query system to parse URIs.\r
+                               // The server will generate the clusters for the generated resources.\r
+                               // The layer0 statements will be generated in phase two.\r
+                               // This will close the connection to server because the only thing\r
+                               // you can do with this connection is to initialize the fixed URIs.\r
+                               Properties info = new Properties();\r
+                               info.setProperty("user", "Default User");\r
+                               info.setProperty("password", "");\r
+                session = server1.createSession(info);\r
+                XSupport xs = session.getService(XSupport.class);\r
+                ClusterUID[] clusters = xs.listClusters();\r
+                if (clusters.length > 1) {// Database contain clusters, assuming initialization is done.");\r
+                    ReadRequest req = new ReadRequest() {\r
+                        @Override\r
+                        public void run(ReadGraph g) {\r
+                            // Registers Layer0 with the session ServiceLocator.\r
+                            Layer0.getInstance(g);\r
+                        }\r
+                    };\r
+                    session.syncRequest(req);\r
+                    return session;\r
+                }\r
+                CoreInitialization.initializeBuiltins(session);\r
+                               // This will try to initialize Builtins.class but because there is no statements\r
+                               // in the server only the previously added fixed URIs are found.\r
+                               // If we'd want to get rid of the missing layer0 URI warnings then\r
+                               // a non initialized session should be used to add graph statements\r
+                               // without using Builtins.class at all or by initializing Builtins.class\r
+                               // only with the fixed URIs.\r
+                           session.getService(XSupport.class).setServiceMode(true, true);\r
+\r
+                               // This will add layer0 statements. The query mechanism is not\r
+                               // yet totally functional because there is no statements in the\r
+                               // server. Mainly WriteOnly request is available here.\r
+                GraphBundle l0 = PlatformUtil.getGraph("org.simantics.layer0");\r
+                final GraphBundleEx l0ex = GraphBundleEx.extend(l0);\r
+                l0ex.build();\r
+                               long[] resourceArray = CoreInitialization.initializeGraph(session, l0ex.getGraph());\r
+                               l0ex.setResourceArray(resourceArray);\r
+                               session.getService(XSupport.class).setServiceMode(true, true);\r
+\r
+                               DatabaseManagementResource.getInstance(session);\r
+                Layer0.getInstance(session);\r
+                               session.syncRequest(new WriteOnlyRequest() {\r
+                                       @Override\r
+                                       public void perform(WriteOnlyGraph graph) throws DatabaseException {\r
+                                           // Root Library is a cluster set\r
+                                           graph.newClusterSet(graph.getRootLibrary());\r
+                                               DatabaseManagement mgt = new DatabaseManagement();\r
+                                               mgt.createGraphBundle(graph, l0ex);\r
+                                               graph.flushCluster();\r
+                                       }});\r
+                   return session;\r
+                       } finally {\r
+                           if (null == session)\r
+                               server1.stop();\r
+                       }\r
+               } catch (Exception e) {\r
+                       throw new DatabaseException("Failed to create Simantics database.", e);\r
+               }\r
+       }\r
+\r
+       /**\r
+        * Get a server that can be started and stopped.\r
+        *\r
+        * The result is actually a proxy server. Each successful start() increases\r
+        * reference count and stop() decreases. The actual server is closed\r
+        * once all proxies are closed.\r
+        *\r
+        * @param databaseDirectory\r
+        * @return server\r
+        * @throws DatabaseException\r
+        */\r
+       private ServerEx getServer(File databaseDirectory) throws DatabaseException {\r
+               File file = databaseDirectory.getAbsoluteFile();\r
+\r
+               ServerHost host = null;\r
+               synchronized(servers) {\r
+                       host = servers.get(file);\r
+                       if (host==null) {\r
+                               // Instantiate actual server. We are using ProCoreDriver and know it's address format and security model. Not good!\r
+                               ServerI server = driver.getServer(file.getAbsolutePath(), null);\r
+\r
+                               try {\r
+                                       host = new ServerHost(server, databaseDirectory);\r
+                               } catch (IOException e) {\r
+                                       throw new DatabaseException("Failed to load " + databaseDirectory, e);\r
+                               }\r
+\r
+                               servers.put(file, host);\r
+                       }\r
+               }\r
+\r
+               ServerEx proxy = new ProxyServer(host);\r
+               return proxy;\r
+       }\r
+\r
+       /**\r
+        * @param parseUnresolved\r
+        * @return\r
+        */\r
+//     public ServerEx getServer(ServerAddress endpoint) {\r
+//             return new ConnectedServer(endpoint);\r
+//     }\r
+\r
+       /**\r
+        * Close the server manager, close all servers.\r
+        * Deletes temporary files.\r
+        */\r
+       public void close() {\r
+               synchronized(servers) {\r
+                       for (ServerHost host : servers.values()) {\r
+                               ServerI server = host.actual;\r
+                try {\r
+                    if (server.isActive())\r
+                        server.stop();\r
+                } catch (DatabaseException e) {\r
+                    Logger myLogger = Logger.getLogger(ServerManager.class);\r
+                    myLogger.error(e);\r
+                }\r
+                       }\r
+                       servers.clear();\r
+               }\r
+       }\r
+\r
+    public static int getFreeEphemeralPort() {\r
+        while(true) {\r
+            try {\r
+                Socket s = null;\r
+                try {\r
+                    s = new Socket();\r
+                    s.bind(null);\r
+                    return s.getLocalPort();\r
+                } finally {\r
+                    if (s != null)\r
+                        s.close();\r
+                }\r
+            } catch(BindException e) {\r
+                // Nothing to do, try next port\r
+            } catch (Throwable e) {\r
+                throw new Error(e);\r
+            }\r
+        }\r
+    }\r
+\r
+    public static void createServerConfig(File file) throws IOException {\r
+               InputStream is = ServerManager.class.getResourceAsStream("server_template.cnfg");\r
+               byte[] data = StreamUtil.readFully(is);\r
+               is.close();\r
+\r
+               FileOutputStream os = new FileOutputStream(file, false);\r
+               os.write(data);\r
+               Properties properties = new Properties();\r
+               properties.store(os, "# automatically generated properties");\r
+               os.close();\r
+       }\r
+\r
+       /**\r
+        * ServerHost hosts a ServerI instance. For each successful start() a\r
+        * reference count is increased and each stop() & kill() it is decreased.\r
+        */\r
+       class ServerHost implements ServerEx {\r
+\r
+               File database;\r
+               ServerI actual;\r
+               int refCount = 0;\r
+               Properties properties;\r
+\r
+               public ServerHost(ServerI actual, File database)\r
+               throws IOException {\r
+                       this.actual = actual;\r
+                       this.database = database;\r
+                       this.properties = new Properties();\r
+               }\r
+\r
+               public File getDatabase() {\r
+                       return database;\r
+               }\r
+\r
+               /**\r
+                * Get properties\r
+                * @return properties\r
+                */\r
+               public Properties getProperties() {\r
+                       return properties;\r
+               }\r
+\r
+        @Override\r
+        public String getAddress()\r
+                throws DatabaseException {\r
+            return actual.getAddress();\r
+        }\r
+\r
+//        @Override\r
+//        public synchronized ServerAddress getServerAddress()\r
+//        throws DatabaseException {\r
+//            throw new DatabaseException("ServerHost.getServerAddress is not supported. Use getAddress instead.");\r
+//        }\r
+\r
+        @Override\r
+        public boolean isActive() {\r
+            try {\r
+                return actual.isActive();\r
+            } catch (DatabaseException e) {\r
+                return false;\r
+            }\r
+               }\r
+\r
+               /**\r
+                * Start server if refCount = 0. If running or start was successful\r
+                * the refcount is increased.\r
+                *\r
+                * For each succesful start(), a stop() or kill() is expected.\r
+                */\r
+               @Override\r
+               public void start() throws DatabaseException {\r
+                       boolean isRunning = actual.isActive();\r
+\r
+                       if (!isRunning) {\r
+                               actual.start();\r
+                       }\r
+\r
+                       refCount++;\r
+               }\r
+\r
+        @Override\r
+        public void stop() throws DatabaseException {\r
+            if (refCount <= 0)\r
+                throw new DatabaseException("Trying to stop a standing process.");\r
+            refCount--;\r
+            if (refCount > 1)\r
+                return;\r
+            actual.stop();\r
+        }\r
+\r
+               @Override\r
+               public Session createSession(Properties properties) throws DatabaseException {\r
+                       return driver.getSession(actual.getAddress(), properties);\r
+               }\r
+\r
+        @Override\r
+        public ServiceLocator getServiceLocator(Properties info) throws DatabaseException {\r
+            return createSession(info);\r
+        }\r
+\r
+        @Override\r
+        public String execute(String command) throws DatabaseException {\r
+            return actual.execute(command);\r
+        }\r
+\r
+        @Override\r
+        public String executeAndDisconnect(String command) throws DatabaseException {\r
+            return actual.executeAndDisconnect(command);\r
+        }\r
+       }\r
+\r
+       /**\r
+        * Proxy Server starts actual server (ServerHost) when first start():ed,\r
+        * and closes the actual server once all proxy servers are closed.\r
+        *\r
+        * @author Toni Kalajainen <toni.kalajainen@vtt.fi>\r
+        */\r
+       public class ProxyServer implements ServerEx {\r
+\r
+               boolean running;\r
+               ServerHost actual;\r
+\r
+               public ProxyServer(ServerHost actual) {\r
+                       this.actual = actual;\r
+               }\r
+\r
+               public File getDatabase() {\r
+                       return actual.getDatabase();\r
+               }\r
+\r
+               /**\r
+                * Get server properties\r
+                *\r
+                * @return properties\r
+                * @throws IOException\r
+                */\r
+               public Properties getProperties() {\r
+                       return actual.getProperties();\r
+               }\r
+\r
+        @Override\r
+        public String getAddress()\r
+        throws DatabaseException {\r
+            return actual.getAddress();\r
+        }\r
+\r
+//        @Override\r
+//        public synchronized ServerAddress getServerAddress()\r
+//        throws DatabaseException {\r
+//            return actual.getServerAddress();\r
+//        }\r
+\r
+               @Override\r
+               public boolean isActive() {\r
+                       return running && actual.isActive();\r
+               }\r
+\r
+               @Override\r
+               public void start() throws DatabaseException {\r
+                       if (running) return;\r
+                       actual.start();\r
+                       running = true;\r
+               }\r
+\r
+               @Override\r
+               public void stop() throws DatabaseException {\r
+                       if (!running) return;\r
+                       actual.stop();\r
+                       running = false;\r
+               }\r
+\r
+               @Override\r
+               public Session createSession(Properties properties) throws DatabaseException {\r
+                       return driver.getSession(actual.getAddress(), properties);\r
+               }\r
+\r
+               @Override\r
+               public ServiceLocator getServiceLocator(Properties info) throws DatabaseException {\r
+                       return createSession(info);\r
+               }\r
+\r
+        @Override\r
+        public String execute(String command) throws DatabaseException {\r
+            return actual.execute(command);\r
+        }\r
+\r
+        @Override\r
+        public String executeAndDisconnect(String command) throws DatabaseException {\r
+            return actual.executeAndDisconnect(command);\r
+        }\r
+       }\r
+\r
+//     public class ConnectedServer implements ServerEx {\r
+//\r
+//             ServerAddress endpoint;\r
+//\r
+//             public ConnectedServer(ServerAddress endpoint) {\r
+//                     this.endpoint = endpoint;\r
+//             }\r
+//\r
+//             @Override\r
+//             public void start() throws DatabaseException {\r
+//                     // Intentional NOP. Cannot control through socket.\r
+//             }\r
+//\r
+//             @Override\r
+//             public void stop() throws DatabaseException {\r
+//                     // Intentional NOP. Cannot control through socket.\r
+//             }\r
+//\r
+//             @Override\r
+//             public boolean isActive() {\r
+//                     // Without better knowledge\r
+//                     return true;\r
+//             }\r
+//\r
+//        @Override\r
+//        public String getAddress()\r
+//        throws DatabaseException {\r
+//            return endpoint.getDbid();\r
+//        }\r
+//\r
+//        @Override\r
+//        public synchronized ServerAddress getServerAddress()\r
+//                throws DatabaseException {\r
+//            return new ServerAddress(endpoint.getAddress());\r
+//        }\r
+//\r
+//        @Override\r
+//        public Session createSession(Properties properties)\r
+//        throws DatabaseException {\r
+//            return driver.getSession(getServerAddress().toString(), properties);\r
+//             }\r
+//\r
+//        @Override\r
+//        public ServiceLocator getServiceLocator(Properties info) throws DatabaseException {\r
+//            return createSession(info);\r
+//        }\r
+//\r
+//        @Override\r
+//        public String execute(String command) throws DatabaseException {\r
+//            // Intentional NOP. Cannot control through socket.\r
+//            return null;\r
+//        }\r
+//\r
+//        @Override\r
+//        public String executeAndDisconnect(String command) throws DatabaseException {\r
+//            // Intentional NOP. Cannot control through socket.\r
+//            return null;\r
+//        }\r
+//     }\r
+\r
+       static {\r
+               DEFAULT = new Properties();\r
+               DEFAULT.setProperty("user", "Default User");\r
+               DEFAULT.setProperty("password", "");\r
+       }\r
+\r
+}\r
+\r