Project

General

Profile

« Previous | Next » 

Revision 24841ac2

Added by Andreas Kohlbecker almost 11 years ago

start, stop of instances and reload of configuration implemented, see #3471 (cdmlib-remote-webapp instances can be stopped / started individually)

View differences:

src/main/java/eu/etaxonomy/cdm/server/Bootloader.java
28 28
import java.io.InputStreamReader;
29 29
import java.io.OutputStream;
30 30
import java.lang.management.ManagementFactory;
31
import java.lang.reflect.InvocationTargetException;
32 31
import java.net.URL;
33
import java.sql.Connection;
34
import java.sql.SQLException;
35 32
import java.util.List;
36 33
import java.util.Properties;
37 34

  
38
import javax.naming.NamingException;
39
import javax.sql.DataSource;
40

  
41 35
import org.apache.commons.cli.CommandLine;
42 36
import org.apache.commons.cli.CommandLineParser;
43 37
import org.apache.commons.cli.GnuParser;
......
49 43
import org.apache.log4j.RollingFileAppender;
50 44
import org.eclipse.jetty.jmx.MBeanContainer;
51 45
import org.eclipse.jetty.security.HashLoginService;
46
import org.eclipse.jetty.server.Handler;
52 47
import org.eclipse.jetty.server.Server;
48
import org.eclipse.jetty.server.handler.ContextHandler;
53 49
import org.eclipse.jetty.server.handler.ContextHandlerCollection;
54 50
import org.eclipse.jetty.util.log.Log;
55 51
import org.eclipse.jetty.webapp.WebAppClassLoader;
......
58 54
import eu.etaxonomy.cdm.server.instance.CdmInstance;
59 55
import eu.etaxonomy.cdm.server.instance.Configuration;
60 56
import eu.etaxonomy.cdm.server.instance.InstanceManager;
57
import eu.etaxonomy.cdm.server.instance.SharedAttributes;
61 58
import eu.etaxonomy.cdm.server.instance.Status;
62 59
import eu.etaxonomy.cdm.server.win32service.Win32Service;
63 60

  
......
108 105
    private static final File DEFAULT_WEBAPP_TEMP_FOLDER = new File(TMP_PATH + DEFAULT_WEBAPP_WAR_NAME);
109 106
    private static final File CDM_WEBAPP_TEMP_FOLDER = new File(TMP_PATH + CDMLIB_REMOTE_WEBAPP);
110 107

  
111
    private static final String ATTRIBUTE_JDBC_JNDI_NAME = "cdm.jdbcJndiName";
112
    public static final String ATTRIBUTE_DATASOURCE_NAME = "cdm.datasource";
113
    private static final String ATTRIBUTE_CDM_LOGFILE = "cdm.logfile";
114
    /**
115
     * same as in eu.etaxonomy.cdm.remote.config.DataSourceConfigurer
116
     */
117
    public static final String ATTRIBUTE_ERROR_MESSAGES = "cdm.errorMessages";
118

  
119

  
120 108
    private final InstanceManager instanceManager = new InstanceManager(new File(USERHOME_CDM_LIBRARY_PATH, DATASOURCE_BEANDEF_FILE));
121 109

  
122 110
    public List<CdmInstance> getCdmInstances() {
......
124 112
    }
125 113

  
126 114
    public InstanceManager getInstanceManager(){
127
    	return instanceManager;
115
        return instanceManager;
128 116
    }
129 117

  
130 118
    private File cdmRemoteWebAppFile = null;
......
166 154
        return answer;
167 155
    }
