Project

General

Profile

Download (18.9 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.database.CdmPersistentDataSource;
45
import eu.etaxonomy.cdm.database.ICdmDataSource;
46
import eu.etaxonomy.cdm.model.metadata.CdmMetaData;
47
import eu.etaxonomy.taxeditor.remoting.server.CDMServerException;
48
import eu.etaxonomy.taxeditor.remoting.server.CDMServerUtils;
49

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

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

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

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

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

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

    
74
    public final static int NULL_PORT = -1;
75
    public final static String NULL_PORT_STRING = "N/A";
76

    
77
    private static final String CDM_REMOTE_SERVERS_CONFIG_FILE = "cdm_remote_servers.json";
78

    
79

    
80
    private final String name;
81
    private final String server;
82
    private int port;
83
    private final List<CdmInstanceInfo> instances;
84

    
85
    private String cdmlibServicesVersion = "";
86
    private String cdmlibServicesLastModified = "";
87

    
88
    private String prefix = "";
89

    
90
    private boolean ignoreCdmLibVersion = false;
91

    
92

    
93
    public CdmServerInfo(CdmServerInfoConfig parameterObject) {
94
        this.name = parameterObject.getName();
95
        this.server = parameterObject.getServer();
96
        this.port = parameterObject.getPort();
97
        this.prefix = parameterObject.getPrefix();
98
        this.ignoreCdmLibVersion = parameterObject.isIgnoreCdmLibVersion();
99
        instances = new ArrayList<>();
100
    }
101

    
102
    public CdmInstanceInfo addInstance(String name, String basePath) {
103
        String _basePath = basePath;
104
        if(isLocalhostMgd()) {
105
            _basePath = "";
106
        }
107
        CdmInstanceInfo cii = new CdmInstanceInfo(name, _basePath);
108
        instances.add(cii);
109
        return cii;
110
    }
111

    
112
    public boolean isLocalhost() {
113
        return name.startsWith(SERVER_LOCALHOST);
114
    }
115

    
116
    public boolean isLocalhostMgd() {
117
        return NAME_LOCALHOST_MGD.equals(name);
118
    }
119

    
120
    public String getCdmlibServicesVersion() {
121
        return cdmlibServicesVersion;
122
    }
123

    
124
    public String getCdmlibLastModified() {
125
        return cdmlibServicesLastModified;
126
    }
127

    
128
    public void refreshInstances() throws CDMServerException {
129
        instances.clear();
130
        if(isLocalhostMgd()) {
131
            addInstancesFromDataSourcesConfig();
132
        } else {
133
            addInstancesViaHttp();
134
        }
135
        Collections.sort(instances, new Comparator<CdmInstanceInfo>() {
136
            @Override
137
            public int compare(CdmInstanceInfo cii1, CdmInstanceInfo cii2)
138
            {
139
                return cii1.getName().toString().compareTo(cii2.getName().toString());
140
            }
141
        });
142
    }
143

    
144
    public void updateInfo() throws CDMServerException {
145

    
146
        String url = guessProtocol() + "://" + server + ":" + String.valueOf(port) + "/" + prefix + "info.jsp";
147
        String responseBody = getResponse(url);
148
        if(responseBody != null) {
149
            try {
150
                JSONObject info = new JSONObject(responseBody);
151
                cdmlibServicesVersion =  info.getString("cdmlibServicesVersion");
152
                cdmlibServicesLastModified = info.getString("cdmlibServicesLastModified");
153
            } catch (JSONException e) {
154
                throw new CDMServerException(e);
155
            }
156
        }
157
    }
158

    
159
    String guessProtocol() {
160
        return port == 443 ? "https" : "http";
161
    }
162

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

    
190
    private String getResponse(String url) throws CDMServerException {
191

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

    
198
        HttpGet httpGet = new HttpGet(url);
199

    
200
        logger.info("Executing request " + httpGet.getRequestLine());
201

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

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

    
217
        };
218
        String responseBody = null;
219
        try {
220
            responseBody = client.execute(httpGet, responseHandler);
221
        } catch (ClientProtocolException e) {
222
            throw new CDMServerException(e);
223
        } catch (IOException e) { // java.net.ConnectException: Connection refused, java.net.SocketTimeoutException: Read timed out
224
            throw new CDMServerException(e);
225
        } finally{
226
            try {
227
                client.close();
228
                client = null;
229
            } catch (IOException e) {
230
                // IGNORE //
231
            }
232
        }
233
        return responseBody;
234
    }
235

    
236
    public void addInstancesFromDataSourcesConfig() {
237
        for(ICdmDataSource dataSource : CdmPersistentDataSource.getAllDataSources()){
238
            String datasourceNCName = CDMServerUtils.xmlNCNamefrom(dataSource.getName());
239
            logger.info("Adding local instance " + dataSource.getName() + " as " + datasourceNCName);
240
            addInstance(datasourceNCName, datasourceNCName);
241
        }
242
    }
243

    
244
    public String toString(String instanceName, int port) {
245
        return server + ":" + String.valueOf(port) + "/" + instanceName;
246
    }
247

    
248
    public CdmInstanceInfo getInstanceFromName(String instanceName) {
249
        if(instanceName == null) {
250
            return null;
251
        }
252

    
253
        for(CdmInstanceInfo instance : instances) {
254
            if(instance.getName() != null && instance.getName().equals(instanceName)) {
255
                return instance;
256
            }
257
        }
258
        return null;
259
    }
