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