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.simantics.project.SessionDescriptor;
43 import org.slf4j.Logger;
44 import org.slf4j.LoggerFactory;
47 * Server Manager handles starting and pooling of ProCore server instances.
49 * @author Toni Kalajainen <toni.kalajainen@vtt.fi>
51 public class ServerManager {
52 private static final Logger LOGGER = LoggerFactory.getLogger(ServerManager.class);
54 /** Default properties with default user and password */
55 public static final Properties DEFAULT;
57 /** Driver for database." */
60 /** Actual Server Instances. This object is synchronized by itself as lock. */
61 Map<File, ServerHost> servers = Collections.synchronizedMap( new HashMap<File, ServerHost>() );
64 * Create a new server manager.
66 * @param applicationDirectory location of org.simantics.db.build
69 public ServerManager(Driver driver) throws IOException {
72 public Management getManagement(File dbFolder) throws DatabaseException {
73 // We are using ProCoreDriver and know it's address format and security model. Not good!
74 return driver.getManagement(dbFolder.getAbsolutePath(), null);
77 * Create a new database that is initialized with given graphs.
78 * One of them must be layer0.
79 * Database directory is created if it did not exist.
81 * @param databaseDirectory place where database is installed
82 * @param initialGraphs initialGraphs to install
83 * @throws DatabaseException
85 public SessionDescriptor createDatabase(File databaseDirectory) throws DatabaseException {
87 LOGGER.debug("Creating database to "+ databaseDirectory);
89 Session session = null;
90 ServerEx server1 = getServer(databaseDirectory);
93 // This will initialize the fixed URIs and corresponding resources.
94 // These are needed by the query system to parse URIs.
95 // The server will generate the clusters for the generated resources.
96 // The layer0 statements will be generated in phase two.
97 // This will close the connection to server because the only thing
98 // you can do with this connection is to initialize the fixed URIs.
99 Properties info = new Properties();
100 info.setProperty("user", "Default User");
101 info.setProperty("password", "");
102 session = server1.createSession(info);
103 XSupport xs = session.getService(XSupport.class);
104 ClusterUID[] clusters = xs.listClusters();
105 if (clusters.length > 1) {// Database contain clusters, assuming initialization is done.");
106 ReadRequest req = new ReadRequest() {
108 public void run(ReadGraph g) {
109 // Registers Layer0 with the session ServiceLocator.
110 Layer0.getInstance(g);
113 session.syncRequest(req);
114 return new SessionDescriptor(session, false);
116 CoreInitialization.initializeBuiltins(session);
117 // This will try to initialize Builtins.class but because there is no statements
118 // in the server only the previously added fixed URIs are found.
119 // If we'd want to get rid of the missing layer0 URI warnings then
120 // a non initialized session should be used to add graph statements
121 // without using Builtins.class at all or by initializing Builtins.class
122 // only with the fixed URIs.
123 session.getService(XSupport.class).setServiceMode(true, true);
125 // This will add layer0 statements. The query mechanism is not
126 // yet totally functional because there is no statements in the
127 // server. Mainly WriteOnly request is available here.
128 GraphBundle l0 = PlatformUtil.getGraph("org.simantics.layer0");
129 final GraphBundleEx l0ex = GraphBundleEx.extend(l0);
131 long[] resourceArray = CoreInitialization.initializeGraph(session, l0ex.getGraph());
132 l0ex.setResourceArray(resourceArray);
133 session.getService(XSupport.class).setServiceMode(true, true);
135 DatabaseManagementResource.getInstance(session);
136 Layer0.getInstance(session);
137 session.syncRequest(new WriteOnlyRequest() {
139 public void perform(WriteOnlyGraph graph) throws DatabaseException {
140 // Root Library is a cluster set
141 graph.newClusterSet(graph.getRootLibrary());
142 DatabaseManagement mgt = new DatabaseManagement();
143 mgt.createGraphBundle(graph, l0ex);
144 graph.flushCluster();
146 return new SessionDescriptor(session, true);
151 } catch (Exception e) {
152 throw new DatabaseException("Failed to create Simantics database.", e);
157 * Get a server that can be started and stopped.
159 * The result is actually a proxy server. Each successful start() increases
160 * reference count and stop() decreases. The actual server is closed
161 * once all proxies are closed.
163 * @param databaseDirectory
165 * @throws DatabaseException
167 private ServerEx getServer(File databaseDirectory) throws DatabaseException {
168 File file = databaseDirectory.getAbsoluteFile();
170 ServerHost host = null;
171 synchronized(servers) {
172 host = servers.get(file);
174 // Instantiate actual server. We are using ProCoreDriver and know it's address format and security model. Not good!
175 ServerI server = driver.getServer(file.getAbsolutePath(), null);
178 host = new ServerHost(server, databaseDirectory);
179 } catch (IOException e) {
180 throw new DatabaseException("Failed to load " + databaseDirectory, e);
183 servers.put(file, host);
187 ServerEx proxy = new ProxyServer(host);
192 * @param parseUnresolved
195 // public ServerEx getServer(ServerAddress endpoint) {
196 // return new ConnectedServer(endpoint);
200 * Close the server manager, close all servers.
201 * Deletes temporary files.
203 public void close() {
204 synchronized(servers) {
205 for (ServerHost host : servers.values()) {
206 ServerI server = host.actual;
208 if (server.isActive())
210 } catch (DatabaseException e) {
211 LOGGER.error("Failed to stop database server.", e);
218 public static int getFreeEphemeralPort() {
225 return s.getLocalPort();
230 } catch(BindException e) {
231 // Nothing to do, try next port
232 } catch (Throwable e) {
238 public static void createServerConfig(File file) throws IOException {
239 InputStream is = ServerManager.class.getResourceAsStream("server_template.cnfg");
240 byte[] data = StreamUtil.readFully(is);
243 FileOutputStream os = new FileOutputStream(file, false);
245 Properties properties = new Properties();
246 properties.store(os, "# automatically generated properties");
251 * ServerHost hosts a ServerI instance. For each successful start() a
252 * reference count is increased and each stop() & kill() it is decreased.
254 class ServerHost implements ServerEx {
259 Properties properties;
261 public ServerHost(ServerI actual, File database)
263 this.actual = actual;
264 this.database = database;
265 this.properties = new Properties();
268 public File getDatabase() {
276 public Properties getProperties() {
281 public String getAddress()
282 throws DatabaseException {
283 return actual.getAddress();
287 // public synchronized ServerAddress getServerAddress()
288 // throws DatabaseException {
289 // throw new DatabaseException("ServerHost.getServerAddress is not supported. Use getAddress instead.");
293 public boolean isActive() {
295 return actual.isActive();
296 } catch (DatabaseException e) {
302 * Start server if refCount = 0. If running or start was successful
303 * the refcount is increased.
305 * For each succesful start(), a stop() or kill() is expected.
308 public void start() throws DatabaseException {
309 boolean isRunning = actual.isActive();
319 public void stop() throws DatabaseException {
321 throw new DatabaseException("Trying to stop a standing process.");
329 public Session createSession(Properties properties) throws DatabaseException {
330 return driver.getSession(actual.getAddress(), properties);
334 public ServiceLocator getServiceLocator(Properties info) throws DatabaseException {
335 return createSession(info);
339 public String execute(String command) throws DatabaseException {
340 return actual.execute(command);
344 public String executeAndDisconnect(String command) throws DatabaseException {
345 return actual.executeAndDisconnect(command);
350 * Proxy Server starts actual server (ServerHost) when first start():ed,
351 * and closes the actual server once all proxy servers are closed.
353 * @author Toni Kalajainen <toni.kalajainen@vtt.fi>
355 public class ProxyServer implements ServerEx {
360 public ProxyServer(ServerHost actual) {
361 this.actual = actual;
364 public File getDatabase() {
365 return actual.getDatabase();
369 * Get server properties
372 * @throws IOException
374 public Properties getProperties() {
375 return actual.getProperties();
379 public String getAddress()
380 throws DatabaseException {
381 return actual.getAddress();
385 // public synchronized ServerAddress getServerAddress()
386 // throws DatabaseException {
387 // return actual.getServerAddress();
391 public boolean isActive() {
392 return running && actual.isActive();
396 public void start() throws DatabaseException {
403 public void stop() throws DatabaseException {
404 if (!running) return;
410 public Session createSession(Properties properties) throws DatabaseException {
411 return driver.getSession(actual.getAddress(), properties);
415 public ServiceLocator getServiceLocator(Properties info) throws DatabaseException {
416 return createSession(info);
420 public String execute(String command) throws DatabaseException {
421 return actual.execute(command);
425 public String executeAndDisconnect(String command) throws DatabaseException {
426 return actual.executeAndDisconnect(command);
430 // public class ConnectedServer implements ServerEx {
432 // ServerAddress endpoint;
434 // public ConnectedServer(ServerAddress endpoint) {
435 // this.endpoint = endpoint;
439 // public void start() throws DatabaseException {
440 // // Intentional NOP. Cannot control through socket.
444 // public void stop() throws DatabaseException {
445 // // Intentional NOP. Cannot control through socket.
449 // public boolean isActive() {
450 // // Without better knowledge
455 // public String getAddress()
456 // throws DatabaseException {
457 // return endpoint.getDbid();
461 // public synchronized ServerAddress getServerAddress()
462 // throws DatabaseException {
463 // return new ServerAddress(endpoint.getAddress());
467 // public Session createSession(Properties properties)
468 // throws DatabaseException {
469 // return driver.getSession(getServerAddress().toString(), properties);
473 // public ServiceLocator getServiceLocator(Properties info) throws DatabaseException {
474 // return createSession(info);
478 // public String execute(String command) throws DatabaseException {
479 // // Intentional NOP. Cannot control through socket.
484 // public String executeAndDisconnect(String command) throws DatabaseException {
485 // // Intentional NOP. Cannot control through socket.
491 DEFAULT = new Properties();
492 DEFAULT.setProperty("user", "Default User");
493 DEFAULT.setProperty("password", "");