]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.project/src/org/simantics/project/management/ServerManager.java
Restored old NoSingleResultException constructors without result count
[simantics/platform.git] / bundles / org.simantics.project / src / org / simantics / project / management / ServerManager.java
1 /*******************************************************************************
2  * Copyright (c) 2007, 2010 Association for Decentralized Information Management
3  * in Industry THTH ry.
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
8  *
9  * Contributors:
10  *     VTT Technical Research Centre of Finland - initial API and implementation
11  *******************************************************************************/
12 package org.simantics.project.management;
13
14 import java.io.File;
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;
22 import java.util.Map;
23 import java.util.Properties;
24
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;
44
45 /**
46  * Server Manager handles starting and pooling of ProCore server instances.
47  *
48  * @author Toni Kalajainen <toni.kalajainen@vtt.fi>
49  */
50 public class ServerManager {
51     private static final Logger LOGGER = LoggerFactory.getLogger(ServerManager.class);
52     
53         /** Default properties with default user and password */
54         public static final Properties DEFAULT;
55
56         /** Driver for database." */
57         final Driver driver;
58
59         /** Actual Server Instances. This object is synchronized by itself as lock. */
60         Map<File, ServerHost> servers = Collections.synchronizedMap( new HashMap<File, ServerHost>() );
61
62         /**
63          * Create a new server manager.
64          *
65          * @param applicationDirectory location of org.simantics.db.build
66          * @throws IOException
67          */
68     public ServerManager(Driver driver) throws IOException {
69         this.driver = driver;
70     }
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);
74     }
75         /**
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.
79          *
80          * @param databaseDirectory place where database is installed
81          * @param initialGraphs initialGraphs to install
82          * @throws DatabaseException
83          */
84         public Session createDatabase(File databaseDirectory) throws DatabaseException {
85                 try {
86                     LOGGER.debug("Creating database to "+ databaseDirectory);
87
88             Session session = null;
89             ServerEx server1 = getServer(databaseDirectory);
90             server1.start();
91                         try {
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() {
106                         @Override
107                         public void run(ReadGraph g) {
108                             // Registers Layer0 with the session ServiceLocator.
109                             Layer0.getInstance(g);
110                         }
111                     };
112                     session.syncRequest(req);
113                     return session;
114                 }
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);
123
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);
129                 l0ex.build();
130                                 long[] resourceArray = CoreInitialization.initializeGraph(session, l0ex.getGraph());
131                                 l0ex.setResourceArray(resourceArray);
132                                 session.getService(XSupport.class).setServiceMode(true, true);
133
134                                 DatabaseManagementResource.getInstance(session);
135                 Layer0.getInstance(session);
136                                 session.syncRequest(new WriteOnlyRequest() {
137                                         @Override
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();
144                                         }});
145                     return session;
146                         } finally {
147                             if (null == session)
148                                 server1.stop();
149                         }
150                 } catch (Exception e) {
151                         throw new DatabaseException("Failed to create Simantics database.", e);
152                 }
153         }
154
155         /**
156          * Get a server that can be started and stopped.
157          *
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.
161          *
162          * @param databaseDirectory
163          * @return server
164          * @throws DatabaseException
165          */
166         private ServerEx getServer(File databaseDirectory) throws DatabaseException {
167                 File file = databaseDirectory.getAbsoluteFile();
168
169                 ServerHost host = null;
170                 synchronized(servers) {
171                         host = servers.get(file);
172                         if (host==null) {
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);
175
176                                 try {
177                                         host = new ServerHost(server, databaseDirectory);
178                                 } catch (IOException e) {
179                                         throw new DatabaseException("Failed to load " + databaseDirectory, e);
180                                 }
181
182                                 servers.put(file, host);
183                         }
184                 }
185
186                 ServerEx proxy = new ProxyServer(host);
187                 return proxy;
188         }
189
190         /**
191          * @param parseUnresolved
192          * @return
193          */
194 //      public ServerEx getServer(ServerAddress endpoint) {
195 //              return new ConnectedServer(endpoint);
196 //      }
197
198         /**
199          * Close the server manager, close all servers.
200          * Deletes temporary files.
201          */
202         public void close() {
203                 synchronized(servers) {
204                         for (ServerHost host : servers.values()) {
205                                 ServerI server = host.actual;
206                 try {
207                     if (server.isActive())
208                         server.stop();
209                 } catch (DatabaseException e) {
210                     LOGGER.error("Failed to stop database server.", e);
211                 }
212                         }
213                         servers.clear();
214                 }
215         }
216
217     public static int getFreeEphemeralPort() {
218         while(true) {
219             try {
220                 Socket s = null;
221                 try {
222                     s = new Socket();
223                     s.bind(null);
224                     return s.getLocalPort();
225                 } finally {
226                     if (s != null)
227                         s.close();
228                 }
229             } catch(BindException e) {
230                 // Nothing to do, try next port
231             } catch (Throwable e) {
232                 throw new Error(e);
233             }
234         }
235     }
236
237     public static void createServerConfig(File file) throws IOException {
238                 InputStream is = ServerManager.class.getResourceAsStream("server_template.cnfg");
239                 byte[] data = StreamUtil.readFully(is);
240                 is.close();
241
242                 FileOutputStream os = new FileOutputStream(file, false);
243                 os.write(data);
244                 Properties properties = new Properties();
245                 properties.store(os, "# automatically generated properties");
246                 os.close();
247         }
248
249         /**
250          * ServerHost hosts a ServerI instance. For each successful start() a
251          * reference count is increased and each stop() & kill() it is decreased.
252          */
253         class ServerHost implements ServerEx {
254
255                 File database;
256                 ServerI actual;
257                 int refCount = 0;
258                 Properties properties;
259
260                 public ServerHost(ServerI actual, File database)
261                 throws IOException {
262                         this.actual = actual;
263                         this.database = database;
264                         this.properties = new Properties();
265                 }
266
267                 public File getDatabase() {
268                         return database;
269                 }
270
271                 /**
272                  * Get properties
273                  * @return properties
274                  */
275                 public Properties getProperties() {
276                         return properties;
277                 }
278
279         @Override
280         public String getAddress()
281                 throws DatabaseException {
282             return actual.getAddress();
283         }
284
285 //        @Override
286 //        public synchronized ServerAddress getServerAddress()
287 //        throws DatabaseException {
288 //            throw new DatabaseException("ServerHost.getServerAddress is not supported. Use getAddress instead.");
289 //        }
290
291         @Override
292         public boolean isActive() {
293             try {
294                 return actual.isActive();
295             } catch (DatabaseException e) {
296                 return false;
297             }
298                 }
299
300                 /**
301                  * Start server if refCount = 0. If running or start was successful
302                  * the refcount is increased.
303                  *
304                  * For each succesful start(), a stop() or kill() is expected.
305                  */
306                 @Override
307                 public void start() throws DatabaseException {
308                         boolean isRunning = actual.isActive();
309
310                         if (!isRunning) {
311                                 actual.start();
312                         }
313
314                         refCount++;
315                 }
316
317         @Override
318         public void stop() throws DatabaseException {
319             if (refCount <= 0)
320                 throw new DatabaseException("Trying to stop a standing process.");
321             refCount--;
322             if (refCount > 1)
323                 return;
324             actual.stop();
325         }
326
327                 @Override
328                 public Session createSession(Properties properties) throws DatabaseException {
329                         return driver.getSession(actual.getAddress(), properties);
330                 }
331
332         @Override
333         public ServiceLocator getServiceLocator(Properties info) throws DatabaseException {
334             return createSession(info);
335         }
336
337         @Override
338         public String execute(String command) throws DatabaseException {
339             return actual.execute(command);
340         }
341
342         @Override
343         public String executeAndDisconnect(String command) throws DatabaseException {
344             return actual.executeAndDisconnect(command);
345         }
346         }
347
348         /**
349          * Proxy Server starts actual server (ServerHost) when first start():ed,
350          * and closes the actual server once all proxy servers are closed.
351          *
352          * @author Toni Kalajainen <toni.kalajainen@vtt.fi>
353          */
354         public class ProxyServer implements ServerEx {
355
356                 boolean running;
357                 ServerHost actual;
358
359                 public ProxyServer(ServerHost actual) {
360                         this.actual = actual;
361                 }
362
363                 public File getDatabase() {
364                         return actual.getDatabase();
365                 }
366
367                 /**
368                  * Get server properties
369                  *
370                  * @return properties
371                  * @throws IOException
372                  */
373                 public Properties getProperties() {
374                         return actual.getProperties();
375                 }
376
377         @Override
378         public String getAddress()
379         throws DatabaseException {
380             return actual.getAddress();
381         }
382
383 //        @Override
384 //        public synchronized ServerAddress getServerAddress()
385 //        throws DatabaseException {
386 //            return actual.getServerAddress();
387 //        }
388
389                 @Override
390                 public boolean isActive() {
391                         return running && actual.isActive();
392                 }
393
394                 @Override
395                 public void start() throws DatabaseException {
396                         if (running) return;
397                         actual.start();
398                         running = true;
399                 }
400
401                 @Override
402                 public void stop() throws DatabaseException {
403                         if (!running) return;
404                         actual.stop();
405                         running = false;
406                 }
407
408                 @Override
409                 public Session createSession(Properties properties) throws DatabaseException {
410                         return driver.getSession(actual.getAddress(), properties);
411                 }
412
413                 @Override
414                 public ServiceLocator getServiceLocator(Properties info) throws DatabaseException {
415                         return createSession(info);
416                 }
417
418         @Override
419         public String execute(String command) throws DatabaseException {
420             return actual.execute(command);
421         }
422
423         @Override
424         public String executeAndDisconnect(String command) throws DatabaseException {
425             return actual.executeAndDisconnect(command);
426         }
427         }
428
429 //      public class ConnectedServer implements ServerEx {
430 //
431 //              ServerAddress endpoint;
432 //
433 //              public ConnectedServer(ServerAddress endpoint) {
434 //                      this.endpoint = endpoint;
435 //              }
436 //
437 //              @Override
438 //              public void start() throws DatabaseException {
439 //                      // Intentional NOP. Cannot control through socket.
440 //              }
441 //
442 //              @Override
443 //              public void stop() throws DatabaseException {
444 //                      // Intentional NOP. Cannot control through socket.
445 //              }
446 //
447 //              @Override
448 //              public boolean isActive() {
449 //                      // Without better knowledge
450 //                      return true;
451 //              }
452 //
453 //        @Override
454 //        public String getAddress()
455 //        throws DatabaseException {
456 //            return endpoint.getDbid();
457 //        }
458 //
459 //        @Override
460 //        public synchronized ServerAddress getServerAddress()
461 //                throws DatabaseException {
462 //            return new ServerAddress(endpoint.getAddress());
463 //        }
464 //
465 //        @Override
466 //        public Session createSession(Properties properties)
467 //        throws DatabaseException {
468 //            return driver.getSession(getServerAddress().toString(), properties);
469 //              }
470 //
471 //        @Override
472 //        public ServiceLocator getServiceLocator(Properties info) throws DatabaseException {
473 //            return createSession(info);
474 //        }
475 //
476 //        @Override
477 //        public String execute(String command) throws DatabaseException {
478 //            // Intentional NOP. Cannot control through socket.
479 //            return null;
480 //        }
481 //
482 //        @Override
483 //        public String executeAndDisconnect(String command) throws DatabaseException {
484 //            // Intentional NOP. Cannot control through socket.
485 //            return null;
486 //        }
487 //      }
488
489         static {
490                 DEFAULT = new Properties();
491                 DEFAULT.setProperty("user", "Default User");
492                 DEFAULT.setProperty("password", "");
493         }
494
495 }
496