1 /*******************************************************************************
2 * Copyright (c) 2007, 2010 Association for Decentralized Information Management
4 * All rights reserved. This program and the accompanying materials
5 * are made available under the terms of the Eclipse Public License v1.0
6 * which accompanies this distribution, and is available at
7 * http://www.eclipse.org/legal/epl-v10.html
10 * VTT Technical Research Centre of Finland - initial API and implementation
11 *******************************************************************************/
12 package org.simantics.project.management;
15 import java.io.FileOutputStream;
16 import java.io.IOException;
17 import java.io.InputStream;
18 import java.net.BindException;
19 import java.net.Socket;
20 import java.util.Collections;
21 import java.util.HashMap;
23 import java.util.Properties;
25 import org.simantics.databoard.util.StreamUtil;
26 import org.simantics.db.Driver;
27 import org.simantics.db.Driver.Management;
28 import org.simantics.db.ReadGraph;
29 import org.simantics.db.ServerEx;
30 import org.simantics.db.ServerI;
31 import org.simantics.db.ServiceLocator;
32 import org.simantics.db.Session;
33 import org.simantics.db.WriteOnlyGraph;
34 import org.simantics.db.common.request.ReadRequest;
35 import org.simantics.db.common.request.WriteOnlyRequest;
36 import org.simantics.db.exception.DatabaseException;
37 import org.simantics.db.service.ClusterUID;
38 import org.simantics.db.service.XSupport;
39 import org.simantics.graph.db.CoreInitialization;
40 import org.simantics.layer0.DatabaseManagementResource;
41 import org.simantics.layer0.Layer0;
42 import org.slf4j.Logger;
43 import org.slf4j.LoggerFactory;
46 * Server Manager handles starting and pooling of ProCore server instances.
48 * @author Toni Kalajainen <toni.kalajainen@vtt.fi>
50 public class ServerManager {
51 private static final Logger LOGGER = LoggerFactory.getLogger(ServerManager.class);
53 /** Default properties with default user and password */
54 public static final Properties DEFAULT;
56 /** Driver for database." */
59 /** Actual Server Instances. This object is synchronized by itself as lock. */
60 Map<File, ServerHost> servers = Collections.synchronizedMap( new HashMap<File, ServerHost>() );
63 * Create a new server manager.
65 * @param applicationDirectory location of org.simantics.db.build
68 public ServerManager(Driver driver) throws IOException {
71 public Management getManagement(File dbFolder) throws DatabaseException {
72 // We are using ProCoreDriver and know it's address format and security model. Not good!
73 return driver.getManagement(dbFolder.getAbsolutePath(), null);
76 * Create a new database that is initialized with given graphs.
77 * One of them must be layer0.
78 * Database directory is created if it did not exist.
80 * @param databaseDirectory place where database is installed
81 * @param initialGraphs initialGraphs to install
82 * @throws DatabaseException
84 public Session createDatabase(File databaseDirectory) throws DatabaseException {
86 LOGGER.debug("Creating database to "+ databaseDirectory);
88 Session session = null;
89 ServerEx server1 = getServer(databaseDirectory);
92 // This will initialize the fixed URIs and corresponding resources.
93 // These are needed by the query system to parse URIs.
94 // The server will generate the clusters for the generated resources.
95 // The layer0 statements will be generated in phase two.
96 // This will close the connection to server because the only thing
97 // you can do with this connection is to initialize the fixed URIs.
98 Properties info = new Properties();
99 info.setProperty("user", "Default User");
100 info.setProperty("password", "");
101 session = server1.createSession(info);
102 XSupport xs = session.getService(XSupport.class);
103 ClusterUID[] clusters = xs.listClusters();
104 if (clusters.length > 1) {// Database contain clusters, assuming initialization is done.");
105 ReadRequest req = new ReadRequest() {
107 public void run(ReadGraph g) {
108 // Registers Layer0 with the session ServiceLocator.
109 Layer0.getInstance(g);
112 session.syncRequest(req);
115 CoreInitialization.initializeBuiltins(session);
116 // This will try to initialize Builtins.class but because there is no statements
117 // in the server only the previously added fixed URIs are found.
118 // If we'd want to get rid of the missing layer0 URI warnings then
119 // a non initialized session should be used to add graph statements
120 // without using Builtins.class at all or by initializing Builtins.class
121 // only with the fixed URIs.
122 session.getService(XSupport.class).setServiceMode(true, true);
124 // This will add layer0 statements. The query mechanism is not
125 // yet totally functional because there is no statements in the
126 // server. Mainly WriteOnly request is available here.
127 GraphBundle l0 = PlatformUtil.getGraph("org.simantics.layer0");
128 final GraphBundleEx l0ex = GraphBundleEx.extend(l0);
130 long[] resourceArray = CoreInitialization.initializeGraph(session, l0ex.getGraph());
131 l0ex.setResourceArray(resourceArray);
132 session.getService(XSupport.class).setServiceMode(true, true);
134 DatabaseManagementResource.getInstance(session);
135 Layer0.getInstance(session);
136 session.syncRequest(new WriteOnlyRequest() {
138 public void perform(WriteOnlyGraph graph) throws DatabaseException {
139 // Root Library is a cluster set
140 graph.newClusterSet(graph.getRootLibrary());
141 DatabaseManagement mgt = new DatabaseManagement();
142 mgt.createGraphBundle(graph, l0ex);
143 graph.flushCluster();
150 } catch (Exception e) {
151 throw new DatabaseException("Failed to create Simantics database.", e);
156 * Get a server that can be started and stopped.
158 * The result is actually a proxy server. Each successful start() increases
159 * reference count and stop() decreases. The actual server is closed
160 * once all proxies are closed.
162 * @param databaseDirectory
164 * @throws DatabaseException
166 private ServerEx getServer(File databaseDirectory) throws DatabaseException {
167 File file = databaseDirectory.getAbsoluteFile();
169 ServerHost host = null;
170 synchronized(servers) {
171 host = servers.get(file);
173 // Instantiate actual server. We are using ProCoreDriver and know it's address format and security model. Not good!
174 ServerI server = driver.getServer(file.getAbsolutePath(), null);
177 host = new ServerHost(server, databaseDirectory);
178 } catch (IOException e) {
179 throw new DatabaseException("Failed to load " + databaseDirectory, e);
182 servers.put(file, host);
186 ServerEx proxy = new ProxyServer(host);
191 * @param parseUnresolved
194 // public ServerEx getServer(ServerAddress endpoint) {
195 // return new ConnectedServer(endpoint);
199 * Close the server manager, close all servers.
200 * Deletes temporary files.
202 public void close() {
203 synchronized(servers) {
204 for (ServerHost host : servers.values()) {
205 ServerI server = host.actual;
207 if (server.isActive())
209 } catch (DatabaseException e) {
210 LOGGER.error("Failed to stop database server.", e);
217 public static int getFreeEphemeralPort() {
224 return s.getLocalPort();
229 } catch(BindException e) {
230 // Nothing to do, try next port
231 } catch (Throwable e) {
237 public static void createServerConfig(File file) throws IOException {
238 InputStream is = ServerManager.class.getResourceAsStream("server_template.cnfg");
239 byte[] data = StreamUtil.readFully(is);
242 FileOutputStream os = new FileOutputStream(file, false);
244 Properties properties = new Properties();
245 properties.store(os, "# automatically generated properties");
250 * ServerHost hosts a ServerI instance. For each successful start() a
251 * reference count is increased and each stop() & kill() it is decreased.
253 class ServerHost implements ServerEx {
258 Properties properties;
260 public ServerHost(ServerI actual, File database)
262 this.actual = actual;
263 this.database = database;
264 this.properties = new Properties();
267 public File getDatabase() {
275 public Properties getProperties() {
280 public String getAddress()
281 throws DatabaseException {
282 return actual.getAddress();
286 // public synchronized ServerAddress getServerAddress()
287 // throws DatabaseException {
288 // throw new DatabaseException("ServerHost.getServerAddress is not supported. Use getAddress instead.");
292 public boolean isActive() {
294 return actual.isActive();
295 } catch (DatabaseException e) {
301 * Start server if refCount = 0. If running or start was successful
302 * the refcount is increased.
304 * For each succesful start(), a stop() or kill() is expected.
307 public void start() throws DatabaseException {
308 boolean isRunning = actual.isActive();
318 public void stop() throws DatabaseException {
320 throw new DatabaseException("Trying to stop a standing process.");
328 public Session createSession(Properties properties) throws DatabaseException {
329 return driver.getSession(actual.getAddress(), properties);
333 public ServiceLocator getServiceLocator(Properties info) throws DatabaseException {
334 return createSession(info);
338 public String execute(String command) throws DatabaseException {
339 return actual.execute(command);
343 public String executeAndDisconnect(String command) throws DatabaseException {
344 return actual.executeAndDisconnect(command);
349 * Proxy Server starts actual server (ServerHost) when first start():ed,
350 * and closes the actual server once all proxy servers are closed.
352 * @author Toni Kalajainen <toni.kalajainen@vtt.fi>
354 public class ProxyServer implements ServerEx {
359 public ProxyServer(ServerHost actual) {
360 this.actual = actual;
363 public File getDatabase() {
364 return actual.getDatabase();
368 * Get server properties
371 * @throws IOException
373 public Properties getProperties() {
374 return actual.getProperties();
378 public String getAddress()
379 throws DatabaseException {
380 return actual.getAddress();
384 // public synchronized ServerAddress getServerAddress()
385 // throws DatabaseException {
386 // return actual.getServerAddress();
390 public boolean isActive() {
391 return running && actual.isActive();
395 public void start() throws DatabaseException {
402 public void stop() throws DatabaseException {
403 if (!running) return;
409 public Session createSession(Properties properties) throws DatabaseException {
410 return driver.getSession(actual.getAddress(), properties);
414 public ServiceLocator getServiceLocator(Properties info) throws DatabaseException {
415 return createSession(info);
419 public String execute(String command) throws DatabaseException {
420 return actual.execute(command);
424 public String executeAndDisconnect(String command) throws DatabaseException {
425 return actual.executeAndDisconnect(command);
429 // public class ConnectedServer implements ServerEx {
431 // ServerAddress endpoint;
433 // public ConnectedServer(ServerAddress endpoint) {
434 // this.endpoint = endpoint;
438 // public void start() throws DatabaseException {
439 // // Intentional NOP. Cannot control through socket.
443 // public void stop() throws DatabaseException {
444 // // Intentional NOP. Cannot control through socket.
448 // public boolean isActive() {
449 // // Without better knowledge
454 // public String getAddress()
455 // throws DatabaseException {
456 // return endpoint.getDbid();
460 // public synchronized ServerAddress getServerAddress()
461 // throws DatabaseException {
462 // return new ServerAddress(endpoint.getAddress());
466 // public Session createSession(Properties properties)
467 // throws DatabaseException {
468 // return driver.getSession(getServerAddress().toString(), properties);
472 // public ServiceLocator getServiceLocator(Properties info) throws DatabaseException {
473 // return createSession(info);
477 // public String execute(String command) throws DatabaseException {
478 // // Intentional NOP. Cannot control through socket.
483 // public String executeAndDisconnect(String command) throws DatabaseException {
484 // // Intentional NOP. Cannot control through socket.
490 DEFAULT = new Properties();
491 DEFAULT.setProperty("user", "Default User");
492 DEFAULT.setProperty("password", "");