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