View Javadoc

1   /**
2    * Copyright (c) 2002-2011 "Neo Technology,"
3    * Network Engine for Objects in Lund AB [http://neotechnology.com]
4    *
5    * This file is part of Neo4j.
6    *
7    * Neo4j is free software: you can redistribute it and/or modify
8    * it under the terms of the GNU General Public License as published by
9    * the Free Software Foundation, either version 3 of the License, or
10   * (at your option) any later version.
11   *
12   * This program is distributed in the hope that it will be useful,
13   * but WITHOUT ANY WARRANTY; without even the implied warranty of
14   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15   * GNU General Public License for more details.
16   *
17   * You should have received a copy of the GNU General Public License
18   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
19   */
20  package org.neo4j.server;
21  
22  import java.io.File;
23  import java.net.URI;
24  import java.net.URISyntaxException;
25  import java.util.ArrayList;
26  import java.util.Collection;
27  import java.util.List;
28  import java.util.Map;
29  
30  import org.apache.commons.configuration.Configuration;
31  import org.neo4j.server.configuration.Configurator;
32  import org.neo4j.server.configuration.PropertyFileConfigurator;
33  import org.neo4j.server.configuration.validation.DatabaseLocationMustBeSpecifiedRule;
34  import org.neo4j.server.configuration.validation.Validator;
35  import org.neo4j.server.database.Database;
36  import org.neo4j.server.database.GraphDatabaseFactory;
37  import org.neo4j.server.logging.Logger;
38  import org.neo4j.server.modules.PluginInitializer;
39  import org.neo4j.server.modules.RESTApiModule;
40  import org.neo4j.server.modules.ServerModule;
41  import org.neo4j.server.plugins.Injectable;
42  import org.neo4j.server.plugins.PluginManager;
43  import org.neo4j.server.startup.healthcheck.StartupHealthCheck;
44  import org.neo4j.server.startup.healthcheck.StartupHealthCheckFailedException;
45  import org.neo4j.server.web.WebServer;
46  
47  public class NeoServerWithEmbeddedWebServer implements NeoServer {
48  
49      public static final Logger log = Logger.getLogger(NeoServerWithEmbeddedWebServer.class);
50  
51      private final File configFile;
52      private Configurator configurator;
53      private Database database;
54      private final WebServer webServer;
55      private final StartupHealthCheck startupHealthCheck;
56  
57      private final AddressResolver addressResolver;
58  
59      private final List<ServerModule> serverModules = new ArrayList<ServerModule>();
60      private PluginInitializer pluginInitializer;
61      // Perhaps this shouldn't reference (an instance of) the Bootstrapper class,
62      // but we need to provide configuration parameters to load the appropriate
63      // GraphDatabaseFactory implementation
64      private final Bootstrapper bootstrapper;
65  
66      public NeoServerWithEmbeddedWebServer( Bootstrapper bootstrapper, AddressResolver addressResolver,
67              StartupHealthCheck startupHealthCheck, File configFile, WebServer webServer,
68              Iterable<Class<? extends ServerModule>> moduleClasses )
69      {
70          this.bootstrapper = bootstrapper;
71          this.addressResolver = addressResolver;
72          this.startupHealthCheck = startupHealthCheck;
73          this.configFile = configFile;
74          this.webServer = webServer;
75          webServer.setNeoServer( this );
76          for ( Class<? extends ServerModule> moduleClass : moduleClasses )
77          {
78              registerModule( moduleClass );
79          }
80      }
81  
82      public NeoServerWithEmbeddedWebServer( Bootstrapper bootstrapper, StartupHealthCheck startupHealthCheck,
83              File configFile, WebServer ws, Iterable<Class<? extends ServerModule>> mc )
84      {
85          this( bootstrapper, new AddressResolver(), startupHealthCheck, configFile, ws, mc );
86      }
87  
88      @Override
89      public void start() {
90          // Start at the bottom of the stack and work upwards to the Web container
91          startupHealthCheck();
92          validateConfiguration();
93  
94          startDatabase();
95  
96          startExtensionInitialization();
97  
98          startModules();
99  
100         startWebServer();
101     }
102 
103     /**
104      *  Initializes individual plugins using the mechanism provided via @{see PluginInitializer} and the java service locator
105      */
106     protected void startExtensionInitialization()
107     {
108         pluginInitializer = new PluginInitializer( this );
109     }
110 
111     /**
112      * Use this method to register server modules from subclasses
113      * @param clazz
114      */
115     protected final void registerModule(Class<? extends ServerModule> clazz) {
116         try {
117             serverModules.add(clazz.newInstance());
118         } catch (Exception e) {
119             log.warn( "Failed to instantiate server module [%s], reason: %s", clazz.getName(), e.getMessage() );
120         }
121     }
122 
123     private void startModules() {
124         for(ServerModule module : serverModules) {
125             module.start(this);
126         }
127     }
128 
129     private void stopModules() {
130         for(ServerModule module : serverModules) {
131 
132             try{
133                 module.stop();
134             }
135             catch(Exception e)
136             {
137                log.error( e );
138             }
139         }
140     }
141 
142     private void startupHealthCheck() {
143         if (!startupHealthCheck.run()) {
144             throw new StartupHealthCheckFailedException(startupHealthCheck.failedRule());
145         }
146     }
147 
148     private void validateConfiguration() {
149         this.configurator = new PropertyFileConfigurator(new Validator(new DatabaseLocationMustBeSpecifiedRule()), configFile);
150     }
151 
152     private void startDatabase() {
153         String dbLocation = new File(configurator.configuration().getString(Configurator.DATABASE_LOCATION_PROPERTY_KEY)).getAbsolutePath();
154         GraphDatabaseFactory dbFactory = bootstrapper.getGraphDatabaseFactory( configurator.configuration() );
155         Map<String, String> databaseTuningProperties = configurator.getDatabaseTuningProperties();
156         if (databaseTuningProperties != null) {
157             this.database = new Database( dbFactory, dbLocation, databaseTuningProperties );
158         } else {
159             this.database = new Database( dbFactory, dbLocation );
160         }
161     }
162 
163     @Override
164     public Configuration getConfiguration() {
165         return configurator.configuration();
166     }
167 
168     private void startWebServer() {
169 
170         int webServerPort = getWebServerPort();
171 
172         log.info("Starting Neo Server on port [%s]", webServerPort);
173         webServer.setPort(webServerPort);
174 
175 
176         try {
177             webServer.start();
178         } catch (Exception e) {
179             e.printStackTrace();
180             log.error("Failed to start Neo Server on port [%d], reason [%s]", getWebServerPort(), e.getMessage());
181         }
182     }
183 
184     protected int getWebServerPort() {
185         return configurator.configuration().getInt(Configurator.WEBSERVER_PORT_PROPERTY_KEY, Configurator.DEFAULT_WEBSERVER_PORT);
186     }
187 
188     @Override
189     public void stop() {
190         try {
191             stopModules();
192             stopExtensionInitializers();
193             stopDatabase();
194             stopWebServer();
195             log.info("Successfully shutdown Neo Server on port [%d], database [%s]", getWebServerPort(), getDatabase().getLocation());
196         } catch (Exception e) {
197             log.warn("Failed to cleanly shutdown Neo Server on port [%d], database [%s]. Reason: %s", getWebServerPort(), getDatabase().getLocation(),
198                     e.getMessage());
199         }
200     }
201 
202     /**
203      * shuts down initializers of individual plugins
204      */
205     private void stopExtensionInitializers()
206     {
207         pluginInitializer.stop(  );
208     }
209 
210     private void stopWebServer() {
211         if (webServer != null) {
212             webServer.stop();
213         }
214     }
215 
216     private void stopDatabase() {
217         if (database != null) {
218             database.shutdown();
219         }
220     }
221 
222     @Override
223     public Database getDatabase() {
224         return database;
225     }
226 
227 
228     public URI baseUri() {
229         StringBuilder sb = new StringBuilder();
230         sb.append("http");
231         int webServerPort = getWebServerPort();
232         if (webServerPort == 443) {
233             sb.append("s");
234 
235         }
236         sb.append("://");
237         sb.append(addressResolver.getHostname());
238 
239         if (webServerPort != 80) {
240             sb.append(":");
241             sb.append(webServerPort);
242         }
243         sb.append("/");
244 
245         try {
246             return new URI(sb.toString());
247         } catch (URISyntaxException e) {
248             throw new RuntimeException(e);
249         }
250     }
251 
252     public WebServer getWebServer() {
253         return webServer;
254     }
255 
256     @Override
257     public Configurator getConfigurator() {
258         return configurator;
259     }
260 
261     @Override
262     public PluginManager getExtensionManager() {
263         if(hasModule(RESTApiModule.class)) {
264             return getModule(RESTApiModule.class).getPlugins();
265         } else {
266             return null;
267         }
268     }
269 
270     @Override
271     public Collection<Injectable<?>> getInjectables( List<String> packageNames )
272     {
273         return pluginInitializer.intitializePackages( packageNames );
274     }
275 
276     private boolean hasModule(Class<? extends ServerModule> clazz) {
277         for(ServerModule sm : serverModules) {
278             if(sm.getClass() == clazz) {
279                 return true;
280             }
281         }
282         return false;
283     }
284 
285     @SuppressWarnings("unchecked")
286     private <T extends ServerModule> T getModule(Class<T> clazz) {
287         for(ServerModule sm : serverModules) {
288             if(sm.getClass() == clazz) {
289                 return (T) sm;
290             }
291         }
292 
293         return null;
294     }
295 
296 }