Project

General

Profile

Download (19.7 KB) Statistics
| Branch: | Tag: | Revision:
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.config.RequestConfig;
28
import org.apache.http.client.methods.HttpGet;
29
import org.apache.http.impl.client.CloseableHttpClient;
30
import org.apache.http.impl.client.HttpClientBuilder;
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.CdmApplicationRemoteConfiguration;
41
import eu.etaxonomy.cdm.api.application.CdmApplicationState;
42
import eu.etaxonomy.cdm.config.CdmSourceException;
43
import eu.etaxonomy.cdm.config.ConfigFileUtil;
44
import eu.etaxonomy.cdm.config.ICdmSource;
45
import eu.etaxonomy.cdm.database.CdmPersistentDataSource;
46
import eu.etaxonomy.cdm.database.ICdmDataSource;
47
import eu.etaxonomy.cdm.model.metadata.CdmMetaData;
48
import eu.etaxonomy.taxeditor.remoting.server.CdmServerException;
49
import eu.etaxonomy.taxeditor.remoting.server.CdmServerUtils;
50

    
51
/**
52
 * @author cmathew
53
 * @date 20 Jan 2015
54
 */
55
public class CdmServerInfo {
56

    
57
    public static final Logger logger = Logger.getLogger(CdmServerInfo.class);
58

    
59
    private final static String CDMSERVER_PREFIX = "cdmserver/";
60
    private final static String NAME_PRODUCTION = "cybertaxonomy.org";
61
    private final static String SERVER_PRODUCTION = "api.cybertaxonomy.org";
62

    
63
    private final static String NAME_DEMO_1 = "demo I";
64
    private final static String SERVER_DEMO_1 = "160.45.63.230";
65

    
66
    public final static String SERVER_LOCALHOST = "localhost";
67
    private final static String NAME_LOCALHOST = "localhost";
68
    public final static String NAME_LOCALHOST_MGD = "localhost mgd.";
69

    
70
    private final static String NAME_LOCALHOST_DEV = "localhost-dev";
71
    private final static String NAME_INSTANCE_LOCALHOST_DEV = "local-dev";
72
    private final static String SERVER_LOCALHOST_DEV = "localhost";
73
    private final static String BASEPATH_LOCALHOST_DEV = "";
74

    
75
    public final static int NULL_PORT = -1;
76
    public final static String NULL_PORT_STRING = "N/A";
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
        this.instances = new ArrayList<>();
101
    }
102

    
103
    public CdmInstanceInfo addInstance(String name, ICdmDataSource dataSource) {
104
        CdmInstanceInfo cii = new CdmInstanceInfo(name, dataSource);
105
        instances.add(cii);
106
        return cii;
107
    }
108

    
109
    public CdmInstanceInfo addInstance(String name, String basePath) {
110
        CdmInstanceInfo cii = new CdmInstanceInfo(name, basePath);
111
        instances.add(cii);
112
        return cii;
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
                return cii1.getName().toString().compareTo(cii2.getName().toString());
142
            }
143
        });
144
    }
