2 * Copyright (C) 2014 EDIT
3 * European Distributed Institute of Taxonomy
4 * http://www.e-taxonomy.eu
6 * The contents of this file are subject to the Mozilla Public License Version 1.1
7 * See LICENSE.TXT at the top of this package for the full license terms.
9 package eu
.etaxonomy
.taxeditor
.local
;
12 import java
.io
.IOException
;
13 import java
.net
.ServerSocket
;
15 import java
.net
.URISyntaxException
;
18 import javax
.management
.InstanceNotFoundException
;
19 import javax
.management
.MBeanException
;
20 import javax
.management
.MBeanServerConnection
;
21 import javax
.management
.MalformedObjectNameException
;
22 import javax
.management
.ObjectName
;
23 import javax
.management
.ReflectionException
;
24 import javax
.management
.remote
.JMXConnector
;
25 import javax
.management
.remote
.JMXConnectorFactory
;
26 import javax
.management
.remote
.JMXServiceURL
;
28 import org
.apache
.commons
.lang3
.StringUtils
;
29 import org
.apache
.log4j
.Logger
;
30 import org
.eclipse
.core
.runtime
.FileLocator
;
31 import org
.eclipse
.core
.runtime
.Platform
;
32 import org
.eclipse
.jetty
.server
.Server
;
33 import org
.eclipse
.jetty
.util
.preventers
.AppContextLeakPreventer
;
34 import org
.eclipse
.jetty
.webapp
.WebAppContext
;
35 import org
.osgi
.framework
.Bundle
;
37 import eu
.etaxonomy
.taxeditor
.workbench
.datasource
.ICdmServerError
;
40 * (Singleton) Server instance which manages a compatible cdmlib-webapp-war.
41 * This is done by launching a jetty instance (using jetty-runner) as an
42 * executed process. The choice of the external process over a more
43 * preferable 'embedded jetty' instance is due to problems arising from the
44 * class loading of classes (e.g. from hibernate core) which are needed
45 * for both the webapp as well as the remoting client.
50 public class CdmServer
{
52 public static final Logger logger
= Logger
.getLogger(CdmServer
.class);
54 //TODO use the constants defined in eu.etaxonomy.cdm.opt.config.DataSourceConfigurer
55 private static final String ATTRIBUTE_FORCE_SCHEMA_CREATE
= "cdm.forceSchemaCreate";
56 private static final String ATTRIBUTE_FORCE_SCHEMA_UPDATE
= "cdm.forceSchemaUpdate";
57 private static final String ATTRIBUTE_DATASOURCE_NAME
= "cdm.datasource";
58 private static final String CDM_BEAN_DEFINITION_FILE
= "cdm.beanDefinitionFile";
60 //see eu.etaxonomy.cdm.server.Bootloader.SPRING_PROFILES_ACTIVE
61 private static final String SPRING_PROFILES_ACTIVE
= "spring.profiles.active";
64 private final String dataSourceName
;
65 private final String host
= "127.0.0.1";
66 private int httpPort
= 9090;
67 private final String contextPath
= "/";
70 private Server server
;
72 private boolean forceSchemaCreate
= false;
73 private boolean forceSchemaUpdate
= false;
75 public CdmServer(String dataSourceName
, File dataSourcesFile
) throws CdmEmbeddedServerException
{
76 if(StringUtils
.isBlank(dataSourceName
)) {
77 throw new CdmEmbeddedServerException("DataSource name is not valid");
80 if(dataSourcesFile
== null || !dataSourcesFile
.exists()) {
81 throw new CdmEmbeddedServerException("DataSource config file does not exist");
83 this.dataSourceName
= dataSourceName
;
84 Bundle bundle
= Platform
.getBundle("eu.etaxonomy.taxeditor.local");
85 URL warURL
= bundle
.getEntry("lib/cdmlib-remote-webapp.war");
88 URL resolvedWarURL
= FileLocator
.toFileURL(warURL
);
89 // We need to use the 3-arg constructor of URI in order to properly escape file system chars
90 URI resolvedURI
= new URI(resolvedWarURL
.getProtocol(), resolvedWarURL
.getPath(), null);
91 warFile
= new File(resolvedURI
);
92 // System.out.println("war url : " + warFile.getAbsolutePath());
94 if(warFile
== null || !warFile
.exists()) {
95 throw new CdmEmbeddedServerException("Cdmlib War file does not exist");
97 } catch (URISyntaxException use
) {
98 throw new CdmEmbeddedServerException(use
);
99 } catch (IOException ioe
) {
100 throw new CdmEmbeddedServerException(ioe
);
103 System
.setProperty(SPRING_PROFILES_ACTIVE
, "remoting");
104 System
.setProperty(CDM_BEAN_DEFINITION_FILE
, dataSourcesFile
.getAbsolutePath());
105 System
.setProperty(ATTRIBUTE_DATASOURCE_NAME
, dataSourceName
);
107 httpPort
= findFreePort();
109 logger
.warn("Starting server on port : " + httpPort
);
111 server
= new Server(httpPort
);
113 server
.addBean(new AppContextLeakPreventer());
115 WebAppContext webapp
= new WebAppContext();
117 webapp
.setContextPath(contextPath
);
118 webapp
.setWar(warFile
.getAbsolutePath());
119 webapp
.setThrowUnavailableOnStartupException(true);
121 server
.setHandler(webapp
);
124 public String
getDataSourceName() {
125 return dataSourceName
;
127 public String
getHost() {
131 public int getPort() {
135 public String
getContextPath() {
139 private static int findFreePort() throws CdmEmbeddedServerException
{
140 ServerSocket socket
= null;
142 socket
= new ServerSocket(0);
143 socket
.setReuseAddress(true);
144 int port
= socket
.getLocalPort();
147 } catch (IOException e
) {
151 } catch (IOException e
) {
153 if (socket
!= null) {
156 } catch (IOException e
) {
160 throw new CdmEmbeddedServerException("Could not find a free TCP/IP port to start embedded Jetty HTTP Server on");
163 public void start(ICdmServerError cdmServerError
) throws CdmEmbeddedServerException
{
164 start(true, cdmServerError
);
167 public void start(boolean wait
, final ICdmServerError remotingLoginDialogLocal
) throws CdmEmbeddedServerException
{
170 throw new CdmEmbeddedServerException("Server is already disposed");
173 if(server
.isStarting()) {
174 throw new CdmEmbeddedServerException("Server is starting");
177 if(server
.isStarted()) {
178 throw new CdmEmbeddedServerException("Server has started");
181 if(server
.isRunning()) {
182 throw new CdmEmbeddedServerException("Server is running");
185 if(server
.isStopping()) {
186 throw new CdmEmbeddedServerException("Server is currently stopping. Please re-try in about 10 seconds");
189 Thread serverThread
= new Thread() {
194 if(isForceSchemaCreate()) {
195 System
.setProperty(ATTRIBUTE_FORCE_SCHEMA_CREATE
, "true");
196 }else if (isForceSchemaUpdate()){
197 System
.setProperty(ATTRIBUTE_FORCE_SCHEMA_UPDATE
, "true");
201 } catch (Throwable t
) {
202 //wait for 1sec to get the right order of login dialog and error
203 //message when connection fails
206 } catch (InterruptedException e
) {
209 while(cause
!= null) {
211 // pure string comparison to avoid dependencies to hibernate or cdmlib-persistance in the taxeditor
212 if(cause
.getClass().getSimpleName().equals("SchemaExtractionException") || cause
.getClass().getSimpleName().equals("CdmDatabaseException") ) {
213 logger
.debug(cause
.getClass().getName() + " detected which indicates missing or corrupt schema");
216 cause
= cause
.getCause();
219 remotingLoginDialogLocal
.handleError(new RuntimeException("Error during CDM server startup", t
));
224 serverThread
.start();
227 while(!server
.isStarted()) {}
231 public boolean isAlive() {
232 return server
.isRunning() || server
.isStarting() || server
.isStarted();
235 public boolean isStarted() {
236 return server
.isStarted();
239 public boolean isFailed() {
240 return server
.isFailed();
243 public void stop() throws Exception
{
249 public static void stopServerViaJMX(int jmxPort
) throws CdmEmbeddedServerException
{
250 String JMX_URL
= "service:jmx:rmi:///jndi/rmi://localhost:" + jmxPort
+ "/jmxrmi";
251 logger
.warn("Shutting down Jetty instance ... ");
254 JMXConnector connector
= JMXConnectorFactory
.connect(new JMXServiceURL(JMX_URL
), null);
255 connector
.connect(null);
256 MBeanServerConnection connection
= connector
.getMBeanServerConnection();
257 ObjectName objectName
= new ObjectName("org.eclipse.jetty.server:type=server,id=0");
258 connection
.invoke(objectName
, "stop", null, null);
259 logger
.warn("Shutdown command sent");
260 } catch (InstanceNotFoundException e
) {
261 throw new CdmEmbeddedServerException(e
);
262 } catch (MBeanException e
) {
263 throw new CdmEmbeddedServerException(e
);
264 } catch (ReflectionException e
) {
265 throw new CdmEmbeddedServerException(e
);
266 } catch (IOException e
) {
267 throw new CdmEmbeddedServerException(e
);
268 } catch (MalformedObjectNameException e
) {
269 throw new CdmEmbeddedServerException(e
);
273 public boolean isForceSchemaCreate() {
274 return forceSchemaCreate
;
277 public void setForceSchemaCreate(boolean forceSchemaCreate
) {
278 this.forceSchemaCreate
= forceSchemaCreate
;
281 public boolean isForceSchemaUpdate() {
282 return forceSchemaUpdate
;
284 public void setForceSchemaUpdate(boolean forceSchemaUpdate
) {
285 this.forceSchemaUpdate
= forceSchemaUpdate
;