260

    
261
    public CdmRemoteSource getCdmRemoteSource(CdmInstanceInfo instance, int port) {
262
        if(instance != null) {
263
            return CdmRemoteSource.NewInstance(name,
264
                    server,
265
                    port,
266
                    instance.getBasePath()
267
                    );
268
        }
269
        return null;
270
    }
271

    
272
    public boolean pingServer() throws CDMServerException, IOException {
273
        if(isLocalhostMgd()) {
274
            return true;
275
        }
276
        Socket s = new Socket();
277
        s.connect(new InetSocketAddress(server, port), CdmApplicationRemoteConfiguration.HTTP_READ_TIMEOUT_MIN);
278
        s.close();
279
        logger.info("[CDM-Server] Available @ " + server + ":" + port );
280
        updateInfo();
281
        return true;
282
    }
283

    
284
    public boolean pingInstance(CdmInstanceInfo instance, int port) throws CDMServerException  {
285
        ICdmRemoteSource crs = getCdmRemoteSource(instance, port);
286
        try {
287
            if(crs != null && crs.checkConnection()) {
288
                logger.info("[CDM-Server] Running @ " + server + ":" + port + " for instance " + instance);
289
                return true;
290
            }
291
        } catch (CdmSourceException e) {
292
            logger.error(e.getMessage(), e);
293
            throw new CDMServerException(e);
294
        }
295

    
296
        return false;
297
    }
298

    
299
    public int compareDbSchemaVersion(CdmInstanceInfo instance, int port) throws CDMServerException {
300

    
301
        ICdmRemoteSource crs = getCdmRemoteSource(instance, port);
302
        String dbSchemaVersion;
303
        try {
304
            dbSchemaVersion = crs.getDbSchemaVersion();
305
        } catch (CdmSourceException e) {
306
            throw new CDMServerException(e);
307
        }
308

    
309

    
310
        if(dbSchemaVersion != null) {
311
            return CdmMetaData.compareVersion(dbSchemaVersion, CdmMetaData.getDbSchemaVersion(), 3, null);
312
        } else {
313
            throw new CDMServerException("Cannot determine editor db. schema version");
314
        }
315
    }
316

    
317
    public int compareCdmlibServicesVersion() throws CdmSourceException {
318

    
319
        String serverVersion = cdmlibServicesVersion;
320
        String serverCdmlibLastModified = cdmlibServicesLastModified;
321
        if(ignoreCdmLibVersion) {
322
            return 0;
323
        } else {
324
            return compareCdmlibServicesVersion(serverVersion, serverCdmlibLastModified);
325
        }
326
    }
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
    private static List<CdmServerInfoConfig>  loadFromConfigFile(File file) {
394

    
395
        List<CdmServerInfoConfig> serverList = null;
396

    
397
        ObjectMapper mapper = new ObjectMapper();
398

    
399
        if(!file.exists()) {
400
            logger.info("The remote server config file '" + file.getAbsolutePath() +
401
                    "' does not yet exist, it will be created based on the defaults.");
402
             try {
403
                serverList = createDefaultServerConfigList();
404
                mapper.writerWithDefaultPrettyPrinter().writeValue(new FileOutputStream(file), serverList);
405

    
406
            } catch (IOException e) {
407
                throw new RuntimeException(e);
408
            }
409
        } else {
410
            try {
411
                serverList = mapper.readValue(new FileInputStream(file), new TypeReference<List<CdmServerInfoConfig>>(){});
412
            } catch (IOException e) {
413
               throw new RuntimeException(e);
414
            }
415
        }
416

    
417
        return serverList;
418

    
419

    
420
    }
421

    
422
    private static List<CdmServerInfoConfig> createDefaultServerConfigList() {
423

    
424
        List<CdmServerInfoConfig> serverInfoList = new ArrayList<>();
425
        serverInfoList.add(new CdmServerInfoConfig(NAME_PRODUCTION, SERVER_PRODUCTION, 80, "", false));
426
        serverInfoList.add(new CdmServerInfoConfig(NAME_DEMO_1, SERVER_DEMO_1, 80, CDMSERVER_PREFIX, false));
427
        serverInfoList.add(new CdmServerInfoConfig(NAME_LOCALHOST, SERVER_LOCALHOST, 8080, CDMSERVER_PREFIX, true));
428
        return serverInfoList;
429
    }
430

    
431
    public String getName() {
432
        return name;
433
    }
434

    
435
    public String getServer() {
436
        return server;
437
    }
438

    
439
    public int getPort() {
440
        return port;
441
    }
442

    
443
    public void setPort(int port) {
444
        this.port = port;
445
    }
446

    
447
    public List<CdmInstanceInfo> getInstances() throws CDMServerException {
448
        if(instances.isEmpty()) {
449
            refreshInstances();
450
        }
451
        return instances;
452
    }
453

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

    
480
    public class CdmInstanceInfo {
481
        private final String name;
482

    
483
        /**
484
         * The full path of the instance including the the prefix (if any).
485
         * E.g. for an EDIT instance this would be something like "cdmserver/remoting"
486
         * For a managed local server this would simply be ""
487
         */
488
        private final String basePath;
489

    
490
        public CdmInstanceInfo(String name, String basePath) {
491
            this.name = name;
492
            this.basePath = basePath;
493
        }
494

    
495
        public String getName() {
496
            return name;
497
        }
498

    
499
        public String getBasePath() {
500
            return basePath;
501
        }
502
    }
503
}
(5-5/7)