Project

General

Profile

Download (26.7 KB) Statistics
| Branch: | Tag: | Revision:
1
// $Id$
2
/**
3
 * Copyright (C) 2009 EDIT
4
 * European Distributed Institute of Taxonomy
5
 * http://www.e-taxonomy.eu
6
 *
7
 * The contents of this file are subject to the Mozilla Public License Version 1.1
8
 * See LICENSE.TXT at the top of this package for the full license terms.
9
 */
10

    
11
package eu.etaxonomy.cdm.server;
12

    
13
import static eu.etaxonomy.cdm.server.CommandOptions.DATASOURCES_FILE;
14
import static eu.etaxonomy.cdm.server.CommandOptions.HELP;
15
import static eu.etaxonomy.cdm.server.CommandOptions.HTTP_PORT;
16
import static eu.etaxonomy.cdm.server.CommandOptions.JMX;
17
import static eu.etaxonomy.cdm.server.CommandOptions.WEBAPP;
18
import static eu.etaxonomy.cdm.server.CommandOptions.WIN32SERVICE;
19
import static eu.etaxonomy.cdm.server.CommandOptions.LOG_DIR;
20

    
21
import java.io.File;
22
import java.io.FileNotFoundException;
23
import java.io.FileOutputStream;
24
import java.io.IOException;
25
import java.io.InputStream;
26
import java.io.OutputStream;
27
import java.lang.management.ManagementFactory;
28
import java.lang.reflect.InvocationTargetException;
29
import java.net.URL;
30
import java.sql.Connection;
31
import java.sql.SQLException;
32
import java.util.List;
33
import java.util.Properties;
34
import java.util.Set;
35

    
36
import javax.naming.NamingException;
37
import javax.sql.DataSource;
38

    
39
import org.apache.commons.cli.CommandLine;
40
import org.apache.commons.cli.CommandLineParser;
41
import org.apache.commons.cli.GnuParser;
42
import org.apache.commons.cli.HelpFormatter;
43
import org.apache.commons.cli.ParseException;
44
import org.apache.commons.io.FileUtils;
45
import org.apache.log4j.Logger;
46
import org.apache.log4j.PatternLayout;
47
import org.apache.log4j.RollingFileAppender;
48
import org.eclipse.jetty.jmx.MBeanContainer;
49
import org.eclipse.jetty.security.HashLoginService;
50
import org.eclipse.jetty.server.Server;
51
import org.eclipse.jetty.server.handler.ContextHandlerCollection;
52
import org.eclipse.jetty.server.handler.ContextHandler.Context;
53
import org.eclipse.jetty.util.component.LifeCycle;
54
import org.eclipse.jetty.util.component.LifeCycle.Listener;
55
import org.eclipse.jetty.util.log.Log;
56
import org.eclipse.jetty.webapp.WebAppClassLoader;
57
import org.eclipse.jetty.webapp.WebAppContext;
58

    
59
import eu.etaxonomy.cdm.server.CdmInstanceProperties.Status;
60
import eu.etaxonomy.cdm.server.win32service.Win32Service;
61

    
62

    
63
/**
64
 * A bootstrap class for starting Jetty Runner using an embedded war.
65
 *
66
 * Recommended start options for the java virtual machine:
67
 * <pre>
68
 * -Xmx1024M
69
 *
70
 * -XX:PermSize=128m
71
 * -XX:MaxPermSize=192m
72
 *
73
 * -XX:+UseConcMarkSweepGC
74
 * -XX:+CMSClassUnloadingEnabled
75
 * -XX:+CMSPermGenSweepingEnabled
76
 * </pre>
77
 *
78
 * @version $Revision$
79
 */
