98e68a5d4c080b84f09e38fe1b2ab6f883c9184e
[taxeditor.git] / eu.etaxonomy.taxeditor.webapp / src / main / java / eu / etaxonomy / taxeditor / webapp / CDMServer.java
1 // $Id$
2 /**
3 * Copyright (C) 2014 EDIT
4 * European Distributed Institute of Taxonomy
5 * http://www.e-taxonomy.eu
6 *
7 * The contents of this file are subject to the Mozilla Public License Version 1.1
8 * See LICENSE.TXT at the top of this package for the full license terms.
9 */
10 package eu.etaxonomy.taxeditor.webapp;
11
12 import java.io.File;
13 import java.io.IOException;
14 import java.net.ServerSocket;
15 import java.net.URI;
16 import java.net.URISyntaxException;
17 import java.net.URL;
18
19 import javax.management.InstanceNotFoundException;
20 import javax.management.MBeanException;
21 import javax.management.MBeanServerConnection;
22 import javax.management.MalformedObjectNameException;
23 import javax.management.ObjectName;
24 import javax.management.ReflectionException;
25 import javax.management.remote.JMXConnector;
26 import javax.management.remote.JMXConnectorFactory;
27 import javax.management.remote.JMXServiceURL;
28
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.StringUtil;
34 import org.eclipse.jetty.util.preventers.AppContextLeakPreventer;
35 import org.eclipse.jetty.webapp.WebAppContext;
36 import org.osgi.framework.Bundle;
37
38 /**
39 *
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.
46 *
47 * @author cmathew
48 * @date 23 Sep 2014
49 *
50 */
51
52 public class CDMServer {
53
54 public static final Logger logger = Logger.getLogger(CDMServer.class);
55
56
57 private final String dataSourceName;
58 private final String host = "127.0.0.1";
59 private int httpPort = 9090;
60 private final String contextPath = "/";
61
62 private File warFile;
63 private Server server;
64
65
66 public CDMServer(String dataSourceName, File dataSourcesFile) throws CDMEmbeddedServerException {
67 if(StringUtil.isBlank(dataSourceName)) {
68 throw new CDMEmbeddedServerException("DataSource name is not valid");
69 }
70
71 if(dataSourcesFile == null || !dataSourcesFile.exists()) {
72 throw new CDMEmbeddedServerException("DataSource config file does not exist");
73 }
74 this.dataSourceName = dataSourceName;
75 Bundle bundle = Platform.getBundle("eu.etaxonomy.taxeditor.webapp");
76 URL warURL = bundle.getEntry("lib/cdmlib-remote-webapp.war");
77
78 try {
79 URL resolvedWarURL = FileLocator.toFileURL(warURL);
80 // We need to use the 3-arg constructor of URI in order to properly escape file system chars
81 URI resolvedURI = new URI(resolvedWarURL.getProtocol(), resolvedWarURL.getPath(), null);
82 warFile = new File(resolvedURI);
83 System.out.println("war url : " + warFile.getAbsolutePath());
84
85 if(warFile == null || !warFile.exists()) {
86 throw new CDMEmbeddedServerException("Cdmlib War file does not exist");
87 }
88 } catch (URISyntaxException use) {
89 throw new CDMEmbeddedServerException(use);
90 } catch (IOException ioe) {
91 throw new CDMEmbeddedServerException(ioe);
92 }
93
94 System.setProperty("spring.profiles.active", "remoting");
95 System.setProperty("cdm.beanDefinitionFile", dataSourcesFile.getAbsolutePath());
96 System.setProperty("cdm.datasource", dataSourceName);
97
98 httpPort = findFreePort();
99
100 logger.warn("Starting server on port : " + httpPort);
101
102 server = new Server(httpPort);
103
104 server.addBean(new AppContextLeakPreventer());
105
106 WebAppContext webapp = new WebAppContext();
107 webapp.setContextPath(contextPath);
108 webapp.setWar(warFile.getAbsolutePath());
109
110 server.setHandler(webapp);
111 }
112
113 public String getDataSourceName() {
114 return dataSourceName;
115 }
116 public String getHost() {
117 return host;
118 }
119
120 public int getPort() {
121 return httpPort;
122 }
123
124 public String getContextPath() {
125 return contextPath;
126 }
127
128 private static int findFreePort() throws CDMEmbeddedServerException {
129 ServerSocket socket = null;
130 try {
131 socket = new ServerSocket(0);
132 socket.setReuseAddress(true);
133 int port = socket.getLocalPort();
134 try {
135 socket.close();
136 } catch (IOException e) {
137
138 }
139 return port;
140 } catch (IOException e) {
141 } finally {
142 if (socket != null) {
143 try {
144 socket.close();
145 } catch (IOException e) {
146 }
147 }
148 }
149 throw new CDMEmbeddedServerException("Could not find a free TCP/IP port to start embedded Jetty HTTP Server on");
150 }
151
152 public void start(ICDMServerError cdmServerError) throws CDMEmbeddedServerException {
153 start(true, cdmServerError);
154 }
155
156 public void start(boolean wait, final ICDMServerError cdmServerError) throws CDMEmbeddedServerException {
157
158 if(server == null) {
159 throw new CDMEmbeddedServerException("Server is already disposed");
160 }
161
162 if(server.isStarting()) {
163 throw new CDMEmbeddedServerException("Server is starting");
164 }
165
166 if(server.isStarted()) {
167 throw new CDMEmbeddedServerException("Server has started");
168 }
169
170 if(server.isRunning()) {
171 throw new CDMEmbeddedServerException("Server is running");
172 }
173
174 if(server.isStopping()) {
175 throw new CDMEmbeddedServerException("Server is currently stopping. Please re-try in about 10 seconds");
176 }
177
178 Thread serverThread = new Thread() {
179
180 @Override
181 public void run() {
182 try {
183 server.start();
184 server.join();
185 } catch (Throwable t) {
186 cdmServerError.handleError(t);
187 }
188 }
189 };
190
191 serverThread.start();
192
193 if(wait) {
194 while(!server.isStarted()) {}
195
196 }
197 }
198
199 public boolean isAlive() {
200 return server.isRunning() || server.isStarting() || server.isStarted();
201 }
202
203 public boolean isStarted() {
204 return server.isStarted();
205 }
206
207 public void stop() throws Exception {
208 server.stop();
209 server.destroy();
210 server = null;
211 }
212
213
214
215
216 public static void stopServerViaJMX(int jmxPort) throws CDMEmbeddedServerException {
217 String JMX_URL = "service:jmx:rmi:///jndi/rmi://localhost:" + jmxPort + "/jmxrmi";
218 logger.warn("Shutting down Jetty instance ... ");
219
220 try {
221 JMXConnector connector = JMXConnectorFactory.connect(new JMXServiceURL(JMX_URL), null);
222 connector.connect(null);
223 MBeanServerConnection connection = connector.getMBeanServerConnection();
224 ObjectName objectName = new ObjectName("org.eclipse.jetty.server:type=server,id=0");
225 connection.invoke(objectName, "stop", null, null);
226 logger.warn("Shutdown command sent");
227 } catch (InstanceNotFoundException e) {
228 throw new CDMEmbeddedServerException(e);
229 } catch (MBeanException e) {
230 throw new CDMEmbeddedServerException(e);
231 } catch (ReflectionException e) {
232 throw new CDMEmbeddedServerException(e);
233 } catch (IOException e) {
234 throw new CDMEmbeddedServerException(e);
235 } catch (MalformedObjectNameException e) {
236 throw new CDMEmbeddedServerException(e);
237 }
238 }
239
240
241 }