Project

General

Profile

Download (27.5 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 int KB = 1024;
179
    private static final long MB = 1024 * KB;
180
    public static final long PERM_GEN_SPACE_PER_INSTANCE = 55 * MB;
181
    public static final long HEAP_PER_INSTANCE = 130 * MB;
182
    public static final long PERM_GEN_SPACE_CDMSERVER = 19 * MB;
183
    public static final long HEAP_CDMSERVER = 15 * MB;
184

    
185

    
186

    
187
    private Set<CdmInstanceProperties> configAndStatusSet = null;
188

    
189
    public Set<CdmInstanceProperties> getConfigAndStatus() {
190
        return configAndStatusSet;
191
    }
192

    
193
    private File cdmRemoteWebAppFile = null;
194
    private File defaultWebAppFile = null;
195

    
196
    private String logPath = null;
197

    
198
    private Server server = null;
199
    private ContextHandlerCollection contexts = new ContextHandlerCollection();
200

    
201
    private CommandLine cmdLine;
202

    
203
    /* thread save singleton implementation */
204

    
205
    private static Bootloader instance = new Bootloader();
206

    
207
    private Bootloader() {}
208

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

    
216
    /* end of singleton implementation */
217

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

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

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

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

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

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

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

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

    
303

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

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

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

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

    
331

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

    
335
        writeStreamTo(resource.openStream(), new FileOutputStream(warFile), 8 * KB);
336

    
337
        logger.info("Extracted " + warFileName);
338
        return warFile;
339
    }
340

    
341

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

    
350
        Bootloader bootloader = Bootloader.getBootloader();
351

    
352
        bootloader.parseCommandOptions(args);
353

    
354
        bootloader.startServer();
355
    }
356

    
357

    
358

    
359
    public void startServer() throws IOException,
360
            FileNotFoundException, Exception, InterruptedException {
361

    
362

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

    
369

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

    
376
        //append logger
377
        configureFileLogger();
378

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

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

    
395

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

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

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

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

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

    
435
        loadDataSources();
436

    
437
        verifyMemoryRequirements();
438

    
439

    
440
        server = new Server(httpPort);
441

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

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

    
458
        // add servelet contexts
459

    
460

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

    
467
        setWebApp(defaultWebappContext, defaultWebAppFile);
468
        defaultWebappContext.setContextPath("/");
469
        defaultWebappContext.setTempDirectory(DEFAULT_WEBAPP_TEMP_FOLDER);
470

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

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

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

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

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

    
504
            @Override
505
            public void lifeCycleStarting(LifeCycle event) {
506
            }
507

    
508
            @Override
509
            public void lifeCycleStopped(LifeCycle event) {
510
            }
511

    
512
            @Override
513
            public void lifeCycleStopping(LifeCycle event) {
514
            }
515

    
516
            });
517

    
518

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

    
524
            server.start();
525

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

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

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

    
557
    /**
558
     *
559
     */
560
    private void verifyMemoryRequirements() {
561

    
562
        verifyMemoryRequirement("PermGenSpace", PERM_GEN_SPACE_CDMSERVER, PERM_GEN_SPACE_PER_INSTANCE, JvmManager.getPermGenSpaceUsage().getMax());
563
        verifyMemoryRequirement("HeapSpace", HEAP_CDMSERVER, HEAP_PER_INSTANCE, JvmManager.getHeapMemoryUsage().getMax());
564

    
565
    }
566

    
567
    private void verifyMemoryRequirement(String memoryName, long requiredSpaceServer, long requiredSpacePerInstance, long availableSpace) {
568

    
569

    
570
        long recommendedMinimumSpace = recommendedMinimumSpace(requiredSpaceServer, requiredSpacePerInstance, null);
571

    
572
        if(recommendedMinimumSpace > availableSpace){
573

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

    
584
            // disabling some instances
585
            int i=0;
586
            for(CdmInstanceProperties instanceProps : configAndStatusSet){
587
                i++;
588
                if(recommendedMinimumSpace(requiredSpaceServer, requiredSpacePerInstance, i)  > availableSpace){
589
                    instanceProps.setStatus(Status.disabled);
590
                    instanceProps.getProblems().add("Disabled due to: " + message);
591
                }
592
            }
593
        }
594
    }
595

    
596
    /**
597
     * @param requiredServerSpace
598
     * @param requiredSpacePerIntance
599
     * @param numOfInstances may be null, the total number of instances found in the current configuration is used in this case.
600
     * @return
601
     */
602
    public long recommendedMinimumSpace(long requiredServerSpace, long requiredSpacePerIntance, Integer numOfInstances) {
603
        if(numOfInstances == null){
604
            numOfInstances = configAndStatusSet.size();
605
        }
606
        return (numOfInstances * requiredSpacePerIntance) + requiredServerSpace;
607
    }
608

    
609
    /**
610
     * Configures and adds a {@link RollingFileAppender} to the root logger
611
     *
612
     * The log files of the cdm-remote instances are configured by the
613
     * {@link eu.etaxonomy.cdm.remote.config.LoggingConfigurer}
614
     */
615
    private void configureFileLogger() {
616

    
617
        PatternLayout layout = new PatternLayout("%d %p [%c] - %m%n");
618
        try {
619
            String logFile = logPath + File.separator + "cdmserver.log";
620
            RollingFileAppender appender = new RollingFileAppender(layout, logFile);
621
            appender.setMaxBackupIndex(3);
622
            appender.setMaxFileSize("2MB");
623
            Logger.getRootLogger().addAppender(appender);
624
            logger.info("logging to :" + logFile);
625
        } catch (IOException e) {
626
            logger.error("Creating RollingFileAppender failed:", e);
627
        }
628
    }