80
public final class Bootloader {
81
    /**
82
     *
83
     */
84
    private static final String VERSION_PROPERTIES_FILE = "version.properties";
85

    
86
    //private static final String DEFAULT_WARFILE = "target/";
87

    
88

    
89

    
90
    /**
91
     * @author a.kohlbecker
92
     * @date 03.02.2011
93
     *
94
     */
95
    private class WebAppContextListener implements Listener {
96

    
97
        WebAppContext cdmWebappContext;
98
        /**
99
         * @param cdmWebappContext
100
         */
101
        public WebAppContextListener(WebAppContext cdmWebappContext) {
102
            this.cdmWebappContext = cdmWebappContext;
103
        }
104

    
105
        @Override
106
        public void lifeCycleStopping(LifeCycle event) {
107
            logger.info("lifeCycleStopping");
108
        }
109

    
110
        @Override
111
        public void lifeCycleStopped(LifeCycle event) {
112
            logger.info("lifeCycleStopped");
113

    
114
        }
115

    
116
        @Override
117
        public void lifeCycleStarting(LifeCycle event) {
118
            logger.info("lifeCycleStarting");
119
        }
120

    
121
        @SuppressWarnings("unchecked")
122
        @Override
123
        public void lifeCycleStarted(LifeCycle event) {
124
            logger.info("lifeCycleStarted");
125

    
126
            List<String> messages = getServletContextAttribute(cdmWebappContext, ATTRIBUTE_ERROR_MESSAGES, List.class);
127
            String dataSourceName = getServletContextAttribute(cdmWebappContext, ATTRIBUTE_DATASOURCE_NAME, String.class);
128

    
129
            if(messages != null && dataSourceName != null){
130
                CdmInstanceProperties configAndStatus = findConfigAndStatusFor(dataSourceName);
131
                configAndStatus.getProblems().addAll(messages);
132
                configAndStatus.setStatus(Status.error);
133
                try {
134
                    logger.warn("Stopping context '" + dataSourceName + "' due to errors reported in ServletContext");
135
                    cdmWebappContext.stop();
136
                } catch (Exception e) {
137
                    logger.error(e);
138
                }
139
            }
140
        }
141

    
142

    
143
        @Override
144
        public void lifeCycleFailure(LifeCycle event, Throwable cause) {
145
            logger.error("lifeCycleFailure");
146
        }
147
    }
148

    
149
    private static final Logger logger = Logger.getLogger(Bootloader.class);
150

    
151
    private static final String DATASOURCE_BEANDEF_FILE = "datasources.xml";
152
    private static final String REALM_PROPERTIES_FILE = "cdm-server-realm.properties";
153

    
154
    private static final String USERHOME_CDM_LIBRARY_PATH = System.getProperty("user.home")+File.separator+".cdmLibrary"+File.separator;
155
    private static final String TMP_PATH = USERHOME_CDM_LIBRARY_PATH + "server" + File.separator;
156
    private static final String LOG_PATH = USERHOME_CDM_LIBRARY_PATH + "log" + File.separator;
157

    
158
    private static final String APPLICATION_NAME = "CDM Server";
159
    private static final String WAR_POSTFIX = ".war";
160

    
161
    private static final String CDMLIB_REMOTE_WEBAPP = "cdmlib-remote-webapp";
162
    private static final String CDMLIB_REMOTE_WEBAPP_VERSION = "cdmlib-remote-webapp.version";
163

    
164
    private static final String DEFAULT_WEBAPP_WAR_NAME = "default-webapp";
165
    private static final File DEFAULT_WEBAPP_TEMP_FOLDER = new File(TMP_PATH + DEFAULT_WEBAPP_WAR_NAME);
166
    private static final File CDM_WEBAPP_TEMP_FOLDER = new File(TMP_PATH + CDMLIB_REMOTE_WEBAPP);
167

    
168
    private static final String ATTRIBUTE_JDBC_JNDI_NAME = "cdm.jdbcJndiName";
169
    private static final String ATTRIBUTE_DATASOURCE_NAME = "cdm.datasource";
170
    private static final String ATTRIBUTE_CDM_LOGFILE = "cdm.logfile";
171
    /**
172
     * same as in eu.etaxonomy.cdm.remote.config.DataSourceConfigurer
173
     */
174
    private static final String ATTRIBUTE_ERROR_MESSAGES = "cdm.errorMessages";
175

    
176

    
177
    // memory requirements
178
    private static final long MB = 1024 * 1024;
179
    private static final long PERM_GEN_SPACE_PER_INSTANCE = 64 * MB;
180
    private static final long HEAP_PER_INSTANCE = 150 * MB;
181

    
182
    private static final int KB = 1024;
183

    
184

    
185
    private Set<CdmInstanceProperties> configAndStatusSet = null;
186

    
187
    public Set<CdmInstanceProperties> getConfigAndStatus() {
188
        return configAndStatusSet;
189
    }
190

    
191
    private File cdmRemoteWebAppFile = null;
192
    private File defaultWebAppFile = null;
193

    
194
    private String logPath = null;
195

    
196
    private Server server = null;
197
    private ContextHandlerCollection contexts = new ContextHandlerCollection();
198

    
199
    private CommandLine cmdLine;
200

    
201
    /* thread save singleton implementation */
202

    
203
    private static Bootloader instance = new Bootloader();
204

    
205
    private Bootloader() {}
206

    
207
    /**
208
     * @return the thread save singleton instance of the Bootloader
209
     */
210
    public synchronized static Bootloader getBootloader(){
211
        return instance;
212
    }
213

    
214
    /* end of singleton implementation */
215

    
216
    private Set<CdmInstanceProperties> loadDataSources(){
217
        if(configAndStatusSet == null){
218
            File datasourcesFile = new File(USERHOME_CDM_LIBRARY_PATH, DATASOURCE_BEANDEF_FILE);
219
            configAndStatusSet = DataSourcePropertyParser.parseDataSourceConfigs(datasourcesFile);
220
            logger.info("cdm server instance names loaded: "+ configAndStatusSet.toString());
221
        }
222
        return configAndStatusSet;
223
    }
224

    
225
    public int writeStreamTo(final InputStream input, final OutputStream output, int bufferSize) throws IOException {
226
        int available = Math.min(input.available(), 256 * KB);
227
        byte[] buffer = new byte[Math.max(bufferSize, available)];
228
        int answer = 0;
229
        int count = input.read(buffer);
230
        while (count >= 0) {
231
            output.write(buffer, 0, count);
232
            answer += count;
233
            count = input.read(buffer);
234
        }
235
        return answer;
236
    }
237

    
238
    private boolean bindJndiDataSource(CdmInstanceProperties conf) {
239
        try {
240
            Class<DataSource> dsCass = (Class<DataSource>) Thread.currentThread().getContextClassLoader().loadClass("com.mchange.v2.c3p0.ComboPooledDataSource");
241
            DataSource datasource = dsCass.newInstance();
242
            dsCass.getMethod("setDriverClass", new Class[] {String.class}).invoke(datasource, new Object[] {conf.getDriverClass()});
243
            dsCass.getMethod("setJdbcUrl", new Class[] {String.class}).invoke(datasource, new Object[] {conf.getUrl()});
244
            dsCass.getMethod("setUser", new Class[] {String.class}).invoke(datasource, new Object[] {conf.getUsername()});
245
            dsCass.getMethod("setPassword", new Class[] {String.class}).invoke(datasource, new Object[] {conf.getPassword()});
246

    
247
            Connection connection = null;
248
            String sqlerror = null;
249
            try {
250
                connection = datasource.getConnection();
251
                connection.close();
252
            } catch (SQLException e) {
253
                sqlerror = e.getMessage() + "["+ e.getSQLState() + "]";
254
                conf.getProblems().add(sqlerror);
255
                if(connection !=  null){
256
                    try {connection.close();} catch (SQLException e1) { /* IGNORE */ }
257
                }
258
                logger.error(conf.toString() + " has problem : "+ sqlerror );
259
            }
260

    
261
            if(!conf.hasProblems()){
262
                logger.info("binding jndi datasource at " + conf.getJdbcJndiName() + " with "+conf.getUsername() +"@"+ conf.getUrl());
263
                org.eclipse.jetty.plus.jndi.Resource jdbcResource = new org.eclipse.jetty.plus.jndi.Resource(conf.getJdbcJndiName(), datasource);
264
                return true;
265
            }
266

    
267
        } catch (IllegalArgumentException e) {
268
            logger.error(e);
269
            e.printStackTrace();
270
        } catch (SecurityException e) {
271
            logger.error(e);
272
        } catch (ClassNotFoundException e) {
273
            logger.error(e);
274
        } catch (InstantiationException e) {
275
            logger.error(e);
276
        } catch (IllegalAccessException e) {
277
            logger.error(e);
278
        } catch (InvocationTargetException e) {
279
            logger.error(e);
280
        } catch (NoSuchMethodException e) {
281
            logger.error(e);
282
        } catch (NamingException e) {
283
            logger.error(e);
284
        }
285
        return false;
286
    }
287

    
288
    public void parseCommandOptions(String[] args) throws ParseException {
289
        CommandLineParser parser = new GnuParser();
290
        cmdLine = parser.parse( CommandOptions.getOptions(), args );
291

    
292
         // print the help message
293
         if(cmdLine.hasOption(HELP.getOpt())){
294
             HelpFormatter formatter = new HelpFormatter();
295
             formatter.setWidth(200);
296
             formatter.printHelp( "java .. ", CommandOptions.getOptions() );
297
             System.exit(0);
298
         }
299
    }
300

    
301

    
302
    private File extractWar(String warName) throws IOException, FileNotFoundException {
303
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
304
        String warFileName = warName + WAR_POSTFIX;
305

    
306
        // 1. find in classpath
307
        URL resource = classLoader.getResource(warFileName);
308
        if (resource == null) {
309
            logger.error("Could not find the " + warFileName + " on classpath!");
310

    
311
            File pomxml = new File("pom.xml");
312
            if(pomxml.exists()){
313
                // 2. try finding in target folder of maven project
314
                File warFile = new File("target" + File.separator + warFileName);
315
                logger.debug("looging for war file at " + warFile.getAbsolutePath());
316
                if (warFile.canRead()) {
317
                    resource = warFile.toURI().toURL();
318
                } else {
319
                    logger.error("Also could not find the " + warFileName + " in maven project, try excuting 'mvn install'");
320
                }
321
            }
322
        }
323

    
324
        if (resource == null) {
325
            // no way finding the war file :-(
326
            System.exit(1);
327
        }
328

    
329

    
330
        File warFile = new File(TMP_PATH, warName + "-" + WAR_POSTFIX);
331
        logger.info("Extracting " + warFileName + " to " + warFile + " ...");
332

    
333
        writeStreamTo(resource.openStream(), new FileOutputStream(warFile), 8 * KB);
334

    
335
        logger.info("Extracted " + warFileName);
336
        return warFile;
337
    }
338

    
339

    
340
    /**
341
     * MAIN METHOD
342
     *
343
     * @param args
344
     * @throws Exception
345
     */
346
    public static void main(String[] args) throws Exception {
347

    
348
        Bootloader bootloader = Bootloader.getBootloader();
349

    
350
        bootloader.parseCommandOptions(args);
351

    
352
        bootloader.startServer();
353
    }
354

    
355

    
356

    
357
    public void startServer() throws IOException,
358
            FileNotFoundException, Exception, InterruptedException {
359

    
360

    
361
        if(cmdLine.hasOption(LOG_DIR.getOpt())){
362
            logPath = cmdLine.getOptionValue(LOG_DIR.getOpt());
363
        } else {
364
            logPath = LOG_PATH;
365
        }
366

    
367

    
368
        //assure LOG_PATH exists
369
        File logPathFile = new File(logPath);
370
        if(!logPathFile.exists()){
371
            FileUtils.forceMkdir(new File(logPath));
372
        }
373

    
374
        //append logger
375
        configureFileLogger();
376

    
377
        logger.info("Starting "+APPLICATION_NAME);
378
        logger.info("Using  " + System.getProperty("user.home") + " as home directory. Can be specified by -Duser.home=<FOLDER>");
379

    
380
        //assure TMP_PATH exists and clean it up
381
        File tempDir = new File(TMP_PATH);
382
        if(!tempDir.exists() && !tempDir.mkdirs()){
383
            logger.error("Error creating temporary directory for webapplications " + tempDir.getAbsolutePath());
384
            System.exit(-1);
385
        } else {
386
            if(FileUtils.deleteQuietly(tempDir)){
387
                tempDir.mkdirs();
388
                logger.info("Old webapplications successfully cleared");
389
            }
390
        }
391
        tempDir = null;
392

    
393

    
394
         // WARFILE
395
         if(cmdLine.hasOption(WEBAPP.getOpt())){
396
             cdmRemoteWebAppFile = new File(cmdLine.getOptionValue(WEBAPP.getOpt()));
397
             if(cdmRemoteWebAppFile.isDirectory()){
398
                 logger.info("using user defined web application folder: " + cdmRemoteWebAppFile.getAbsolutePath());
399
             } else {
400
                 logger.info("using user defined warfile: " + cdmRemoteWebAppFile.getAbsolutePath());
401
             }
402
             if(isRunningFromCdmRemoteWebAppSource()){
403
                 //TODO check if all local paths are valid !!!!
404
                defaultWebAppFile = new File("./src/main/webapp");
405

    
406
             } else {
407
                defaultWebAppFile = extractWar(DEFAULT_WEBAPP_WAR_NAME);
408
             }
409
         } else {
410
             // read version number
411
             String version = readCdmRemoteVersion();
412

    
413
             cdmRemoteWebAppFile = extractWar(CDMLIB_REMOTE_WEBAPP + "-" + version);
414
             defaultWebAppFile = extractWar(DEFAULT_WEBAPP_WAR_NAME);
415
         }
416

    
417
         // HTTP Port
418
         int httpPort = 8080;
419
         if(cmdLine.hasOption(HTTP_PORT.getOpt())){
420
             try {
421
                httpPort = Integer.parseInt(cmdLine.getOptionValue(HTTP_PORT.getOpt()));
422
                logger.info(HTTP_PORT.getOpt()+" set to "+cmdLine.getOptionValue(HTTP_PORT.getOpt()));
423
            } catch (NumberFormatException e) {
424
                logger.error("Supplied portnumber is not an integer");
425
                System.exit(-1);
426
            }
427
         }
428

    
429
         if(cmdLine.hasOption(DATASOURCES_FILE.getOpt())){
430
             logger.error(DATASOURCES_FILE.getOpt() + " NOT JET IMPLEMENTED!!!");
431
         }
432

    
433
        loadDataSources();
434

    
435
        verifyMemoryRequirements();
436

    
437

    
438
        server = new Server(httpPort);
439

    
440
        // JMX support
441
        if(cmdLine.hasOption(JMX.getOpt())){
442
            logger.info("adding JMX support ...");
443
            MBeanContainer mBeanContainer = new MBeanContainer(ManagementFactory.getPlatformMBeanServer());
444
            server.getContainer().addEventListener(mBeanContainer);
445
            mBeanContainer.addBean(Log.getLog());
446
            mBeanContainer.start();
447
        }
448

    
449
        if(cmdLine.hasOption(WIN32SERVICE.getOpt())){
450
            Win32Service win32Service = new Win32Service();
451
            win32Service.setServer(server);
452
            server.setStopAtShutdown(true);
453
            server.addBean(win32Service);
454
        }
455

    
456
        // add servelet contexts
457

    
458

    
459
        //
460
        // 1. default context
461
        //
462
        logger.info("preparing default WebAppContext");
463
        WebAppContext defaultWebappContext = new WebAppContext();
464

    
465
        setWebApp(defaultWebappContext, defaultWebAppFile);
466
        defaultWebappContext.setContextPath("/");
467
        defaultWebappContext.setTempDirectory(DEFAULT_WEBAPP_TEMP_FOLDER);
468

    
469
        // configure security context
470
        // see for reference * http://docs.codehaus.org/display/JETTY/Realms
471
        //                   * http://wiki.eclipse.org/Jetty/Starting/Porting_to_Jetty_7
472
        HashLoginService loginService = new HashLoginService();
473
        loginService.setConfig(USERHOME_CDM_LIBRARY_PATH + REALM_PROPERTIES_FILE);
474
        defaultWebappContext.getSecurityHandler().setLoginService(loginService);
475

    
476
        // Important:
477
        // the defaultWebappContext MUST USE the super classloader
478
        // otherwise the status page (index.jsp) might not work
479
        defaultWebappContext.setClassLoader(this.getClass().getClassLoader());
480
        contexts.addHandler(defaultWebappContext);
481

    
482
        //
483
        // 2. cdm server contexts
484
        //
485
        server.addLifeCycleListener(new LifeCycle.Listener(){
486

    
487
            @Override
488
            public void lifeCycleFailure(LifeCycle event, Throwable cause) {
489
                logger.error("Jetty LifeCycleFailure", cause);
490
            }
491

    
492
            @Override
493
            public void lifeCycleStarted(LifeCycle event) {
494
                logger.info("cdmserver has started, now adding CDM server contexts");
495
                try {
496
                    addCdmServerContexts(true);
497
                } catch (IOException e1) {
498
                    logger.error(e1);
499
                }
500
            }
501

    
502
            @Override
503
            public void lifeCycleStarting(LifeCycle event) {
504
            }
505

    
506
            @Override
507
            public void lifeCycleStopped(LifeCycle event) {
508
            }
509

    
510
            @Override
511
            public void lifeCycleStopping(LifeCycle event) {
512
            }
513

    
514
            });
515

    
516

    
517
        logger.info("setting contexts ...");
518
        server.setHandler(contexts);
519
        logger.info("starting jetty ...");
520
//        try {
521

    
522
            server.start();
523

    
524
//        } catch(org.springframework.beans.BeansException e){
525
//        	Throwable rootCause = null;
526
//        	while(e.getCause() != null){
527
//        		rootCause = e.getCause();
528
//        	}
529
//        	if(rootCause != null && rootCause.getClass().getSimpleName().equals("InvalidCdmVersionException")){
530
//
531
//        		logger.error("rootCause ----------->" + rootCause.getMessage());
532
////        		for(CdmInstanceProperties props : configAndStatus){
533
////        			if(props.getDataSourceName())
534
////        		}
535
//        	}
536
//        }
537

    
538
        if(cmdLine.hasOption(WIN32SERVICE.getOpt())){
539
            logger.info("jetty has started as win32 service");
540
        } else {
541
            server.join();
542
            logger.info(APPLICATION_NAME+" stopped.");
543
            System.exit(0);
544
        }
545
    }
546

    
547
    public String readCdmRemoteVersion() throws IOException {
548
        InputStream versionInStream = Bootloader.class.getClassLoader().getResourceAsStream(VERSION_PROPERTIES_FILE);
549
         Properties versionProperties = new Properties();
550
         versionProperties.load(versionInStream);
551
         String version = versionProperties.getProperty(CDMLIB_REMOTE_WEBAPP_VERSION);
552
        return version;
553
    }
554

    
555
    /**
556
     *
557
     */
558
    private void verifyMemoryRequirements() {
559

    
560
        verifyMemoryRequirement("PermGenSpace", PERM_GEN_SPACE_PER_INSTANCE, JvmManager.getPermGenSpaceUsage().getMax());
561
        verifyMemoryRequirement("HeapSpace", HEAP_PER_INSTANCE, JvmManager.getHeapMemoryUsage().getMax());
562

    
563
    }
564

    
565
    private void verifyMemoryRequirement(String memoryName, long requiredSpacePerIntance, long availableSpace) {
566

    
567

    
568
        long requiredSpace = configAndStatusSet.size() * requiredSpacePerIntance;
569

    
570
        if(requiredSpace > availableSpace){
571

    
572
            String message = memoryName + " ("
573
                + (availableSpace / MB)
574
                + "MB) insufficient for "
575
                + configAndStatusSet.size()
576
                + " instances. Increase " + memoryName + " to "
577
                + (requiredSpace / MB)
578
                + "MB";
579
                ;
580
            logger.error(message + " => disabling some instances!!!");
581

    
582
            // disabling some instances
583
            int i=0;
584
            for(CdmInstanceProperties instanceProps : configAndStatusSet){
585
                i++;
586
                if(i * requiredSpacePerIntance > availableSpace){
587
                    instanceProps.setStatus(Status.disabled);
588
                    instanceProps.getProblems().add("Disbled due to: " + message);
589
                }
590
            }
591
        }
592
    }
593

    
594
    /**
595
     * Configures and adds a {@link RollingFileAppender} to the root logger
596
     *
597
     * The log files of the cdm-remote instances are configured by the
598
     * {@link eu.etaxonomy.cdm.remote.config.LoggingConfigurer}
599
     */
600
    private void configureFileLogger() {
601

    
602
        PatternLayout layout = new PatternLayout("%d %p [%c] - %m%n");
603
        try {
604
            String logFile = logPath + File.separator + "cdmserver.log";
605
            RollingFileAppender appender = new RollingFileAppender(layout, logFile);
606
            appender.setMaxBackupIndex(3);
607
            appender.setMaxFileSize("2MB");
608
            Logger.getRootLogger().addAppender(appender);
609
            logger.info("logging to :" + logFile);
610
        } catch (IOException e) {
611
            logger.error("Creating RollingFileAppender failed:", e);
612
        }
613
    }
614

    
615
    private void addCdmServerContexts(boolean austostart) throws IOException {
616

    
617
        for(CdmInstanceProperties conf : configAndStatusSet){
618

    
619
            if(!conf.isEnabled()){
620
                logger.info(conf.getDataSourceName() + " is disabled due to JVM memory limitations => skipping");
621
                continue;
622
            }
623
            conf.setStatus(CdmInstanceProperties.Status.initializing);
624
            logger.info("preparing WebAppContext for '"+ conf.getDataSourceName() + "'");
625
            WebAppContext cdmWebappContext = new WebAppContext();
626

    
627
            cdmWebappContext.setContextPath("/"+conf.getDataSourceName());
628
            cdmWebappContext.setTempDirectory(CDM_WEBAPP_TEMP_FOLDER);
629

    
630
            if(!bindJndiDataSource(conf)){
631
                // a problem with the datasource occurred skip this webapp
632
                cdmWebappContext = null;
633
                logger.error("a problem with the datasource occurred -> skipping /" + conf.getDataSourceName());
634
                conf.setStatus(CdmInstanceProperties.Status.error);
635
                continue;
636
            }
637

    
638
            cdmWebappContext.setAttribute(ATTRIBUTE_DATASOURCE_NAME, conf.getDataSourceName());
639
            cdmWebappContext.setAttribute(ATTRIBUTE_JDBC_JNDI_NAME, conf.getJdbcJndiName());
640
            setWebApp(cdmWebappContext, cdmRemoteWebAppFile);
641

    
642
            cdmWebappContext.setAttribute(ATTRIBUTE_CDM_LOGFILE,
643
                    logPath + File.separator + "cdm-"
644
                            + conf.getDataSourceName() + ".log");
645

    
646
            if(cdmRemoteWebAppFile.isDirectory() && isRunningFromCdmRemoteWebAppSource()){
647

    
648
                /*
649
                 * when running the webapp from {projectpath} src/main/webapp we
650
                 * must assure that each web application is using it's own
651
                 * classloader thus we tell the WebAppClassLoader where the
652
                 * dependencies of the webapplication can be found. Otherwise
653
                 * the system classloader would load these resources.
654
                 */
655
                logger.info("Running webapp from source folder, thus adding java.class.path to WebAppClassLoader");
656

    
657
                WebAppClassLoader classLoader = new WebAppClassLoader(cdmWebappContext);
658

    
659
                String classPath = System.getProperty("java.class.path");
660
                classLoader.addClassPath(classPath);
661
                cdmWebappContext.setClassLoader(classLoader);
662
            }
663

    
664
            cdmWebappContext.addLifeCycleListener(new WebAppContextListener(cdmWebappContext));
665
            contexts.addHandler(cdmWebappContext);
666

    
667
            if(austostart){
668
                try {
669
                    conf.setStatus(CdmInstanceProperties.Status.starting);
670
                    cdmWebappContext.start();
671
                    if(!conf.getStatus().equals(Status.error)){
672
                        conf.setStatus(CdmInstanceProperties.Status.started);
673
                    }
674
                } catch (Exception e) {
675
                    logger.error("Could not start " + cdmWebappContext.getContextPath());
676
                    conf.setStatus(CdmInstanceProperties.Status.error);
677
                }
678
            }
679

    
680
        }
681
    }
682

    
683
    /**
684
     * @param context
685
     * @param webApplicationResource
686
     */
687
    private void setWebApp(WebAppContext context, File webApplicationResource) {
688
        if(webApplicationResource.isDirectory()){
689
            context.setResourceBase(webApplicationResource.getAbsolutePath());
690
            logger.debug("setting directory " + webApplicationResource.getAbsolutePath() + " as webapplication");
691
        } else {
692
            context.setWar(webApplicationResource.getAbsolutePath());
693
            logger.debug("setting war file " + webApplicationResource.getAbsolutePath() + " as webapplication");
694
        }
695
    }
696

    
697
    /**
698
     * @return
699
     */
700
    private boolean isRunningFromCdmRemoteWebAppSource() {
701
        String webappPathNormalized = cdmRemoteWebAppFile.getAbsolutePath().replace('\\', '/');
702
        return webappPathNormalized.endsWith("src/main/webapp") || webappPathNormalized.endsWith("cdmlib-remote-webapp/target/cdmserver");
703
    }
704

    
705
    /**
706
     * @param dataSourceName
707
     * @return
708
     */
709
    private CdmInstanceProperties findConfigAndStatusFor(String dataSourceName){
710
        for(CdmInstanceProperties props : configAndStatusSet){
711
            if(props.getDataSourceName().equals(dataSourceName)){
712
                return props;
713
            }
714
        }
715
        return null;
716
    }
717

    
718
    /**
719
     * @param <T>
720
     * @param webAppContext
721
     * @param attributeName
722
     * @param type
723
     * @return
724
     */
725
    @SuppressWarnings("unchecked")
726
    private <T> T getServletContextAttribute(WebAppContext webAppContext, String attributeName, Class<T> type) {
727

    
728
        Context servletContext = webAppContext.getServletContext();
729
        Object value = servletContext.getAttribute(attributeName);
730
        if( value != null && type.isAssignableFrom(value.getClass())){
731

    
732
        }
733
        return (T) value;
734
    }
735

    
736
    public Server getServer() {
737
        return server;
738
    }
739
}
(1-1/5)