Project

General

Profile

Download (10.2 KB) Statistics
| Branch: | Tag: | Revision:
1
/**
2
 * Copyright (C) 2014 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.local;
10

    
11
import java.io.File;
12
import java.io.IOException;
13
import java.net.ServerSocket;
14
import java.net.URI;
15
import java.net.URISyntaxException;
16
import java.net.URL;
17

    
18
import javax.management.InstanceNotFoundException;
19
import javax.management.MBeanException;
20
import javax.management.MBeanServerConnection;
21
import javax.management.MalformedObjectNameException;
22
import javax.management.ObjectName;
23
import javax.management.ReflectionException;
24
import javax.management.remote.JMXConnector;
25
import javax.management.remote.JMXConnectorFactory;
26
import javax.management.remote.JMXServiceURL;
27

    
28
import org.apache.commons.lang3.StringUtils;
29
import org.apache.log4j.Logger;
30
import org.eclipse.core.runtime.FileLocator;
31
import org.eclipse.core.runtime.Platform;
32
import org.eclipse.jetty.server.Server;
33
import org.eclipse.jetty.util.preventers.AppContextLeakPreventer;
34
import org.eclipse.jetty.webapp.WebAppContext;
35
import org.osgi.framework.Bundle;
36

    
37
import eu.etaxonomy.taxeditor.workbench.datasource.ICdmServerError;
38

    
39
/**
40
 * (Singleton) Server instance which manages a compatible cdmlib-webapp-war.
41
 * This is done by launching a jetty instance (using jetty-runner) as an
42
 * executed process. The choice of the external process over a more
43
 * preferable 'embedded jetty' instance is due to problems arising from the
44
 * class loading of classes (e.g. from hibernate core) which are needed
45
 * for both the webapp as well as the remoting client.
46
 *
47
 * @author cmathew
48
 * @date 23 Sep 2014
49
 */
