Merge branch 'develop' into remoting-4.0
[taxeditor.git] / eu.etaxonomy.taxeditor.test / src / test / java / eu / etaxonomy / taxeditor / httpinvoker / 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.httpinvoker;
11
12 import java.io.BufferedReader;
13 import java.io.File;
14 import java.io.FileInputStream;
15 import java.io.FileNotFoundException;
16 import java.io.IOException;
17 import java.io.InputStream;
18 import java.io.InputStreamReader;
19 import java.net.URISyntaxException;
20 import java.net.URL;
21 import java.util.Properties;
22
23 import javax.management.InstanceNotFoundException;
24 import javax.management.MBeanException;
25 import javax.management.MBeanServerConnection;
26 import javax.management.MalformedObjectNameException;
27 import javax.management.ObjectName;
28 import javax.management.ReflectionException;
29 import javax.management.remote.JMXConnector;
30 import javax.management.remote.JMXConnectorFactory;
31 import javax.management.remote.JMXServiceURL;
32 import javax.sql.DataSource;
33
34 import org.apache.log4j.Logger;
35 import org.eclipse.core.runtime.FileLocator;
36 import org.springframework.core.io.ClassPathResource;
37 import org.springframework.core.io.Resource;
38 import org.unitils.database.annotations.TestDataSource;
39
40 import eu.etaxonomy.cdm.database.CdmPersistentDataSource;
41 import eu.etaxonomy.cdm.database.ICdmDataSource;
42 import eu.etaxonomy.taxeditor.remoting.server.CDMServerException;
43 import eu.etaxonomy.taxeditor.remoting.source.CdmRemoteSourceBase;
44
45 /**
46 *
47 * (Singleton) Server instance which manages a compatible cdmlib-webapp-war.
48 * This is done by launching a jetty instance (using jetty-runner) as an
49 * executed process. The choice of the external process over a more
50 * preferable 'embedded jetty' instance is due to problems arising from the
51 * class loading of classes (e.g. from hibernate core) which are needed
52 * for both the webapp as well as the remoting client.
53 *
54 * @author cmathew
55 * @date 23 Sep 2014
56 *
57 */
58
59 public class CDMServer {
60
61 public static final Logger logger = Logger.getLogger(CDMServer.class);
62
63 @TestDataSource
64 protected DataSource dataSource;
65
66 private final String name = "default";
67 private final String host = "127.0.0.1";
68 private int httpPort = 9090;
69 private int stopPort = 9191;
70 private String stopKey = "jetty-cdm-server";
71 private final String contextPath = "";
72
73 public static final Resource DEFAULT_CDM_WEBAPP_RESOURCE =
74 new ClassPathResource("/etc/jetty/cdmlib-remote-webapp.war");
75
76 public static final Resource DEFAULT_DATASOURCE_FILE =
77 new ClassPathResource("datasources.xml");
78
79 public static final Resource DEFAULT_JETTY_RUNNER_RESOURCE =
80 new ClassPathResource("/etc/jetty/jetty-runner-9.2.3.v20140905.jar");
81
82 public static final Resource DEFAULT_JETTY_RESOURCE =
83 new ClassPathResource("/etc/jetty/start-9.2.3.v20140905.jar");
84
85 private static CDMServer cdmServer = null;
86 private static CDMServerException cdmse = null;
87
88 private boolean serverAlreadyRunning = false;
89
90 private File dataSourcesFile;
91 private final String dataSourceName;
92
93 public CDMServer(String dataSourceName, URL serverPropertiesURL) throws CDMServerException {
94 this.dataSourceName = dataSourceName;
95 Properties prop = new Properties();
96
97 try {
98 File serverPropertiesFile = new File(FileLocator.resolve(serverPropertiesURL).toURI());
99 InputStream inputStream = new FileInputStream(serverPropertiesFile);
100
101 prop.load(inputStream);
102 inputStream.close();
103
104 } catch (FileNotFoundException e) {
105 throw new CDMServerException(e);
106 } catch (URISyntaxException e) {
107 throw new CDMServerException(e);
108 } catch (IOException e) {
109 throw new CDMServerException(e);
110 }
111
112
113
114 if(prop.getProperty("httpPort") != null) {
115 setHttpPort(Integer.valueOf(prop.getProperty("httpPort")));
116 }
117
118 if(prop.getProperty("stopPort") != null) {
119 setStopPort(Integer.valueOf(prop.getProperty("stopPort")));
120 }
121
122 if(prop.getProperty("stopKey") != null) {
123 setStopKey(prop.getProperty("stopKey"));
124 }
125
126 }
127
128
129
130 public String getName() {
131 return name;
132 }
133
134 public String getHost() {
135 return host;
136 }
137
138 public int getPort() {
139 return httpPort;
140 }
141
142 public String getContextPath() {
143 return contextPath;
144 }
145
146 public void setHttpPort(int port) {
147 this.httpPort = port;
148 }
149
150 public void setStopPort(int stopPort) {
151 this.stopPort = stopPort;
152 }
153
154 public void setStopKey(String stopKey) {
155 this.stopKey = stopKey;
156 }
157
158
159 public static boolean isRunningInEclipse() {
160 return (System.getProperty("sun.java.command") != null &&
161 System.getProperty("sun.java.command").startsWith("org.eclipse.jdt.internal.junit.runner.RemoteTestRunner"));
162 }
163
164 private String getVMArgs() throws IOException {
165 StringBuilder sb = new StringBuilder();
166 sb.append(" -Dspring.profiles.active=remoting");
167 sb.append(" -Dcdm.beanDefinitionFile=" + DEFAULT_DATASOURCE_FILE.getFile().getAbsolutePath());
168 sb.append(" -Dcdm.datasource=cdmTest");
169 return sb.toString();
170 }
171
172 private String getStartServerArgs() throws IOException {
173 StringBuilder sb = new StringBuilder();
174 sb.append(" --port " + httpPort);
175 return sb.toString();
176 }
177
178 private String getStopServerSettings() {
179 StringBuilder sb = new StringBuilder();
180 sb.append(" --stop-port ");
181 sb.append(stopPort);
182 sb.append(" --stop-key ");
183 sb.append(stopKey);
184 return sb.toString();
185 }
186
187 private String getStopServerArgs() {
188 StringBuilder sb = new StringBuilder();
189 sb.append(" STOP.PORT=");
190 sb.append(stopPort);
191 sb.append(" STOP.KEY=");
192 sb.append(stopKey);
193 return sb.toString();
194 }
195
196
197 public void start() throws CDMServerException {
198
199 /**
200 * First check if the CDM server responds to a service request, which implies that
201 * the server has started properly. If no response is received then check if the
202 * server is listening on specific host / port, which implies that the server
203 * has started but incorrectly, in which case we try to force stop it (if we can)
204 * and start a new server.
205 */
206 if(isStarted(1)) {
207 logger.info("[CDM-Server] Server already running @ " + host + ":" + httpPort );
208 serverAlreadyRunning = true;
209 return;
210 }
211
212 Thread t = new Thread() {
213 @Override
214 public void run() {
215
216 StringBuffer output = new StringBuffer();
217 try{
218 Process p;
219 String command = "java "
220 + getVMArgs()
221 + " -jar "
222 + DEFAULT_JETTY_RUNNER_RESOURCE.getFile().getAbsolutePath()
223 + getStartServerArgs()
224 + getStopServerSettings()
225 + " "
226 + DEFAULT_CDM_WEBAPP_RESOURCE.getFile().getAbsolutePath();
227 logger.info("[CDM-Server] Starting server with Command : " + command);
228 p = Runtime.getRuntime().exec(command);
229
230 BufferedReader inpReader =
231 new BufferedReader(new InputStreamReader(p.getInputStream()));
232
233 BufferedReader errReader =
234 new BufferedReader(new InputStreamReader(p.getErrorStream()));
235
236 String line = "";
237 while ((line = inpReader.readLine())!= null) {
238 logger.info("[CDM-Server Start] : " + line);
239 }
240
241 while ((line = errReader.readLine())!= null) {
242 logger.info("[CDM-Server Start] : " + line);
243 }
244
245 } catch (Exception e) {
246 e.printStackTrace();
247 cdmse = new CDMServerException(e);
248 }
249 }
250 };
251
252 t.setDaemon(true);
253 cdmse = null;
254 t.start();
255
256 if(isStarted(50)) {
257 logger.info("[CDM-Server] Server running @ " + host + ":" + httpPort );
258 } else {
259 logger.info("[CDM-Server] Server not started within given interval");
260 // making sure to kill server if it is not started correctly
261 try {
262 stop(true);
263 } catch (Exception e) {
264 throw new CDMServerException("CDM Server could not be stopped : " + e.getMessage());
265 }
266 throw new CDMServerException("CDM Server not started : ");
267 }
268
269 }
270
271
272 public boolean isStarted(int checkingIntervals) throws CDMServerException {
273 CdmRemoteSourceBase crsb = new CdmRemoteSourceBase("local-cdm-server",
274 host,
275 httpPort,
276 contextPath,
277 null);
278 int intervalsCount = 0;
279 do {
280 try {
281 if(cdmse != null) {
282 return false;
283 }
284 boolean check = crsb.checkConnection();
285 if(check) {
286 logger.info("[CDM-Server] Running @ " + host + ":" + httpPort );
287 return true;
288 }
289 } catch (Exception e) {
290
291 }
292 try {
293 Thread.sleep(1000);
294 } catch (InterruptedException ie) {
295 throw new CDMServerException("Error checking CDM Server status", ie);
296 }
297 intervalsCount++;
298 } while (intervalsCount < checkingIntervals);
299 return false;
300 }
301
302 public void stop() throws Exception {
303 stop(false);
304 }
305
306 public void stop(boolean force) throws Exception {
307
308 if(!force) {
309 if(!cdmServer.isStarted(1)) {
310 logger.info("[CDM-Server] Server already stopped @ " + host + ":" + httpPort );
311 return;
312 }
313 }
314
315 if(serverAlreadyRunning) {
316 return;
317 }
318 Thread t = new Thread() {
319 @Override
320 public void run() {
321 StringBuffer output = new StringBuffer();
322 try{
323 Process p;
324 String command = "java -jar " + DEFAULT_JETTY_RESOURCE.getFile().getAbsolutePath()
325 + getStopServerArgs() + " --stop ";
326 logger.info("[CDM-Server] Stop Command : " + command);
327 p = Runtime.getRuntime().exec(command);
328
329 BufferedReader inpReader =
330 new BufferedReader(new InputStreamReader(p.getInputStream()));
331
332 BufferedReader errReader =
333 new BufferedReader(new InputStreamReader(p.getErrorStream()));
334
335 String line = "";
336 while ((line = inpReader.readLine())!= null) {
337 logger.info("[CDM-Server Stop] : " + line);
338 }
339
340 while ((line = errReader.readLine())!= null) {
341 logger.info("[CDM-Server Stop] : " + line);
342 }
343 logger.info("CDM-Server Stopped : ");
344 } catch (Exception e) {
345 logger.info("[CDM-Server] Could not stop @ " + host + ":" + httpPort + ". Please kill it manually");
346
347 }
348
349 }
350 };
351
352 t.setDaemon(true);
353 t.start();
354
355 }
356
357 public static void stopServerViaJMX(int jmxPort) throws CDMServerException {
358 String JMX_URL = "service:jmx:rmi:///jndi/rmi://localhost:" + jmxPort + "/jmxrmi";
359 logger.warn("Shutting down Jetty instance ... ");
360
361 try {
362 JMXConnector connector = JMXConnectorFactory.connect(new JMXServiceURL(JMX_URL), null);
363 connector.connect(null);
364 MBeanServerConnection connection = connector.getMBeanServerConnection();
365 ObjectName objectName = new ObjectName("org.eclipse.jetty.server:type=server,id=0");
366 connection.invoke(objectName, "stop", null, null);
367 logger.warn("Shutdown command sent");
368 } catch (InstanceNotFoundException e) {
369 throw new CDMServerException(e);
370 } catch (MBeanException e) {
371 throw new CDMServerException(e);
372 } catch (ReflectionException e) {
373 throw new CDMServerException(e);
374 } catch (IOException e) {
375 throw new CDMServerException(e);
376 } catch (MalformedObjectNameException e) {
377 throw new CDMServerException(e);
378 }
379 }
380
381 public void convertEditorToServerConfig() {
382 String xmlString = "<?xml version=\"1.0\" encoding=\"UTF-8\"?> " + System.lineSeparator() +
383 "<beans xmlns=\"http://www.springframework.org/schema/beans\"" + System.lineSeparator() +
384 "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"" + System.lineSeparator() +
385 "xmlns:tx=\"http://www.springframework.org/schema/tx\"" + System.lineSeparator() +
386 "xmlns:context=\"http://www.springframework.org/schema/context\"" + System.lineSeparator() +
387 "xsi:schemaLocation=\"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd" + System.lineSeparator() +
388 "http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd" + System.lineSeparator() +
389 "http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd" + System.lineSeparator() +
390 ">" + System.lineSeparator() +
391 "<bean id=\"dataSourceProperties\" class=\"eu.etaxonomy.cdm.remote.config.DataSourceProperties\">" + System.lineSeparator() +
392 " <property name=\"propsMap\">" + System.lineSeparator() +
393 " <map/>" + System.lineSeparator() +
394 " </property>" + System.lineSeparator() +
395 "</bean>";
396
397 for(ICdmDataSource dataSource : CdmPersistentDataSource.getAllDataSources()) {
398
399 }
400 }
401 }