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
.webapp
;
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
.log4j
.Logger
;
29 import org
.eclipse
.core
.runtime
.FileLocator
;
30 import org
.eclipse
.core
.runtime
.Platform
;
31 import org
.eclipse
.jetty
.server
.Server
;
32 import org
.eclipse
.jetty
.util
.StringUtil
;
33 import org
.eclipse
.jetty
.util
.preventers
.AppContextLeakPreventer
;
34 import org
.eclipse
.jetty
.webapp
.WebAppContext
;
35 import org
.osgi
.framework
.Bundle
;
38 * (Singleton) Server instance which manages a compatible cdmlib-webapp-war.
39 * This is done by launching a jetty instance (using jetty-runner) as an
40 * executed process. The choice of the external process over a more
41 * preferable 'embedded jetty' instance is due to problems arising from the
42 * class loading of classes (e.g. from hibernate core) which are needed
43 * for both the webapp as well as the remoting client.
48 public class CdmServer
{
50 public static final Logger logger
= Logger
.getLogger(CdmServer
.class);
52 //TODO use the constants defined in eu.etaxonomy.cdm.opt.config.DataSourceConfigurer
53 private static final String ATTRIBUTE_FORCE_SCHEMA_CREATE
= "cdm.forceSchemaCreate";
54 private static final String ATTRIBUTE_FORCE_SCHEMA_UPDATE
= "cdm.forceSchemaUpdate";
55 private static final String ATTRIBUTE_DATASOURCE_NAME
= "cdm.datasource";
56 private static final String CDM_BEAN_DEFINITION_FILE
= "cdm.beanDefinitionFile";
58 //see eu.etaxonomy.cdm.server.Bootloader.SPRING_PROFILES_ACTIVE
59 private static final String SPRING_PROFILES_ACTIVE
= "spring.profiles.active";
62 private final String dataSourceName
;
63 private final String host
= "127.0.0.1";
64 private int httpPort
= 9090;
65 private final String contextPath
= "/";
68 private Server server
;
70 private boolean forceSchemaCreate
= false;
71 private boolean forceSchemaUpdate
= false;
73 public CdmServer(String dataSourceName
, File dataSourcesFile
) throws CdmEmbeddedServerException
{
74 if(StringUtil
.isBlank(dataSourceName
)) {
75 throw new CdmEmbeddedServerException("DataSource name is not valid");
78 if(dataSourcesFile
== null || !dataSourcesFile
.exists()) {
79 throw new CdmEmbeddedServerException("DataSource config file does not exist");
81 this.dataSourceName
= dataSourceName
;
82 Bundle bundle
= Platform
.getBundle("eu.etaxonomy.taxeditor.webapp");
83 URL warURL
= bundle
.getEntry("lib/cdmlib-remote-webapp.war");
86 URL resolvedWarURL
= FileLocator
.toFileURL(warURL
);
87 // We need to use the 3-arg constructor of URI in order to properly escape file system chars
88 URI resolvedURI
= new URI(resolvedWarURL
.getProtocol(), resolvedWarURL
.getPath(), null);
89 warFile
= new File(resolvedURI
);
90 System
.out
.println("war url : " + warFile
.getAbsolutePath());
92 if(warFile
== null || !warFile
.exists()) {
93 throw new CdmEmbeddedServerException("Cdmlib War file does not exist");
95 } catch (URISyntaxException use
) {
96 throw new CdmEmbeddedServerException(use
);
97 } catch (IOException ioe
) {
98 throw new CdmEmbeddedServerException(ioe
);
101 System
.setProperty(SPRING_PROFILES_ACTIVE
, "remoting");
102 System
.setProperty(CDM_BEAN_DEFINITION_FILE
, dataSourcesFile
.getAbsolutePath());
103 System
.setProperty(ATTRIBUTE_DATASOURCE_NAME
, dataSourceName
);
105 httpPort
= findFreePort();
107 logger
.warn("Starting server on port : " + httpPort
);
109 server
= new Server(httpPort
);
111 server
.addBean(new AppContextLeakPreventer());
113 WebAppContext webapp
= new WebAppContext();
114 webapp
.setContextPath(contextPath
);
115 webapp
.setWar(warFile
.getAbsolutePath());
116 webapp
.setThrowUnavailableOnStartupException(true);
118 server
.setHandler(webapp
);
121 public String
getDataSourceName() {
122 return dataSourceName
;
124 public String
getHost() {
128 public int getPort() {
132 public String
getContextPath() {
136 private static int findFreePort() throws CdmEmbeddedServerException
{
137 ServerSocket socket
= null;
139 socket
= new ServerSocket(0);
140 socket
.setReuseAddress(true);
141 int port
= socket
.getLocalPort();
144 } catch (IOException e
) {
148 } catch (IOException e
) {
150 if (socket
!= null) {
153 } catch (IOException e
) {
157 throw new CdmEmbeddedServerException("Could not find a free TCP/IP port to start embedded Jetty HTTP Server on");
160 public void start(ICDMServerError cdmServerError
) throws CdmEmbeddedServerException
{
161 start(true, cdmServerError
);
164 public void start(boolean wait
, final ICDMServerError cdmServerError
) throws CdmEmbeddedServerException
{
167 throw new CdmEmbeddedServerException("Server is already disposed");
170 if(server
.isStarting()) {
171 throw new CdmEmbeddedServerException("Server is starting");
174 if(server
.isStarted()) {
175 throw new CdmEmbeddedServerException("Server has started");
178 if(server
.isRunning()) {
179 throw new CdmEmbeddedServerException("Server is running");
182 if(server
.isStopping()) {
183 throw new CdmEmbeddedServerException("Server is currently stopping. Please re-try in about 10 seconds");
186 Thread serverThread
= new Thread() {
191 if(isForceSchemaCreate()) {
192 System
.setProperty(ATTRIBUTE_FORCE_SCHEMA_CREATE
, "true");
193 }else if (isForceSchemaUpdate()){
194 System
.setProperty(ATTRIBUTE_FORCE_SCHEMA_UPDATE
, "true");
198 } catch (Throwable t
) {
199 //wait for 1sec to get the right order of login dialog and error
200 //message when connection fails
203 } catch (InterruptedException e
) {
206 while(cause
!= null) {
208 // pure string comparison to avoid dependencies to hibernate or cdmlib-persistance in the taxeditor
209 if(cause
.getClass().getSimpleName().equals("SchemaExtractionException") || cause
.getClass().getSimpleName().equals("CdmDatabaseException") ) {
210 logger
.debug(cause
.getClass().getName() + " detected which indicates missing or corrupt schema");
213 cause
= cause
.getCause();
216 cdmServerError
.handleError(new RuntimeException("Error during CDM server startup", t
));
221 serverThread
.start();
224 while(!server
.isStarted()) {}
228 public boolean isAlive() {
229 return server
.isRunning() || server
.isStarting() || server
.isStarted();
232 public boolean isStarted() {
233 return server
.isStarted();
236 public boolean isFailed() {
237 return server
.isFailed();
240 public void stop() throws Exception
{
246 public static void stopServerViaJMX(int jmxPort
) throws CdmEmbeddedServerException
{
247 String JMX_URL
= "service:jmx:rmi:///jndi/rmi://localhost:" + jmxPort
+ "/jmxrmi";
248 logger
.warn("Shutting down Jetty instance ... ");
251 JMXConnector connector
= JMXConnectorFactory
.connect(new JMXServiceURL(JMX_URL
), null);
252 connector
.connect(null);
253 MBeanServerConnection connection
= connector
.getMBeanServerConnection();
254 ObjectName objectName
= new ObjectName("org.eclipse.jetty.server:type=server,id=0");
255 connection
.invoke(objectName
, "stop", null, null);
256 logger
.warn("Shutdown command sent");
257 } catch (InstanceNotFoundException e
) {
258 throw new CdmEmbeddedServerException(e
);
259 } catch (MBeanException e
) {
260 throw new CdmEmbeddedServerException(e
);
261 } catch (ReflectionException e
) {
262 throw new CdmEmbeddedServerException(e
);
263 } catch (IOException e
) {
264 throw new CdmEmbeddedServerException(e
);
265 } catch (MalformedObjectNameException e
) {
266 throw new CdmEmbeddedServerException(e
);
270 public boolean isForceSchemaCreate() {
271 return forceSchemaCreate
;
274 public void setForceSchemaCreate(boolean forceSchemaCreate
) {
275 this.forceSchemaCreate
= forceSchemaCreate
;
278 public boolean isForceSchemaUpdate() {
279 return forceSchemaUpdate
;
281 public void setForceSchemaUpdate(boolean forceSchemaUpdate
) {
282 this.forceSchemaUpdate
= forceSchemaUpdate
;