168 156

  
169
    private boolean bindJndiDataSource(CdmInstance instance) {
170
        try {
171
        	Configuration conf = instance.getConfiguration();
172
            Class<DataSource> dsCass = (Class<DataSource>) Thread.currentThread().getContextClassLoader().loadClass("com.mchange.v2.c3p0.ComboPooledDataSource");
173
            DataSource datasource = dsCass.newInstance();
174
            dsCass.getMethod("setDriverClass", new Class[] {String.class}).invoke(datasource, new Object[] {conf.getDriverClass()});
175
            dsCass.getMethod("setJdbcUrl", new Class[] {String.class}).invoke(datasource, new Object[] {conf.getDataSourceUrl()});
176
            dsCass.getMethod("setUser", new Class[] {String.class}).invoke(datasource, new Object[] {conf.getUsername()});
177
            dsCass.getMethod("setPassword", new Class[] {String.class}).invoke(datasource, new Object[] {conf.getPassword()});
178

  
179
            Connection connection = null;
180
            String sqlerror = null;
181
            try {
182
                connection = datasource.getConnection();
183
                connection.close();
184
            } catch (SQLException e) {
185
                sqlerror = e.getMessage() + "["+ e.getSQLState() + "]";
186
                instance.getProblems().add(sqlerror);
187
                if(connection !=  null){
188
                    try {connection.close();} catch (SQLException e1) { /* IGNORE */ }
189
                }
190
                logger.error(conf.toString() + " has problem : "+ sqlerror );
191
            }
192 157

  
193
            if(!instance.hasProblems()){
194
                logger.info("binding jndi datasource at " + conf.getJdbcJndiName() + " with "+conf.getUsername() +"@"+ conf.getDataSourceUrl());
195
                org.eclipse.jetty.plus.jndi.Resource jdbcResource = new org.eclipse.jetty.plus.jndi.Resource(conf.getJdbcJndiName(), datasource);
196
                return true;
197
            }
198

  
199
        } catch (IllegalArgumentException e) {
200
            logger.error(e);
201
            e.printStackTrace();
202
        } catch (SecurityException e) {
203
            logger.error(e);
204
        } catch (ClassNotFoundException e) {
205
            logger.error(e);
206
        } catch (InstantiationException e) {
207
            logger.error(e);
208
        } catch (IllegalAccessException e) {
209
            logger.error(e);
210
        } catch (InvocationTargetException e) {
211
            logger.error(e);
212
        } catch (NoSuchMethodException e) {
213
            logger.error(e);
214
        } catch (NamingException e) {
215
            logger.error(e);
216
        }
217
        return false;
218
    }
219 158

  
220 159
    public void parseCommandOptions(String[] args) throws ParseException {
221 160
        CommandLineParser parser = new GnuParser();
......
438 377
    }
439 378

  
440 379
    public String readCdmRemoteVersion() throws IOException {
441
    	String version = "cdmlib version unreadable";
380
        String version = "cdmlib version unreadable";
442 381
        InputStream versionInStream = Bootloader.class.getClassLoader().getResourceAsStream(VERSION_PROPERTIES_FILE);
443 382
        if (versionInStream != null){
444
        	Properties versionProperties = new Properties();
445
        	versionProperties.load(versionInStream);
446
        	version = versionProperties.getProperty(CDMLIB_REMOTE_WEBAPP_VERSION, version);
383
            Properties versionProperties = new Properties();
384
            versionProperties.load(versionInStream);
385
            version = versionProperties.getProperty(CDMLIB_REMOTE_WEBAPP_VERSION, version);
447 386
        }
448 387
        return version;
449 388
    }
......
495 434
    }
496 435

  
497 436

  
498
	/**
499
	 * @param conf
500
	 * @param austostart
501
	 * @return
502
	 * @throws IOException
503
	 */
