Project

General

Profile

Download (20.2 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.lang3.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.logging.log4j.LogManager;import org.apache.logging.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.common.CdmUtils;
43
import eu.etaxonomy.cdm.config.CdmSourceException;
44
import eu.etaxonomy.cdm.config.ConfigFileUtil;
45
import eu.etaxonomy.cdm.config.ICdmSource;
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
public class CdmServerInfo {
57

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

    
60
    protected 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
    private final static String NAME_DEMO_2 = "demo II";
67
    private final static String SERVER_DEMO_2 = "160.45.63.231";
68

    
69

    
70
    public final static String SERVER_LOCALHOST = "localhost";
71
    private final static String NAME_LOCALHOST = "localhost";
72
    public final static String NAME_LOCALHOST_MGD = "localhost mgd.";
73

    
74
    private final static String NAME_LOCALHOST_DEV = "localhost-dev";
75
    private final static String NAME_INSTANCE_LOCALHOST_DEV = "local-dev";
76
    private final static String SERVER_LOCALHOST_DEV = "localhost";
77
    private final static String BASEPATH_LOCALHOST_DEV = "";
78

    
79
    public final static int NULL_PORT = -1;
80
    public final static String NULL_PORT_STRING = "N/A";
81

    
82
    protected static final String CDM_REMOTE_SERVERS_CONFIG_FILE = "cdm_remote_servers.json";
83

    
84

    
85
    private final String name;
86
    private final String server;
87
    private int port;
88
    protected final List<CdmInstanceInfo> instances;
89

    
90
    private String cdmlibServicesVersion = "";
91
    private String cdmlibServicesLastModified = "";
92

    
93
    private String prefix = "";
94

    
95
    private boolean ignoreCdmLibVersion = false;
96

    
97

    
98
    public CdmServerInfo(CdmServerInfoConfig parameterObject) {
99
        this.name = parameterObject.getName();
100
        this.server = parameterObject.getServer();
101
        this.port = parameterObject.getPort();
102
        this.prefix = parameterObject.getPrefix();
103
        this.ignoreCdmLibVersion = parameterObject.isIgnoreCdmLibVersion();
104
        this.instances = new ArrayList<>();
105
    }
106

    
107
    public CdmInstanceInfo addInstance(String name, ICdmDataSource dataSource) {
108
        CdmInstanceInfo cii = new CdmInstanceInfo(name, dataSource);
109
        instances.add(cii);
110
        return cii;
111
    }
112

    
113
    public CdmInstanceInfo addInstance(String name, String basePath) {
114
        CdmInstanceInfo cii = new CdmInstanceInfo(name, basePath);
115
        instances.add(cii);
116
        return cii;
117
    }
118

    
119
    public boolean isLocalhost() {
120
        return name.startsWith(SERVER_LOCALHOST);
121
    }
122

    
123
    public boolean isLocalhostMgd() {
124
        return NAME_LOCALHOST_MGD.equals(name);
125
    }
126

    
127
    public String getCdmlibServicesVersion() {
128
        return cdmlibServicesVersion;
129
    }
130

    
131
    public String getCdmlibLastModified() {
132
        return cdmlibServicesLastModified;
133
    }
134

    
135
    public void refreshInstances() throws CdmServerException {
136
        instances.clear();
137
        if(isLocalhostMgd()) {
138
            addInstancesFromDataSourcesConfig();
139
        } else {
140
            addInstancesViaHttp();
141
        }
142

    
143

    
144
        Collections.sort(instances, new Comparator<CdmInstanceInfo>() {
145
            @Override
146
            public int compare(CdmInstanceInfo cii1, CdmInstanceInfo cii2){
147
                return cii1.getName().toString().compareTo(cii2.getName().toString());
148
            }
149
        });
150
    }
151

    
152
    public void updateInfo() throws CdmServerException {
153

    
154
        String url = guessProtocol() + "://" + server + ":" + String.valueOf(port) + "/" + prefix + "info.jsp";
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
    String guessProtocol() {
168
        return port == 443 ? "https" : "http";
169
    }
170

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

    
198
    private String getResponse(String url) throws CdmServerException {
199

    
200
        RequestConfig.Builder requestBuilder = RequestConfig.custom();
201
        requestBuilder.setConnectTimeout(CdmApplicationRemoteConfiguration.HTTP_READ_TIMEOUT_MIN);
202
        requestBuilder.setConnectionRequestTimeout(CdmApplicationRemoteConfiguration.HTTP_READ_TIMEOUT_MIN);
203
        requestBuilder.setSocketTimeout(CdmApplicationRemoteConfiguration.HTTP_READ_TIMEOUT_MIN);
204
        CloseableHttpClient client = HttpClientBuilder.create().setDefaultRequestConfig(requestBuilder.build()).build();
205

    
206
        HttpGet httpGet = new HttpGet(url);
207

    
208
        logger.info("Executing request " + httpGet.getRequestLine());
209

    
210
        // Create a custom response handler
211
        ResponseHandler<String> responseHandler = new ResponseHandler<String>() {
212

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

    
225
        };
226
        String responseBody = null;
227
        try {
228
            responseBody = client.execute(httpGet, responseHandler);
229
        } catch (ClientProtocolException e) {
230
            throw new CdmServerException(e);
231
        } catch (IOException e) { // java.net.ConnectException: Connection refused, java.net.SocketTimeoutException: Read timed out
232
            throw new CdmServerException(e);
233
        } finally{
234
            try {
235
                client.close();
236
                client = null;
237
            } catch (IOException e) {
238
                // IGNORE //
239
            }
240
        }
241
        return responseBody;
242
    }
243

    
244
    public void addInstancesFromDataSourcesConfig() {
245
        for(ICdmDataSource dataSource : CdmPersistentDataSource.getAllDataSources()){
246
            String datasourceNCName = CdmServerUtils.xmlNCNamefrom(dataSource.getName());
247
            logger.info("Adding local instance " + dataSource.getName() + " as " + datasourceNCName);
248
            addInstance(datasourceNCName, dataSource);
249
        }
250
    }
251

    
252
    public String toString(String instanceName, int port) {
253
        return server + ":" + String.valueOf(port) + "/" + instanceName;
254
    }
255

    
256
    public CdmInstanceInfo getInstanceFromName(String instanceName) {
257
        if(instanceName == null) {
258
            return null;
259
        }
260

    
261
        for(CdmInstanceInfo instance : instances) {
262
            if(instance.getName() != null && instance.getName().equals(instanceName)) {
263
                return instance;
264
            }
265
        }
266
        return null;
267
    }
268

    
269
    public ICdmRemoteSource getCdmRemoteSource(CdmInstanceInfo instance, int port) {
270
        if(instance != null) {
271
            if (instance.getDataSource()!=null){
272
                return CdmRemoteLocalhostSource.NewInstance(name,
273
                        server,
274
                        port,
275
                        instance.getDataSource(),
276
                        instance.name
277
                        );
278
            }else{
279
                return CdmRemoteSource.NewInstance(name,
280
                        server,
281
                        port,
282
                        instance.getBasePath()
283
                        );
284
            }
285
        }
286
        return null;
287
    }
288

    
289
    public boolean pingServer() throws CdmServerException, IOException {
290
        if(isLocalhostMgd()) {
291
            return true;
292
        }
293
        Socket s = new Socket();
294
        s.connect(new InetSocketAddress(server, port), CdmApplicationRemoteConfiguration.HTTP_READ_TIMEOUT_MIN);
295
        s.close();
296
        logger.info("[CDM-Server] Available @ " + server + ":" + port );
297
        updateInfo();
298
        return true;
299
    }
300

    
301
    public boolean pingInstance(CdmInstanceInfo instance, int port) throws CdmServerException  {
302
        ICdmSource cdmRemoteSource = getCdmRemoteSource(instance, port);
303
        try {
304
            if(cdmRemoteSource != null && cdmRemoteSource.checkConnection()) {
305
                logger.info("[CDM-Server] Running @ " + server + ":" + port + " for instance " + instance);
306
                return true;
307
            }
308
        } catch (CdmSourceException e) {
309
            logger.error(e.getMessage(), e);
310
            throw new CdmServerException(e);
311
        }
312

    
313
        return false;
314
    }
315

    
316
    public int compareDbSchemaVersion(CdmInstanceInfo instance, int port) throws CdmServerException {
317

    
318
        ICdmSource remoteSource = getCdmRemoteSource(instance, port);
319
        String remoteDbSchemaVersion;
320
        try {
321
            remoteDbSchemaVersion = remoteSource.getDbSchemaVersion();
322
        } catch (CdmSourceException e) {
323
            throw new CdmServerException(e);
324
        }
325

    
326
        if(remoteDbSchemaVersion != null) {
327
            return CdmMetaData.compareVersion(remoteDbSchemaVersion, CdmMetaData.getDbSchemaVersion(), 3, null);
328
        } else {
329
            throw new CdmServerException("Cannot determine editor db schema version");
330
        }
331
    }
332

    
333
    public int compareCdmlibServicesVersion() throws CdmSourceException {
334

    
335
        String serverVersion = cdmlibServicesVersion;
336
        String serverCdmlibLastModified = cdmlibServicesLastModified;
337
        if(ignoreCdmLibVersion) {
338
            return 0;
339
        } else {
340
            return compareCdmlibServicesVersion(serverVersion, serverCdmlibLastModified);
341
        }
342
    }
343

    
344
    public static int compareCdmlibServicesVersion(String serverVersion, String serverCdmlibLastModified) throws CdmSourceException {
345

    
346
        String editorVersion = CdmApplicationState.getCdmlibVersion();
347
        String editorCdmlibLastModified = CdmApplicationState.getCdmlibLastModified();
348

    
349
        int result = 0;
350
        if(StringUtils.isBlank(serverVersion) || StringUtils.isBlank(editorVersion)) {
351
            throw new CdmSourceException("cdmlib-services server or editor version is empty");
352
        }
353

    
354
        String[] serverVersionSplit = serverVersion.split("\\.");
355
        String[] editorVersionSplit = editorVersion.split("\\.");
356

    
357
        if(serverVersionSplit.length < 3 || editorVersionSplit.length < 3 || serverVersionSplit.length > 4 || editorVersionSplit.length > 4) {
358
            throw new CdmSourceException("cdmlib-services server or editor version is invalid");
359
        }
360

    
361
        Integer serverVersionPart;
362
        Integer editorVersionPart;
363

    
364
        for(int i=0 ; i<3 ; i++) {
365
            serverVersionPart = Integer.valueOf(serverVersionSplit[i]);
366
            editorVersionPart = Integer.valueOf(editorVersionSplit[i]);
367

    
368
            int partCompare = serverVersionPart.compareTo(editorVersionPart);
369
            if (partCompare != 0){
370
                return partCompare;
371
            }
372
        }
373
        // at this point major, minor and patch versions are matching
374

    
375
        if(StringUtils.isBlank(serverCdmlibLastModified) || StringUtils.isBlank(editorCdmlibLastModified)) {
376
            throw new CdmSourceException("cdmlib-services server or editor version is empty");
377
        }
378

    
379
        String cdmServerIgnoreVersion = System.getProperty("cdm.server.version.lm.ignore");
380
        if(StringUtils.isBlank(cdmServerIgnoreVersion) || !cdmServerIgnoreVersion.equals("true")) {
381
            Long serverLastModified = Long.valueOf(serverCdmlibLastModified);
382
            Long editorLastModified = Long.valueOf(editorCdmlibLastModified);
383
            return serverLastModified.compareTo(editorLastModified);
384
        }
385

    
386
        return result;
387
    }
388

    
389
    public static List<CdmServerInfo> getCdmServers(boolean isLocal) {
390
        List<CdmServerInfoConfig> configList;
391
        File file = new File(ConfigFileUtil.perUserCdmFolderFallback(), CDM_REMOTE_SERVERS_CONFIG_FILE);
392
        if (file.exists()){
393
            configList = loadFromConfigFile(file);
394
        }else{
395
            configList = loadFromConfigFile(new File(ConfigFileUtil.perUserCdmFolderFallback(), CDM_REMOTE_SERVERS_CONFIG_FILE));
396
        }
397
        List<CdmServerInfo> serverInfoList = new ArrayList<>(configList.size());
398
        for(CdmServerInfoConfig config : configList) {
399
            serverInfoList.add(new CdmServerInfo(config));
400
        }
401
        // The local host managed server must not be in the config file, this should be moved and only added when plugin is installed
402
        if (isLocal){
403
            CdmServerInfoConfig localHostManagedConfig = new CdmServerInfoConfig(NAME_LOCALHOST_MGD, SERVER_LOCALHOST, NULL_PORT, CDMSERVER_PREFIX, false);
404
            serverInfoList.add(new CdmServerInfo(localHostManagedConfig));
405
        }
406

    
407
        return serverInfoList;
408
    }
409

    
410
    protected static List<CdmServerInfoConfig>  loadFromConfigFile(File file) {
411

    
412
        List<CdmServerInfoConfig> serverList = null;
413

    
414
        ObjectMapper mapper = new ObjectMapper();
415

    
416
        if(!file.exists()) {
417
            logger.info("The remote server config file '" + file.getAbsolutePath() +
418
                    "' does not yet exist, it will be created based on the defaults.");
419
             try {
420
                serverList = createDefaultServerConfigList();
421
                mapper.writerWithDefaultPrettyPrinter().writeValue(new FileOutputStream(file), serverList);
422

    
423
            } catch (IOException e) {
424
                throw new RuntimeException(e);
425
            }
426
        } else {
427
            try {
428
                serverList = mapper.readValue(new FileInputStream(file), new TypeReference<List<CdmServerInfoConfig>>(){});
429
            } catch (IOException e) {
430
               throw new RuntimeException(e);
431
            }
432
        }
433

    
434
        return serverList;
435
    }
436

    
437
    private static List<CdmServerInfoConfig> createDefaultServerConfigList() {
438

    
439
        List<CdmServerInfoConfig> serverInfoList = new ArrayList<>();
440
        serverInfoList.add(new CdmServerInfoConfig(NAME_PRODUCTION, SERVER_PRODUCTION, 443, "", false));
441
        serverInfoList.add(new CdmServerInfoConfig(NAME_DEMO_1, SERVER_DEMO_1, 80, CDMSERVER_PREFIX, false));
442
        serverInfoList.add(new CdmServerInfoConfig(NAME_DEMO_2, SERVER_DEMO_2, 80, CDMSERVER_PREFIX, false));
443
        serverInfoList.add(new CdmServerInfoConfig(NAME_LOCALHOST, SERVER_LOCALHOST, 8080, CDMSERVER_PREFIX, true));
444
        return serverInfoList;
445
    }
446

    
447
    public String getName() {
448
        return name;
449
    }
450

    
451
    public String getServer() {
452
        return server;
453
    }
454

    
455
    public int getPort() {
456
        return port;
457
    }
458

    
459
    public void setPort(int port) {
460
        this.port = port;
461
    }
462

    
463
    public List<CdmInstanceInfo> getInstances() throws CdmServerException {
464
        if(instances.isEmpty()) {
465
            refreshInstances();
466
        }
467
        return instances;
468
    }
469

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

    
496
    public class CdmInstanceInfo {
497

    
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
        private final ICdmDataSource dataSource;
508

    
509
        public CdmInstanceInfo(String name, String basePath) {
510
            this.name = name;
511
            this.basePath = basePath;
512
            this.dataSource = null;
513
        }
514

    
515
        public CdmInstanceInfo(String name, ICdmDataSource dataSource) {
516
            this.name = name;
517
            this.basePath = "";
518
            this.dataSource = dataSource;
519
        }
520

    
521
        public String getName() {
522
            return name;
523
        }
524

    
525
        public String getBasePath() {
526
            return basePath;
527
        }
528

    
529
        public ICdmDataSource getDataSource() {
530
            return dataSource;
531
        }
532
    }
533
}
(6-6/8)