629

    
630
    private void addCdmServerContexts(boolean austostart) throws IOException {
631

    
632
        for(CdmInstanceProperties conf : configAndStatusSet){
633

    
634
            if(!conf.isEnabled()){
635
                logger.info(conf.getDataSourceName() + " is disabled due to JVM memory limitations => skipping");
636
                continue;
637
            }
638
            conf.setStatus(CdmInstanceProperties.Status.initializing);
639
            logger.info("preparing WebAppContext for '"+ conf.getDataSourceName() + "'");
640
            WebAppContext cdmWebappContext = new WebAppContext();
641

    
642
            cdmWebappContext.setContextPath("/"+conf.getDataSourceName());
643
            cdmWebappContext.setTempDirectory(CDM_WEBAPP_TEMP_FOLDER);
644

    
645
            if(!bindJndiDataSource(conf)){
646
                // a problem with the datasource occurred skip this webapp
647
                cdmWebappContext = null;
648
                logger.error("a problem with the datasource occurred -> skipping /" + conf.getDataSourceName());
649
                conf.setStatus(CdmInstanceProperties.Status.error);
650
                continue;
651
            }
652

    
653
            cdmWebappContext.setAttribute(ATTRIBUTE_DATASOURCE_NAME, conf.getDataSourceName());
654
            cdmWebappContext.setAttribute(ATTRIBUTE_JDBC_JNDI_NAME, conf.getJdbcJndiName());
655
            setWebApp(cdmWebappContext, cdmRemoteWebAppFile);
656

    
657
            cdmWebappContext.setAttribute(ATTRIBUTE_CDM_LOGFILE,
658
                    logPath + File.separator + "cdm-"
659
                            + conf.getDataSourceName() + ".log");
660

    
661
            if(cdmRemoteWebAppFile.isDirectory() && isRunningFromCdmRemoteWebAppSource()){
662

    
663
                /*
664
                 * when running the webapp from {projectpath} src/main/webapp we
665
                 * must assure that each web application is using it's own
666
                 * classloader thus we tell the WebAppClassLoader where the
667
                 * dependencies of the webapplication can be found. Otherwise
668
                 * the system classloader would load these resources.
669
                 */
670
                logger.info("Running webapp from source folder, thus adding java.class.path to WebAppClassLoader");
671

    
672
                WebAppClassLoader classLoader = new WebAppClassLoader(cdmWebappContext);
673

    
674
                String classPath = System.getProperty("java.class.path");
675
                classLoader.addClassPath(classPath);
676
                cdmWebappContext.setClassLoader(classLoader);
677
            }
678

    
679
            cdmWebappContext.addLifeCycleListener(new WebAppContextListener(cdmWebappContext));
680
            contexts.addHandler(cdmWebappContext);
681

    
682
            if(austostart){
683
                try {
684
                    conf.setStatus(CdmInstanceProperties.Status.starting);
685
                    cdmWebappContext.start();
686
                    if(!conf.getStatus().equals(Status.error)){
687
                        conf.setStatus(CdmInstanceProperties.Status.started);
688
                    }
689
                } catch (Exception e) {
690
                    logger.error("Could not start " + cdmWebappContext.getContextPath());
691
                    conf.setStatus(CdmInstanceProperties.Status.error);
692
                }
693
            }
694

    
695
        }
696
    }
697

    
698
    /**
699
     * @param context
700
     * @param webApplicationResource
701
     */
702
    private void setWebApp(WebAppContext context, File webApplicationResource) {
703
        if(webApplicationResource.isDirectory()){
704
            context.setResourceBase(webApplicationResource.getAbsolutePath());
705
            logger.debug("setting directory " + webApplicationResource.getAbsolutePath() + " as webapplication");
706
        } else {
707
            context.setWar(webApplicationResource.getAbsolutePath());
708
            logger.debug("setting war file " + webApplicationResource.getAbsolutePath() + " as webapplication");
709
        }
710
    }
711

    
712
    /**
713
     * @return
714
     */
715
    private boolean isRunningFromCdmRemoteWebAppSource() {
716
        String webappPathNormalized = cdmRemoteWebAppFile.getAbsolutePath().replace('\\', '/');
717
        return webappPathNormalized.endsWith("src/main/webapp") || webappPathNormalized.endsWith("cdmlib-remote-webapp/target/cdmserver");
718
    }
719

    
720
    /**
721
     * @param dataSourceName
722
     * @return
723
     */
724
    private CdmInstanceProperties findConfigAndStatusFor(String dataSourceName){
725
        for(CdmInstanceProperties props : configAndStatusSet){
726
            if(props.getDataSourceName().equals(dataSourceName)){
727
                return props;
728
            }
729
        }
730
        return null;
731
    }
732

    
733
    /**
734
     * @param <T>
735
     * @param webAppContext
736
     * @param attributeName
737
     * @param type
738
     * @return
739
     */
740
    @SuppressWarnings("unchecked")
741
    private <T> T getServletContextAttribute(WebAppContext webAppContext, String attributeName, Class<T> type) {
742

    
743
        Context servletContext = webAppContext.getServletContext();
744
        Object value = servletContext.getAttribute(attributeName);
745
        if( value != null && type.isAssignableFrom(value.getClass())){
746

    
747
        }
748
        return (T) value;
749
    }
750

    
751
    public Server getServer() {
752
        return server;
753
    }
754
}
(1-1/5)