Project

General

Profile

Download (10.1 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.webapp;
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.log4j.Logger;
29
import org.eclipse.core.runtime.FileLocator;
30
import org.eclipse.core.runtime.Platform;
31
import org.eclipse.jetty.server.Server;
32
import org.eclipse.jetty.util.StringUtil;
33
import org.eclipse.jetty.util.preventers.AppContextLeakPreventer;
34
import org.eclipse.jetty.webapp.WebAppContext;
35
import org.osgi.framework.Bundle;
36

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

    
50
    public static final Logger logger = Logger.getLogger(CdmServer.class);
51

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

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

    
61

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

    
67
    private File warFile;
68
    private Server server;
69

    
70
    private boolean forceSchemaCreate = false;
71
    private boolean forceSchemaUpdate = false;
72

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

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

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

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

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

    
105
        httpPort = findFreePort();
106

    
107
        logger.warn("Starting server on port : " + httpPort);
108

    
109
        server = new Server(httpPort);
110

    
111
        server.addBean(new AppContextLeakPreventer());
112

    
113
        WebAppContext webapp = new WebAppContext();
114
        webapp.setContextPath(contextPath);
115
        webapp.setWar(warFile.getAbsolutePath());
116
        webapp.setThrowUnavailableOnStartupException(true);
117

    
118
        server.setHandler(webapp);
119
    }
120

    
121
    public String getDataSourceName() {
122
        return dataSourceName;
123
    }
124
    public String getHost() {
125
        return host;
126
    }
127

    
128
    public int getPort() {
129
        return httpPort;
130
    }
131

    
132
    public String getContextPath() {
133
        return contextPath;
134
    }
135

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

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

    
160
    public void start(ICDMServerError cdmServerError) throws CdmEmbeddedServerException {
161
        start(true, cdmServerError);
162
    }
163

    
164
    public void start(boolean wait, final ICDMServerError cdmServerError) throws CdmEmbeddedServerException {
165

    
166
        if(server == null) {
167
            throw new CdmEmbeddedServerException("Server is already disposed");
168
        }
169

    
170
        if(server.isStarting()) {
171
            throw new CdmEmbeddedServerException("Server is starting");
172
        }
173

    
174
        if(server.isStarted()) {
175
            throw new CdmEmbeddedServerException("Server has started");
176
        }
177

    
178
        if(server.isRunning()) {
179
            throw new CdmEmbeddedServerException("Server is running");
180
        }
181

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

    
186
        Thread serverThread = new Thread() {
187

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

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

    
221
        serverThread.start();
222

    
223
        if(wait) {
224
            while(!server.isStarted()) {}
225
        }
226
    }
227

    
228
    public boolean isAlive()  {
229
        return server.isRunning() || server.isStarting() || server.isStarted();
230
    }
231

    
232
    public boolean isStarted() {
233
        return server.isStarted();
234
    }
235

    
236
    public boolean isFailed() {
237
        return server.isFailed();
238
    }
239

    
240
    public void stop() throws Exception {
241
        server.stop();
242
        server.destroy();
243
        server = null;
244
    }
245

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

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

    
270
    public boolean isForceSchemaCreate() {
271
        return forceSchemaCreate;
272
    }
273

    
274
    public void setForceSchemaCreate(boolean forceSchemaCreate) {
275
        this.forceSchemaCreate = forceSchemaCreate;
276
    }
277

    
278
    public boolean isForceSchemaUpdate() {
279
        return forceSchemaUpdate;
280
    }
281
    public void setForceSchemaUpdate(boolean forceSchemaUpdate) {
282
        this.forceSchemaUpdate = forceSchemaUpdate;
283
    }
284
}
(2-2/4)