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