50
public class CdmServer {
51

    
52
    public static final Logger logger = Logger.getLogger(CdmServer.class);
53

    
54
    //TODO use the constants defined in eu.etaxonomy.cdm.opt.config.DataSourceConfigurer
55
    private static final String ATTRIBUTE_FORCE_SCHEMA_CREATE = "cdm.forceSchemaCreate";
56
    private static final String ATTRIBUTE_FORCE_SCHEMA_UPDATE = "cdm.forceSchemaUpdate";
57
    private static final String ATTRIBUTE_DATASOURCE_NAME = "cdm.datasource";
58
    private static final String CDM_BEAN_DEFINITION_FILE = "cdm.beanDefinitionFile";
59

    
60
    //see eu.etaxonomy.cdm.server.Bootloader.SPRING_PROFILES_ACTIVE
61
    private static final String SPRING_PROFILES_ACTIVE = "spring.profiles.active";
62

    
63

    
64
    private final String dataSourceName;
65
    private final String host = "127.0.0.1";
66
    private int httpPort = 9090;
67
    private final String contextPath = "/";
68

    
69
    private File warFile;
70
    private Server server;
71

    
72
    private boolean forceSchemaCreate = false;
73
    private boolean forceSchemaUpdate = false;
74

    
75
    public CdmServer(String dataSourceName, File dataSourcesFile) throws CdmEmbeddedServerException {
76
        if(StringUtils.isBlank(dataSourceName)) {
77
            throw new CdmEmbeddedServerException("DataSource name is not valid");
78
        }
79

    
80
        if(dataSourcesFile == null || !dataSourcesFile.exists()) {
81
            throw new CdmEmbeddedServerException("DataSource config file does not exist");
82
        }
83
        this.dataSourceName = dataSourceName;
84
        Bundle bundle = Platform.getBundle("eu.etaxonomy.taxeditor.local");
85
        URL warURL = bundle.getEntry("lib/cdmlib-remote-webapp.war");
86

    
87
        try {
88
            URL resolvedWarURL = FileLocator.toFileURL(warURL);
89
            // We need to use the 3-arg constructor of URI in order to properly escape file system chars
90
            URI resolvedURI = new URI(resolvedWarURL.getProtocol(), resolvedWarURL.getPath(), null);
91
            warFile = new File(resolvedURI);
92
//            System.out.println("war url : " + warFile.getAbsolutePath());
93

    
94
            if(warFile == null || !warFile.exists()) {
95
                throw new CdmEmbeddedServerException("Cdmlib War file does not exist");
96
            }
97
        } catch (URISyntaxException use) {
98
            throw new CdmEmbeddedServerException(use);
99
        } catch (IOException ioe) {
100
            throw new CdmEmbeddedServerException(ioe);
101
        }
102

    
103
        System.setProperty(SPRING_PROFILES_ACTIVE, "remoting");
104
        System.setProperty(CDM_BEAN_DEFINITION_FILE, dataSourcesFile.getAbsolutePath());
105
        System.setProperty(ATTRIBUTE_DATASOURCE_NAME, dataSourceName);
106

    
107
        httpPort = findFreePort();
108

    
109
        logger.warn("Starting server on port : " + httpPort);
110

    
111
        server = new Server(httpPort);
112

    
113
        server.addBean(new AppContextLeakPreventer());
114

    
115
        WebAppContext webapp = new WebAppContext();
116

    
117
        webapp.setContextPath(contextPath);
118
        webapp.setWar(warFile.getAbsolutePath());
119
        webapp.setThrowUnavailableOnStartupException(true);
120

    
121
        server.setHandler(webapp);
122
    }
123

    
124
    public String getDataSourceName() {
125
        return dataSourceName;
126
    }
127
    public String getHost() {
128
        return host;
129
    }
130

    
131
    public int getPort() {
132
        return httpPort;
133
    }
134

    
135
    public String getContextPath() {
136
        return contextPath;
137
    }
138

    
139
    private static int findFreePort() throws CdmEmbeddedServerException {
140
        ServerSocket socket = null;
141
        try {
142
            socket = new ServerSocket(0);
143
            socket.setReuseAddress(true);
144
            int port = socket.getLocalPort();
145
            try {
146
                socket.close();
147
            } catch (IOException e) {
148

    
149
            }
150
            return port;
151
        } catch (IOException e) {
152
        } finally {
153
            if (socket != null) {
154
                try {
155
                    socket.close();
156
                } catch (IOException e) {
157
                }
158
            }
159
        }
160
        throw new CdmEmbeddedServerException("Could not find a free TCP/IP port to start embedded Jetty HTTP Server on");
161
    }
162

    
163
    public void start(ICdmServerError cdmServerError) throws CdmEmbeddedServerException {
164
        start(true, cdmServerError);
165
    }
166

    
167
    public void start(boolean wait, final ICdmServerError remotingLoginDialogLocal) throws CdmEmbeddedServerException {
168

    
169
        if(server == null) {
170
            throw new CdmEmbeddedServerException("Server is already disposed");
171
        }
172

    
173
        if(server.isStarting()) {
174
            throw new CdmEmbeddedServerException("Server is starting");
175
        }
176

    
177
        if(server.isStarted()) {
178
            throw new CdmEmbeddedServerException("Server has started");
179
        }
180

    
181
        if(server.isRunning()) {
182
            throw new CdmEmbeddedServerException("Server is running");
183
        }
184

    
185
        if(server.isStopping()) {
186
            throw new CdmEmbeddedServerException("Server is currently stopping. Please re-try in about 10 seconds");
187
        }
188

    
189
        Thread serverThread = new Thread() {
190

    
191
            @Override
192
            public void run() {
193
                try {
194
                    if(isForceSchemaCreate()) {
195
                        System.setProperty(ATTRIBUTE_FORCE_SCHEMA_CREATE, "true");
196
                    }else if (isForceSchemaUpdate()){
197
                        System.setProperty(ATTRIBUTE_FORCE_SCHEMA_UPDATE, "true");
198
                    }
199
                    server.start();
200
                    server.join();
201
                } catch (Throwable t) {
202
                    //wait for 1sec to get the right order of login dialog and error
203
                    //message when connection fails
204
                    try {
205
                        Thread.sleep(1000);
206
                    } catch (InterruptedException e) {
207
                    }
208
                    Throwable cause = t;
209
                    while(cause != null) {
210

    
211
                        // pure string comparison to avoid dependencies to hibernate or cdmlib-persistance in the taxeditor
212
                        if(cause.getClass().getSimpleName().equals("SchemaExtractionException") || cause.getClass().getSimpleName().equals("CdmDatabaseException") ) {
213
                            logger.debug(cause.getClass().getName() + " detected which indicates missing or corrupt schema");
214
                            cause = null;
215
                        } else {
216
                            cause = cause.getCause();
217
                        }
218
                    }
219
                    remotingLoginDialogLocal.handleError(new RuntimeException("Error during CDM server startup", t));
220
                }
221
            }
222
        };
223

    
224
        serverThread.start();
225

    
226
        if(wait) {
227
            while(!server.isStarted()) {}
228
        }
229
    }
230

    
231
    public boolean isAlive()  {
232
        return server.isRunning() || server.isStarting() || server.isStarted();
233
    }
234

    
235
    public boolean isStarted() {
236
        return server.isStarted();
237
    }
238

    
239
    public boolean isFailed() {
240
        return server.isFailed();
241
    }
242

    
243
    public void stop() throws Exception {
244
        server.stop();
245
        server.destroy();
246
        server = null;
247
    }
248

    
249
    public static void stopServerViaJMX(int jmxPort) throws CdmEmbeddedServerException  {
250
        String JMX_URL = "service:jmx:rmi:///jndi/rmi://localhost:" + jmxPort + "/jmxrmi";
251
        logger.warn("Shutting down Jetty instance ... ");
252

    
253
        try {
254
            JMXConnector connector = JMXConnectorFactory.connect(new JMXServiceURL(JMX_URL), null);
255
            connector.connect(null);
256
            MBeanServerConnection connection = connector.getMBeanServerConnection();
257
            ObjectName objectName = new ObjectName("org.eclipse.jetty.server:type=server,id=0");
258
            connection.invoke(objectName, "stop", null, null);
259
            logger.warn("Shutdown command sent");
260
        } catch (InstanceNotFoundException e) {
261
            throw new CdmEmbeddedServerException(e);
262
        } catch (MBeanException e) {
263
            throw new CdmEmbeddedServerException(e);
264
        } catch (ReflectionException e) {
265
            throw new CdmEmbeddedServerException(e);
266
        } catch (IOException e) {
267
            throw new CdmEmbeddedServerException(e);
268
        } catch (MalformedObjectNameException e) {
269
            throw new CdmEmbeddedServerException(e);
270
        }
271
    }
272

    
273
    public boolean isForceSchemaCreate() {
274
        return forceSchemaCreate;
275
    }
276

    
277
    public void setForceSchemaCreate(boolean forceSchemaCreate) {
278
        this.forceSchemaCreate = forceSchemaCreate;
279
    }
280

    
281
    public boolean isForceSchemaUpdate() {
282
        return forceSchemaUpdate;
283
    }
284
    public void setForceSchemaUpdate(boolean forceSchemaUpdate) {
285
        this.forceSchemaUpdate = forceSchemaUpdate;
286
    }
287
}
(2-2/3)