a5914aae31e3ac37902d9e0a6454aded30345843
[taxeditor.git] / eu.etaxonomy.taxeditor.cdmlib / src / main / java / eu / etaxonomy / taxeditor / remoting / source / CdmServerInfo.java
1 // $Id$
2 /**
3 * Copyright (C) 2015 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.remoting.source;
11
12 import java.io.File;
13 import java.io.FileInputStream;
14 import java.io.FileNotFoundException;
15 import java.io.FileOutputStream;
16 import java.io.IOException;
17 import java.net.Socket;
18 import java.util.ArrayList;
19 import java.util.Arrays;
20 import java.util.Collections;
21 import java.util.Comparator;
22 import java.util.List;
23
24 import org.apache.commons.lang.StringUtils;
25 import org.apache.http.HttpEntity;
26 import org.apache.http.HttpResponse;
27 import org.apache.http.client.ClientProtocolException;
28 import org.apache.http.client.HttpClient;
29 import org.apache.http.client.ResponseHandler;
30 import org.apache.http.client.methods.HttpGet;
31 import org.apache.http.impl.client.DefaultHttpClient;
32 import org.apache.http.params.HttpConnectionParams;
33 import org.apache.http.params.HttpParams;
34 import org.apache.http.util.EntityUtils;
35 import org.apache.log4j.Logger;
36 import org.json.JSONArray;
37 import org.json.JSONException;
38 import org.json.JSONObject;
39
40 import com.fasterxml.jackson.core.JsonParseException;
41 import com.fasterxml.jackson.databind.JsonMappingException;
42 import com.fasterxml.jackson.databind.ObjectMapper;
43 import com.fasterxml.jackson.databind.type.TypeFactory;
44
45 import eu.etaxonomy.cdm.api.application.CdmApplicationState;
46 import eu.etaxonomy.cdm.common.CdmUtils;
47 import eu.etaxonomy.cdm.config.CdmSourceException;
48 import eu.etaxonomy.cdm.database.CdmPersistentDataSource;
49 import eu.etaxonomy.cdm.database.ICdmDataSource;
50 import eu.etaxonomy.cdm.model.metadata.CdmMetaData;
51 import eu.etaxonomy.taxeditor.remoting.server.CDMServerException;
52 import eu.etaxonomy.taxeditor.remoting.server.CDMServerUtils;
53
54 /**
55 * @author cmathew
56 * @date 20 Jan 2015
57 *
58 */
59 public class CdmServerInfo {
60 public static final Logger logger = Logger.getLogger(CdmServerInfo.class);
61
62 private final static String CDMSERVER_PREFIX = "cdmserver/";
63 private final static String NAME_PRODUCTION = "cybertaxonomy.org";
64 private final static String SERVER_PRODUCTION = "api.cybertaxonomy.org";
65
66 private final static String NAME_INTEGRATION = "edit-integration";
67 private final static String SERVER_INTEGRATION = "int.e-taxonomy.eu";
68
69 private final static String NAME_DEMO_1 = "edit-WS I";
70 private final static String SERVER_DEMO_1 = "160.45.63.230";
71
72 private final static String NAME_DEMO_2 = "edit-WS II";
73 private final static String SERVER_DEMO_2 = "160.45.63.231";
74
75 private final static String NAME_TEST = "edit-test";
76 private final static String SERVER_TEST = "test.e-taxonomy.eu";
77
78 public final static String SERVER_LOCALHOST = "localhost";
79 private final static String NAME_LOCALHOST = "localhost";
80 public final static String NAME_LOCALHOST_MGD = "localhost mgd.";
81
82 private final static String NAME_LOCALHOST_DEV = "localhost-dev";
83 private final static String NAME_INSTANCE_LOCALHOST_DEV = "local-dev";
84 private final static String SERVER_LOCALHOST_DEV = "localhost";
85 private final static int PORT_LOCALHOST_DEV = 8080;
86 private final static String BASEPATH_LOCALHOST_DEV = "";
87
88 public final static int NULL_PORT = -1;
89 public final static String NULL_PORT_STRING = "N/A";
90
91 private static final String CDM_REMOTE_SERVERS_CONFIG_FILE = "cdm_remote_servers.json";
92
93
94 private final String name;
95 private final String server;
96 private int port;
97 private final List<CdmInstanceInfo> instances;
98
99 private String cdmlibServicesVersion = "";
100 private String cdmlibServicesLastModified = "";
101
102 private String prefix = "";
103
104 private boolean ignoreCdmLibVersion = false;
105
106
107 public CdmServerInfo(String name, String server, int port, String prefix, boolean ignoreCdmLibVersion) {
108 this.name = name;
109 this.server = server;
110 this.port = port;
111 this.prefix = prefix;
112 this.ignoreCdmLibVersion = ignoreCdmLibVersion;
113 instances = new ArrayList<CdmInstanceInfo>();
114
115 }
116
117
118 public CdmInstanceInfo addInstance(String name, String basePath) {
119 String _basePath = basePath;
120 if(isLocalhostMgd()) {
121 _basePath = "";
122 }
123 CdmInstanceInfo cii = new CdmInstanceInfo(name, _basePath);
124 instances.add(cii);
125 return cii;
126
127 }
128
129 public boolean isLocalhost() {
130 return name.startsWith(SERVER_LOCALHOST);
131 }
132
133 public boolean isLocalhostMgd() {
134 return NAME_LOCALHOST_MGD.equals(name);
135 }
136
137 public String getCdmlibServicesVersion() {
138 return cdmlibServicesVersion;
139 }
140
141 public String getCdmlibLastModified() {
142 return cdmlibServicesLastModified;
143 }
144
145 public void refreshInstances() throws CDMServerException {
146 instances.clear();
147 if(isLocalhostMgd()) {
148 addInstancesFromDataSourcesConfig();
149 } else {
150 addInstancesViaHttp();
151 }
152 Collections.sort(instances, new Comparator<CdmInstanceInfo>() {
153 @Override
154 public int compare(CdmInstanceInfo cii1, CdmInstanceInfo cii2)
155 {
156 return cii1.getName().toString().compareTo(cii2.getName().toString());
157 }
158 });
159 }
160
161 public void updateInfo() throws CDMServerException {
162 String url = "http://" + server + ":" + String.valueOf(port) + "/" + prefix + "info.jsp";
163 String responseBody = getResponse(url);
164 if(responseBody != null) {
165 try {
166 JSONObject info = new JSONObject(responseBody);
167 cdmlibServicesVersion = info.getString("cdmlibServicesVersion");
168 cdmlibServicesLastModified = info.getString("cdmlibServicesLastModified");
169 } catch (JSONException e) {
170 throw new CDMServerException(e);
171 }
172 }
173 }
174
175 public void addInstancesViaHttp() throws CDMServerException {
176 updateInfo();
177 String url = "http://" + server + ":" + String.valueOf(port) + "/" + prefix + "instances.jsp";
178 String responseBody = getResponse(url);
179 if(responseBody != null) {
180 try {
181 JSONArray array = new JSONArray(responseBody);
182 for(int i=0;i<array.length();i++) {
183 JSONObject instance = (JSONObject)array.get(i);
184 if(instance != null) {
185 JSONObject conf = (JSONObject)instance.get("configuration");
186 if(conf != null) {
187 String instanceName = conf.getString("instanceName");
188 // we need to remove the first (char) forward slash from
189 // the base path
190 String basePath = conf.getString("basePath").substring(1);
191 addInstance(instanceName, basePath);
192 logger.info("Added instance with name : " + instanceName + ", basePath : " + basePath);
193 }
194 }
195 }
196 } catch (JSONException e) {
197 throw new CDMServerException(e);
198 }
199 }
200 }
201
202 private String getResponse(String url) throws CDMServerException {
203 HttpClient client = new DefaultHttpClient();
204 HttpParams params = client.getParams();
205
206 HttpConnectionParams.setConnectionTimeout(params, 5000);
207 HttpConnectionParams.setSoTimeout(params, 5000);
208
209 HttpGet httpGet = new HttpGet(url);
210
211 logger.info("Executing request " + httpGet.getRequestLine());
212
213 // Create a custom response handler
214 ResponseHandler<String> responseHandler = new ResponseHandler<String>() {
215
216 @Override
217 public String handleResponse(
218 final HttpResponse response) throws ClientProtocolException, IOException {
219 int status = response.getStatusLine().getStatusCode();
220 if (status >= 200 && status < 300) {
221 HttpEntity entity = response.getEntity();
222 return entity != null ? EntityUtils.toString(entity) : null;
223 } else {
224 throw new ClientProtocolException("Unexpected response status: " + status);
225 }
226 }
227
228 };
229 String responseBody = null;
230 try {
231 responseBody = client.execute(httpGet, responseHandler);
232 } catch (ClientProtocolException e) {
233 throw new CDMServerException(e);
234 } catch (IOException e) {
235 throw new CDMServerException(e);
236 }
237 return responseBody;
238 }
239
240 public void addInstancesFromDataSourcesConfig() {
241 for(ICdmDataSource dataSource : CdmPersistentDataSource.getAllDataSources()){
242 String datasourceNCName = CDMServerUtils.xmlNCNamefrom(dataSource.getName());
243 logger.info("Adding local instance " + dataSource.getName() + " as " + datasourceNCName);
244 addInstance(datasourceNCName, datasourceNCName);
245 }
246 }
247
248 public String toString(String instanceName, int port) {
249 return server + ":" + String.valueOf(port) + "/" + instanceName;
250 }
251
252 public CdmInstanceInfo getInstanceFromName(String instanceName) {
253 if(instanceName == null) {
254 return null;
255 }
256
257 for(CdmInstanceInfo instance : instances) {
258 if(instance.getName() != null && instance.getName().equals(instanceName)) {
259 return instance;
260 }
261 }
262 return null;
263 }
264
265 public CdmRemoteSource getCdmRemoteSource(CdmInstanceInfo instance, int port) {
266 if(instance != null) {
267 return CdmRemoteSource.NewInstance(name,
268 server,
269 port,
270 instance.getBasePath(),
271 null);
272 }
273 return null;
274 }
275
276 public boolean pingServer() {
277 if(isLocalhostMgd()) {
278 return true;
279 }
280 try {
281 Socket s = new Socket(server, port);
282 logger.info("[CDM-Server] Available @ " + server + ":" + port );
283 updateInfo();
284 return true;
285 } catch (IOException ioe) {
286
287 } catch (CDMServerException e) {
288
289 }
290 return false;
291 }
292
293 public boolean pingInstance(CdmInstanceInfo instance, int port) throws CDMServerException {
294
295 ICdmRemoteSource crs = getCdmRemoteSource(instance, port);
296 try {
297 if(crs != null && crs.checkConnection()) {
298 logger.info("[CDM-Server] Running @ " + server + ":" + port + " for instance " + instance);
299 return true;
300 }
301 } catch (CdmSourceException e) {
302 throw new CDMServerException(e);
303 }
304
305 return false;
306 }
307
308 public int compareDbSchemaVersion(CdmInstanceInfo instance, int port) throws CDMServerException {
309
310 ICdmRemoteSource crs = getCdmRemoteSource(instance, port);
311 String dbSchemaVersion;
312 try {
313 dbSchemaVersion = crs.getDbSchemaVersion();
314 } catch (CdmSourceException e) {
315 throw new CDMServerException(e);
316 }
317
318
319 if(dbSchemaVersion != null) {
320 return CdmMetaData.compareVersion(dbSchemaVersion, CdmMetaData.getDbSchemaVersion(), 3, null);
321 } else {
322 throw new CDMServerException("Cannot determine editor db. schema version");
323 }
324 }
325
326 public int compareCdmlibServicesVersion() throws CdmSourceException {
327
328 String serverVersion = cdmlibServicesVersion;
329 String serverCdmlibLastModified = cdmlibServicesLastModified;
330 if(ignoreCdmLibVersion) {
331 return 0;
332 } else {
333 return compareCdmlibServicesVersion(serverVersion, serverCdmlibLastModified);
334 }
335 }
336
337
338 /**
339 * @param serverVersion
340 * @param editorVersion
341 * @throws CdmSourceException
342 */
343 public static int compareCdmlibServicesVersion(String serverVersion, String serverCdmlibLastModified) throws CdmSourceException {
344
345 String editorVersion = CdmApplicationState.getCdmlibVersion();
346 String editorCdmlibLastModified = CdmApplicationState.getCdmlibLastModified();
347
348 int result = 0;
349 if(StringUtils.isBlank(serverVersion) || StringUtils.isBlank(editorVersion)) {
350 throw new CdmSourceException("cdmlib-services server or editor version is empty");
351 }
352
353 String[] serverVersionSplit = serverVersion.split("\\.");
354 String[] editorVersionSplit = editorVersion.split("\\.");
355
356 if(serverVersionSplit.length < 3 || editorVersionSplit.length < 3 || serverVersionSplit.length > 4 || editorVersionSplit.length > 4) {
357 throw new CdmSourceException("cdmlib-services server or editor version is invalid");
358 }
359
360 Integer serverVersionPart;
361 Integer editorVersionPart;
362
363 for(int i=0 ; i<3 ; i++) {
364 serverVersionPart = Integer.valueOf(serverVersionSplit[i]);
365 editorVersionPart = Integer.valueOf(editorVersionSplit[i]);
366
367 int partCompare = serverVersionPart.compareTo(editorVersionPart);
368 if (partCompare != 0){
369 return partCompare;
370 }
371 }
372 // at this point major, minor and patch versions are matching
373
374 if(StringUtils.isBlank(serverCdmlibLastModified) || StringUtils.isBlank(editorCdmlibLastModified)) {
375 throw new CdmSourceException("cdmlib-services server or editor version is empty");
376 }
377
378 String cdmServerIgnoreVersion = System.getProperty("cdm.server.version.lm.ignore");
379 if(StringUtils.isBlank(cdmServerIgnoreVersion) || !cdmServerIgnoreVersion.equals("true")) {
380 Long serverLastModified = Long.valueOf(serverCdmlibLastModified);
381 Long editorLastModified = Long.valueOf(editorCdmlibLastModified);
382 return serverLastModified.compareTo(editorLastModified);
383 }
384
385 return 0;
386 }
387
388
389
390 public static List<CdmServerInfo> getCdmServers() {
391 return loadFromJSONConfigFile(new File(CdmUtils.perUserCdmFolder, CDM_REMOTE_SERVERS_CONFIG_FILE));
392 }
393
394
395 /**
396 * @param file
397 * @throws IOException
398 * @throws FileNotFoundException
399 * @throws JsonMappingException
400 * @throws JsonParseException
401 */
402 private static List<CdmServerInfo> loadFromJSONConfigFile(File file) {
403
404 List<CdmServerInfo> serverList = null;
405 ObjectMapper mapper = new ObjectMapper();
406
407 if(!file.exists()) {
408 logger.info("The remote server config file '" + file.getAbsolutePath() +
409 "' does not yet exist, it will be created based on the defaults.");
410 try {
411 serverList = createDefaultServerList();
412 mapper.writerWithDefaultPrettyPrinter().writeValue(new FileOutputStream(file), serverList.toArray(new CdmServerInfo[serverList.size()]));
413 } catch (IOException e) {
414 throw new RuntimeException(e);
415 }
416 } else {
417 try {
418 CdmServerInfo[] serverInfo = mapper.readValue(new FileInputStream(file),
419 TypeFactory.defaultInstance().constructArrayType(CdmServerInfo.class));
420 serverList = Arrays.asList(serverInfo);
421 } catch (IOException e) {
422 throw new RuntimeException(e);
423 }
424 }
425
426 return serverList;
427
428
429 }
430
431
432 /**
433 *
434 */
435 private static List<CdmServerInfo> createDefaultServerList() {
436
437 List<CdmServerInfo> serverInfoList = new ArrayList<CdmServerInfo>();
438 // serverInfoList.add(new CdmServerInfo(NAME_PRODUCTION, SERVER_PRODUCTION, 80, ""));
439 //serverInfoList.add(new CdmServerInfo(NAME_INTEGRATION, SERVER_INTEGRATION, 80, CDMSERVER_PREFIX));
440 serverInfoList.add(new CdmServerInfo(NAME_DEMO_1, SERVER_DEMO_1, 80, CDMSERVER_PREFIX, false));
441 // serverInfoList.add(new CdmServerInfo(NAME_DEMO_2, SERVER_DEMO_2, 80, CDMSERVER_PREFIX));
442 serverInfoList.add(new CdmServerInfo(NAME_TEST, SERVER_TEST, 80, CDMSERVER_PREFIX, false));
443 serverInfoList.add(new CdmServerInfo(NAME_LOCALHOST, SERVER_LOCALHOST, 8080, CDMSERVER_PREFIX, true));
444 serverInfoList.add(new CdmServerInfo(NAME_LOCALHOST_MGD, SERVER_LOCALHOST, NULL_PORT, CDMSERVER_PREFIX, false));
445 return serverInfoList;
446 }
447
448 public String getName() {
449 return name;
450 }
451
452 public String getServer() {
453 return server;
454 }
455
456
457 public int getPort() {
458 return port;
459 }
460
461 public void setPort(int port) {
462 this.port = port;
463 }
464
465 public List<CdmInstanceInfo> getInstances() throws CDMServerException {
466 if(instances.isEmpty()) {
467 refreshInstances();
468 }
469 return instances;
470 }
471
472 public static CdmRemoteSource getDevServerRemoteSource() {
473 String value = System.getProperty("cdm.server.dev.port");
474 boolean available = false;
475 CdmInstanceInfo devInstance = null;
476 if(value != null && !value.isEmpty()) {
477 int devPort = Integer.valueOf(value);
478 CdmServerInfo devCii = new CdmServerInfo(NAME_LOCALHOST_DEV, SERVER_LOCALHOST_DEV, devPort, "", false);
479 try {
480 devInstance = devCii.addInstance(NAME_INSTANCE_LOCALHOST_DEV, BASEPATH_LOCALHOST_DEV);
481 available = devCii.pingInstance(devInstance, devPort);
482 if(available) {
483 return devCii.getCdmRemoteSource(devInstance, devPort);
484 }
485 } catch (Exception e) {
486
487 }
488 }
489 return null;
490 }
491
492 public class CdmInstanceInfo {
493 private final String name;
494
495 /**
496 * The full path of the instance including the the prefix (if any).
497 * E.g. for an EDIT instance this would be something like "cdmserver/remoting"
498 * For a managed local server this would simply be ""
499 */
500 private final String basePath;
501
502
503 public CdmInstanceInfo(String name, String basePath) {
504 this.name = name;
505 this.basePath = basePath;
506 }
507
508
509 public String getName() {
510 return name;
511 }
512
513 public String getBasePath() {
514 return basePath;
515 }
516 }
517 }