145

    
146
    public void updateInfo() throws CdmServerException {
147

    
148
        String url = guessProtocol() + "://" + 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
    String guessProtocol() {
162
        return port == 443 ? "https" : "http";
163
    }
164

    
165
    public void addInstancesViaHttp() throws CdmServerException {
166
        updateInfo();
167
        String url = guessProtocol() + "://" + server + ":" + String.valueOf(port) + "/" + prefix + "instances.jsp";
168
        String responseBody = getResponse(url);
169
        if(responseBody != null) {
170
            try {
171
                JSONArray array = new JSONArray(responseBody);
172
                for(int i=0;i<array.length();i++) {
173
                    JSONObject instance = (JSONObject)array.get(i);
174
                    if(instance != null) {
175
                        JSONObject conf = (JSONObject)instance.get("configuration");
176
                        if(conf != null) {
177
                            String instanceName = conf.getString("instanceName");
178
                            // we need to remove the first (char) forward slash from
179
                            // the base path
180
                            String basePath = conf.getString("basePath").substring(1);
181
                            addInstance(instanceName, basePath);
182
                            logger.info("Added instance with name : " + instanceName + ", basePath : " + basePath);
183
                        }
184
                    }
185
                }
186
            } catch (JSONException e) {
187
                throw new CdmServerException(e);
188
            }
189
        }
190
    }
191

    
192
    private String getResponse(String url) throws CdmServerException {
193

    
194
        RequestConfig.Builder requestBuilder = RequestConfig.custom();
195
        requestBuilder.setConnectTimeout(CdmApplicationRemoteConfiguration.HTTP_READ_TIMEOUT_MIN);
196
        requestBuilder.setConnectionRequestTimeout(CdmApplicationRemoteConfiguration.HTTP_READ_TIMEOUT_MIN);
197
        requestBuilder.setSocketTimeout(CdmApplicationRemoteConfiguration.HTTP_READ_TIMEOUT_MIN);
198
        CloseableHttpClient client = HttpClientBuilder.create().setDefaultRequestConfig(requestBuilder.build()).build();
199

    
200
        HttpGet httpGet = new HttpGet(url);
201

    
202
        logger.info("Executing request " + httpGet.getRequestLine());
203

    
204
        // Create a custom response handler
205
        ResponseHandler<String> responseHandler = new ResponseHandler<String>() {
206

    
207
            @Override
208
            public String handleResponse(
209
                    final HttpResponse response) throws ClientProtocolException, IOException {
210
                int status = response.getStatusLine().getStatusCode();
211
                if (status >= 200 && status < 300) {
212
                    HttpEntity entity = response.getEntity();
213
                    return entity != null ? EntityUtils.toString(entity) : null;
214
                } else {
215
                    throw new ClientProtocolException("Unexpected response status: " + status);
216
                }
217
            }
218

    
219
        };
220
        String responseBody = null;
221
        try {
222
            responseBody = client.execute(httpGet, responseHandler);
223
        } catch (ClientProtocolException e) {
224
            throw new CdmServerException(e);
225
        } catch (IOException e) { // java.net.ConnectException: Connection refused, java.net.SocketTimeoutException: Read timed out
226
            throw new CdmServerException(e);
227
        } finally{
228
            try {
229
                client.close();
230
                client = null;
231
            } catch (IOException e) {
232
                // IGNORE //
233
            }
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, dataSource);
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 ICdmRemoteSource getCdmRemoteSource(CdmInstanceInfo instance, int port) {
264
        if(instance != null) {
265
            if (instance.getDataSource()!=null){
266
                return CdmRemoteLocalhostSource.NewInstance(name,
267
                        server,
268
                        port,
269
                        instance.name
270
                        );
271
            }else{
272
                return CdmRemoteSource.NewInstance(name,
273
                        server,
274
                        port,
275
                        instance.getBasePath()
276
                        );
277
            }
278
        }
279
        return null;
280
    }
281

    
282
    public boolean pingServer() throws CdmServerException, IOException {
283
        if(isLocalhostMgd()) {
284
            return true;
285
        }
286
        Socket s = new Socket();
287
        s.connect(new InetSocketAddress(server, port), CdmApplicationRemoteConfiguration.HTTP_READ_TIMEOUT_MIN);
288
        s.close();
289
        logger.info("[CDM-Server] Available @ " + server + ":" + port );
290
        updateInfo();
291
        return true;
292
    }
293

    
294
    public boolean pingInstance(CdmInstanceInfo instance, int port) throws CdmServerException  {
295
        ICdmSource cdmRemoteSource = getCdmRemoteSource(instance, port);
296
        try {
297
            if(cdmRemoteSource != null && cdmRemoteSource.checkConnection()) {
298
                logger.info("[CDM-Server] Running @ " + server + ":" + port + " for instance " + instance);
299
                return true;
300
            }
301
        } catch (CdmSourceException e) {
302
            logger.error(e.getMessage(), e);
303
            throw new CdmServerException(e);
304
        }
305

    
306
        return false;
307
    }
308

    
309
    public int compareDbSchemaVersion(CdmInstanceInfo instance, int port) throws CdmServerException {
310

    
311
        ICdmSource remoteSource = getCdmRemoteSource(instance, port);
312
        String remoteDbSchemaVersion;
313
        try {
314
            remoteDbSchemaVersion = remoteSource.getDbSchemaVersion();
315
        } catch (CdmSourceException e) {
316
            throw new CdmServerException(e);
317
        }
318

    
319
        if(remoteDbSchemaVersion != null) {
320
            return CdmMetaData.compareVersion(remoteDbSchemaVersion, 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
    public static int compareCdmlibServicesVersion(String serverVersion, String serverCdmlibLastModified) throws CdmSourceException {
338

    
339
        String editorVersion = CdmApplicationState.getCdmlibVersion();
340
        String editorCdmlibLastModified = CdmApplicationState.getCdmlibLastModified();
341

    
342
        int result = 0;
343
        if(StringUtils.isBlank(serverVersion) || StringUtils.isBlank(editorVersion)) {
344
            throw new CdmSourceException("cdmlib-services server or editor version is empty");
345
        }
346

    
347
        String[] serverVersionSplit = serverVersion.split("\\.");
348
        String[] editorVersionSplit = editorVersion.split("\\.");
349

    
350
        if(serverVersionSplit.length < 3 || editorVersionSplit.length < 3 || serverVersionSplit.length > 4 || editorVersionSplit.length > 4) {
351
            throw new CdmSourceException("cdmlib-services server or editor version is invalid");
352
        }
353

    
354
        Integer serverVersionPart;
355
        Integer editorVersionPart;
356

    
357
        for(int i=0 ; i<3 ; i++) {
358
            serverVersionPart = Integer.valueOf(serverVersionSplit[i]);
359
            editorVersionPart = Integer.valueOf(editorVersionSplit[i]);
360

    
361
            int partCompare = serverVersionPart.compareTo(editorVersionPart);
362
            if (partCompare != 0){
363
                return partCompare;
364
            }
365
        }
366
        // at this point major, minor and patch versions are matching
367

    
368
        if(StringUtils.isBlank(serverCdmlibLastModified) || StringUtils.isBlank(editorCdmlibLastModified)) {
369
            throw new CdmSourceException("cdmlib-services server or editor version is empty");
370
        }
371

    
372
        String cdmServerIgnoreVersion = System.getProperty("cdm.server.version.lm.ignore");
373
        if(StringUtils.isBlank(cdmServerIgnoreVersion) || !cdmServerIgnoreVersion.equals("true")) {
374
            Long serverLastModified = Long.valueOf(serverCdmlibLastModified);
375
            Long editorLastModified = Long.valueOf(editorCdmlibLastModified);
376
            return serverLastModified.compareTo(editorLastModified);
377
        }
378

    
379
        return result;
380
    }
381

    
382
    public static List<CdmServerInfo> getCdmServers() {
383
        List<CdmServerInfoConfig> configList;
384
        File file = new File(ConfigFileUtil.perUserCdmFolderFallback(), CDM_REMOTE_SERVERS_CONFIG_FILE);
385
        if (file.exists()){
386
            configList = loadFromConfigFile(file);
387
        }else{
388
            configList = loadFromConfigFile(new File(ConfigFileUtil.perUserCdmFolderFallback(), CDM_REMOTE_SERVERS_CONFIG_FILE));
389
        }
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
    private static List<CdmServerInfoConfig>  loadFromConfigFile(File file) {
401

    
402
        List<CdmServerInfoConfig> serverList = null;
403

    
404
        ObjectMapper mapper = new ObjectMapper();
405

    
406
        if(!file.exists()) {
407
            logger.info("The remote server config file '" + file.getAbsolutePath() +
408
                    "' does not yet exist, it will be created based on the defaults.");
409
             try {
410
                serverList = createDefaultServerConfigList();
411
                mapper.writerWithDefaultPrettyPrinter().writeValue(new FileOutputStream(file), serverList);
412

    
413
            } catch (IOException e) {
414
                throw new RuntimeException(e);
415
            }
416
        } else {
417
            try {
418
                serverList = mapper.readValue(new FileInputStream(file), new TypeReference<List<CdmServerInfoConfig>>(){});
419
            } catch (IOException e) {
420
               throw new RuntimeException(e);
421
            }
422
        }
423

    
424
        return serverList;
425
    }
426

    
427
    private static List<CdmServerInfoConfig> createDefaultServerConfigList() {
428

    
429
        List<CdmServerInfoConfig> serverInfoList = new ArrayList<>();
430
        serverInfoList.add(new CdmServerInfoConfig(NAME_PRODUCTION, SERVER_PRODUCTION, 80, "", false));
431
        serverInfoList.add(new CdmServerInfoConfig(NAME_DEMO_1, SERVER_DEMO_1, 80, CDMSERVER_PREFIX, false));
432
        serverInfoList.add(new CdmServerInfoConfig(NAME_LOCALHOST, SERVER_LOCALHOST, 8080, CDMSERVER_PREFIX, true));
433
        return serverInfoList;
434
    }
435

    
436
    public String getName() {
437
        return name;
438
    }
439

    
440
    public String getServer() {
441
        return server;
442
    }
443

    
444
    public int getPort() {
445
        return port;
446
    }
447

    
448
    public void setPort(int port) {
449
        this.port = port;
450
    }
451

    
452
    public List<CdmInstanceInfo> getInstances() throws CdmServerException {
453
        if(instances.isEmpty()) {
454
            refreshInstances();
455
        }
456
        return instances;
457
    }
458

    
459
    public static ICdmSource getDevServerRemoteSource() {
460
        String value = System.getProperty("cdm.server.dev.port");
461
        boolean available = false;
462
        CdmInstanceInfo devInstance = null;
463
        if(value != null && !value.isEmpty()) {
464
            int devPort = Integer.valueOf(value);
465
            CdmServerInfo devCii = new CdmServerInfo(new CdmServerInfoConfig(NAME_LOCALHOST_DEV, SERVER_LOCALHOST_DEV, devPort, "", false));
466
            try {
467
                devInstance = devCii.addInstance(NAME_INSTANCE_LOCALHOST_DEV, BASEPATH_LOCALHOST_DEV);
468
                available = devCii.pingInstance(devInstance, devPort);
469
                if(available) {
470
                    logger.info("Will connect local development cdm instance: at port " + devPort);
471
                    return devCii.getCdmRemoteSource(devInstance, devPort);
472
                }
473
            } catch (CdmServerException e) {
474
                logger.error("Can not connect to local development cdm instance at port " + devPort + ". "
475
                        + "Make sure the cdm instance is running and that the Spring profile \"remoting\" is "
476
                        + "activated (-Dspring.profiles.active=remoting)", e);
477
                //TODO show message dialog only instead of throwing the exception to show the error
478
                // dialog is not necessary in this situation
479
            }
480
            logger.error("local development cdm instance at port " + devPort + " is not accessible");
481
        }
482
        return null;
483
    }
484

    
485
    public class CdmInstanceInfo {
486

    
487
        private final String name;
488

    
489
        /**
490
         * The full path of the instance including the the prefix (if any).
491
         * E.g. for an EDIT instance this would be something like "cdmserver/remoting"
492
         * For a managed local server this would simply be ""
493
         */
494
        private final String basePath;
495

    
496
        private final ICdmDataSource dataSource;
497

    
498
        public CdmInstanceInfo(String name, String basePath) {
499
            this.name = name;
500
            this.basePath = basePath;
501
            this.dataSource = null;
502
        }
503

    
504
        public CdmInstanceInfo(String name, ICdmDataSource dataSource) {
505
            this.name = name;
506
            this.basePath = "";
507
            this.dataSource = dataSource;
508
        }
509

    
510
        public String getName() {
511
            return name;
512
        }
513

    
514
        public String getBasePath() {
515
            return basePath;
516
        }
517

    
518
        public ICdmDataSource getDataSource() {
519
            return dataSource;
520
        }
521
    }
522
}
(6-6/8)