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