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