504
	public WebAppContext addCdmInstanceContext(CdmInstance instance) throws IOException {
505
		Configuration conf = instance.getConfiguration();
506
		if(!instance.isEnabled()){
507
		    logger.info(conf.getInstanceName() + " is disabled due to JVM memory limitations => skipping");
508
		    return null;
509
		}
510
		instance.setStatus(Status.initializing);
511
		logger.info("preparing WebAppContext for '"+ conf.getInstanceName() + "'");
512
		WebAppContext cdmWebappContext = new WebAppContext();
513

  
514
		cdmWebappContext.setContextPath("/"+conf.getInstanceName());
515
		cdmWebappContext.setTempDirectory(CDM_WEBAPP_TEMP_FOLDER);
516

  
517
		if(!bindJndiDataSource(instance)){
518
		    // a problem with the datasource occurred skip this webapp
519
		    cdmWebappContext = null;
520
		    logger.error("a problem with the datasource occurred -> skipping /" + conf.getInstanceName());
521
		    instance.setStatus(Status.error);
522
		    return cdmWebappContext;
523
		}
524

  
525
		cdmWebappContext.setAttribute(ATTRIBUTE_DATASOURCE_NAME, conf.getInstanceName());
526
		cdmWebappContext.setAttribute(ATTRIBUTE_JDBC_JNDI_NAME, conf.getJdbcJndiName());
527
		setWebApp(cdmWebappContext, cdmRemoteWebAppFile);
528

  
529
		cdmWebappContext.setAttribute(ATTRIBUTE_CDM_LOGFILE,
530
		        logPath + File.separator + "cdm-"
531
		                + conf.getInstanceName() + ".log");
532

  
533
		if(cdmRemoteWebAppFile.isDirectory() && isRunningFromCdmRemoteWebAppSource()){
534

  
535
		    /*
536
		     * when running the webapp from {projectpath} src/main/webapp we
537
		     * must assure that each web application is using it's own
538
		     * classloader thus we tell the WebAppClassLoader where the
539
		     * dependencies of the webapplication can be found. Otherwise
540
		     * the system classloader would load these resources.
541
		     */
542
		    logger.info("Running webapp from source folder, thus adding java.class.path to WebAppClassLoader");
543

  
544
		    WebAppClassLoader classLoader = new WebAppClassLoader(cdmWebappContext);
545

  
546
		    String classPath = System.getProperty("java.class.path");
547
		    classLoader.addClassPath(classPath);
548
		    cdmWebappContext.setClassLoader(classLoader);
549
		}
550

  
551
		contexts.addHandler(cdmWebappContext);
552
		instance.setWebAppContext(cdmWebappContext);
553
		cdmWebappContext.addLifeCycleListener(instance);
554

  
555
		return cdmWebappContext;
556
	}
437
    /**
438
     * Adds a new WebAppContext to the contexts of the running jetty instance.
439
     * <ol>
440
     * <li>Initialize WebAppContext:
441
     * <ol>
442
     * <li>set context path</li>
443
     * <li>set tmp directory</li>
444
     * <li>bind JndiDataSource</li>
445
     * <li>set web app context attributes</li>
446
     * <li>create and setup individual classloader for the instance</li>
447
     * </ol>
448
     * </li>
449
     * <li>
450
     * finally add the new webappcontext to the contexts of the jetty instance</li>
451
     * </ol>
452
     *
453
     * @param instance
454
     * @return the instance given as parameter of null in case the instance has
455
     *         {@link Status.#disabled} or if it is already added.
456
     * @throws IOException
457
     */
458
    public WebAppContext addCdmInstanceContext(CdmInstance instance) throws IOException {
459

  
460
        Configuration conf = instance.getConfiguration();
461
        if(!instance.isEnabled()){
462
            logger.info(conf.getInstanceName() + " is disabled, possibly due to JVM memory limitations");
463
            return null;
464
        }
465
        if(getContextFor(conf) != null){
466
            logger.info(conf.getInstanceName() + " is alreaddy added to the contexts - skipping");
467
            return null;
468
        }
469

  
470
        instance.setStatus(Status.initializing);
471
        logger.info("preparing WebAppContext for '"+ conf.getInstanceName() + "'");
472
        WebAppContext cdmWebappContext = new WebAppContext();
473

  
474
        cdmWebappContext.setContextPath(constructContextPath(conf));
475
        cdmWebappContext.setTempDirectory(CDM_WEBAPP_TEMP_FOLDER);
476

  
477
        if(!instance.bindJndiDataSource()){
478
            // a problem with the datasource occurred skip this webapp
479
            cdmWebappContext = null;
480
            logger.error("a problem with the datasource occurred -> skipping /" + conf.getInstanceName());
481
            instance.setStatus(Status.error);
482
            return cdmWebappContext;
483
        }
484

  
485
        cdmWebappContext.setAttribute(SharedAttributes.ATTRIBUTE_DATASOURCE_NAME, conf.getInstanceName());
486
        cdmWebappContext.setAttribute(SharedAttributes.ATTRIBUTE_JDBC_JNDI_NAME, conf.getJdbcJndiName());
487
        setWebApp(cdmWebappContext, getCdmRemoteWebAppFile());
488

  
489
        cdmWebappContext.setAttribute(SharedAttributes.ATTRIBUTE_CDM_LOGFILE,
490
                logPath + File.separator + "cdm-"
491
                        + conf.getInstanceName() + ".log");
492

  
493
        if( getCdmRemoteWebAppFile().isDirectory() && isRunningFromCdmRemoteWebAppSource()){
494

  
495
            /*
496
             * when running the webapp from {projectpath} src/main/webapp we
497
             * must assure that each web application is using it's own
498
             * classloader thus we tell the WebAppClassLoader where the
499
             * dependencies of the webapplication can be found. Otherwise
500
             * the system classloader would load these resources.
501
             */
502
            String classPath = System.getProperty("java.class.path");
503
            logger.info("Running webapp from source folder, thus adding system property 'java.class.path=" + classPath +"'  to WebAppClassLoader");
504
            WebAppClassLoader classLoader = new WebAppClassLoader(cdmWebappContext);
505
            classLoader.addClassPath(classPath);
506
            cdmWebappContext.setClassLoader(classLoader);
507
        }
508

  
509
        contexts.addHandler(cdmWebappContext);
510
        instance.setWebAppContext(cdmWebappContext);
511
        cdmWebappContext.addLifeCycleListener(instance);
512
        instance.setStatus(Status.stopped);
513

  
514
        return cdmWebappContext;
515
    }
516

  
517
    /**
518
     * @param conf
519
     * @return
520
     */
521
    private String constructContextPath(Configuration conf) {
522
        return "/" + conf.getInstanceName();
523
    }
524

  
525
    /**
526
     * Removes the given instance from the contexts. If the instance is running
527
     * at the time of calling this method it will be stopped first.
528
     * The JndiDataSource and the webapplicationContext will be released and removed.
529
     *
530
     * @param instance the instance to be removed
531
     *
532
     * @throws Exception in case stopping the instance fails
533
     */
534
    public void removeCdmInstanceContext(CdmInstance instance) throws Exception {
535

  
536
        if(instance.getWebAppContext() != null){
537
            if(instance.getWebAppContext().isRunning()){
538
                try {
539
                    instance.getWebAppContext().stop();
540
                } catch (Exception e) {
541
                    instance.getProblems().add("Error while stopping instance: " + e.getMessage());
542
                    throw e;
543
                }
544
            }
545
            contexts.removeHandler(instance.getWebAppContext());
546
            instance.releaseWebAppContext();
547
        } else  {
548
            // maybe something went wrong before, try to find the potentially lost
549
            // contexts directly in the server
550
            ContextHandler handler = getContextFor(instance.getConfiguration());
551
            if(handler != null){
552
                contexts.removeHandler(handler);
553
            }
554
        }
555
        instance.unbindJndiDataSource();
556
    }
557 557

  
558 558
    /**
559 559
     * Sets the webapp specified by the <code>webApplicationResource</code> to
......
586 586
    public Server getServer() {
587 587
        return server;
588 588
    }
589
}
589

  
590
    public ContextHandler getContextFor(Configuration conf) {
591
        return getContextFor(constructContextPath(conf));
592
    }
593

  
594
    public ContextHandler getContextFor(String contextPath) {
595
        for( Handler handler : contexts.getHandlers()){
596
            if(handler instanceof ContextHandler){
597
                if(((ContextHandler)handler).getContextPath().equals(contextPath)){
598
                    return (ContextHandler)handler;
599
                }
600
            }
601
        }
602
        return null;
603
    }
604

  
605
    public ContextHandlerCollection getContexts() {
606
        return contexts;
607
    }
608

  
609
    /**
610
     * @return a File object pointing to the location of the cdmlib-remote-webapp
611
     */
612
    public File getCdmRemoteWebAppFile(){
613
        if(cdmRemoteWebAppFile == null){
614
            throw new RuntimeException("Invalid order of action. Server not yet started. The startServer() method must be called first. ");
615
        }
616
        return cdmRemoteWebAppFile;
617
    }
618
}

Also available in: Unified diff