Project

General

Profile

Download (23 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

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

    
34
import javax.naming.NamingException;
35
import javax.sql.DataSource;
36

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

    
57
import eu.etaxonomy.cdm.server.CdmInstanceProperties.Status;
58
import eu.etaxonomy.cdm.server.win32service.Win32Service;
59

    
60

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

    
81
	/**
82
	 * @author a.kohlbecker
83
	 * @date 03.02.2011
84
	 *
85
	 */
86
	private class WebAppContextListener implements Listener {
87
		
88
		WebAppContext cdmWebappContext;
89
		/**
90
		 * @param cdmWebappContext
91
		 */
92
		public WebAppContextListener(WebAppContext cdmWebappContext) {
93
			this.cdmWebappContext = cdmWebappContext;
94
		}
95

    
96
		@Override
97
		public void lifeCycleStopping(LifeCycle event) {
98
			logger.error("lifeCycleStopping");
99
		}
100

    
101
		@Override
102
		public void lifeCycleStopped(LifeCycle event) {
103
			logger.error("lifeCycleStopped");
104
			
105
		}
106

    
107
		@Override
108
		public void lifeCycleStarting(LifeCycle event) {
109
			logger.error("lifeCycleStarting");
110
		}
111

    
112
		@SuppressWarnings("unchecked")
113
		@Override
114
		public void lifeCycleStarted(LifeCycle event) {
115
			logger.error("lifeCycleStarted");
116
			
117
			List<String> messages = getServletContextAttribute(cdmWebappContext, ATTRIBUTE_ERROR_MESSAGES, List.class);
118
			String dataSourceName = getServletContextAttribute(cdmWebappContext, ATTRIBUTE_DATASOURCE_NAME, String.class);
119
			
120
			if(messages != null && dataSourceName != null){
121
				CdmInstanceProperties configAndStatus = findConfigAndStatusFor(dataSourceName);
122
				configAndStatus.getProblems().addAll(messages);
123
				configAndStatus.setStatus(Status.error);
124
				try {
125
					logger.warn("Stopping context '" + dataSourceName + "' due to errors reported in ServletContext");
126
					cdmWebappContext.stop();
127
				} catch (Exception e) {
128
					logger.error(e);
129
				}
130
			} 
131
		}
132
		 
133

    
134
		@Override
135
		public void lifeCycleFailure(LifeCycle event, Throwable cause) {
136
			logger.error("lifeCycleFailure");
137
		}
138
	}
139

    
140
	private static final Logger logger = Logger.getLogger(Bootloader.class);
141
	
142
	private static final String DATASOURCE_BEANDEF_FILE = "datasources.xml";
143
	private static final String REALM_PROPERTIES_FILE = "cdm-server-realm.properties";
144
	
145
	private static final String USERHOME_CDM_LIBRARY_PATH = System.getProperty("user.home")+File.separator+".cdmLibrary"+File.separator;
146
	private static final String TMP_PATH = USERHOME_CDM_LIBRARY_PATH + "server" + File.separator;
147
	private static final String LOG_PATH = USERHOME_CDM_LIBRARY_PATH + "log" + File.separator;
148
	
149
	private static final String APPLICATION_NAME = "CDM Server";
150
    private static final String WAR_POSTFIX = ".war";
151
    
152
    private static final String CDM_WEBAPP_WAR_NAME = "cdmserver";
153
    private static final String DEFAULT_WEBAPP_WAR_NAME = "default-webapp";
154
    private static final File DEFAULT_WEBAPP_TEMP_FOLDER = new File(TMP_PATH + DEFAULT_WEBAPP_WAR_NAME);
155
    private static final File CDM_WEBAPP_TEMP_FOLDER = new File(TMP_PATH + CDM_WEBAPP_WAR_NAME);
156
    
157
    private static final String ATTRIBUTE_JDBC_JNDI_NAME = "cdm.jdbcJndiName";
158
    private static final String ATTRIBUTE_DATASOURCE_NAME = "cdm.datasource";
159
    private static final String ATTRIBUTE_CDM_LOGFILE = "cdm.logfile";
160
    /**
161
     * same as in eu.etaxonomy.cdm.remote.config.DataSourceConfigurer
162
     */
163
    private static final String ATTRIBUTE_ERROR_MESSAGES = "cdm.errorMessages";
164

    
165
    
166
    // memory requirements
167
    private static final long MB = 1024 * 1024;
168
    private static final long PERM_GEN_SPACE_PER_INSTANCE = 64 * MB;
169
    private static final long HEAP_PER_INSTANCE = 300 * MB;
170
    
171
    private static final int KB = 1024;
172
    
173
    
174
    private Set<CdmInstanceProperties> configAndStatusSet = null;
175
    
176
    public Set<CdmInstanceProperties> getConfigAndStatus() {
177
		return configAndStatusSet;
178
	}
179

    
180
	private File webappFile = null;
181
    private File defaultWebAppFile = null;
182
    
183
    private Server server = null;
184
    private ContextHandlerCollection contexts = new ContextHandlerCollection();
185
    
186
    private CommandLine cmdLine;
187
    
188
    /* thread save singleton implementation */
189

    
190
	private static Bootloader instance = new Bootloader();
191

    
192
    private Bootloader() {}
193
    
194
    public synchronized static Bootloader getBootloader(){
195
    	return instance;
196
    }
197
    
198
    /* end of singleton implementation */
199
    
200
    private Set<CdmInstanceProperties> loadDataSources(){
201
    	if(configAndStatusSet == null){
202
    		File datasourcesFile = new File(USERHOME_CDM_LIBRARY_PATH, DATASOURCE_BEANDEF_FILE); 
203
    		configAndStatusSet = DataSourcePropertyParser.parseDataSourceConfigs(datasourcesFile);
204
        	logger.info("cdm server instance names loaded: "+ configAndStatusSet.toString());
205
    	}
206
    	return configAndStatusSet;
207
    }
208

    
209
    public int writeStreamTo(final InputStream input, final OutputStream output, int bufferSize) throws IOException {
210
        int available = Math.min(input.available(), 256 * KB);
211
        byte[] buffer = new byte[Math.max(bufferSize, available)];
212
        int answer = 0;
213
        int count = input.read(buffer);
214
        while (count >= 0) {
215
            output.write(buffer, 0, count);
216
            answer += count;
217
            count = input.read(buffer);
218
        }
219
        return answer;
220
    }
221

    
222
	private boolean bindJndiDataSource(CdmInstanceProperties conf) {
223
		try {
224
			Class<DataSource> dsCass = (Class<DataSource>) Thread.currentThread().getContextClassLoader().loadClass("com.mchange.v2.c3p0.ComboPooledDataSource");
225
			DataSource datasource = dsCass.newInstance();
226
			dsCass.getMethod("setDriverClass", new Class[] {String.class}).invoke(datasource, new Object[] {conf.getDriverClass()});
227
			dsCass.getMethod("setJdbcUrl", new Class[] {String.class}).invoke(datasource, new Object[] {conf.getUrl()});
228
			dsCass.getMethod("setUser", new Class[] {String.class}).invoke(datasource, new Object[] {conf.getUsername()});
229
			dsCass.getMethod("setPassword", new Class[] {String.class}).invoke(datasource, new Object[] {conf.getPassword()});
230
		 
231
			Connection connection = null;
232
			String sqlerror = null;
233
			try {
234
				connection = datasource.getConnection();
235
				connection.close();
236
			} catch (SQLException e) {
237
				sqlerror = e.getMessage() + "["+ e.getSQLState() + "]";
238
				conf.getProblems().add(sqlerror);
239
				if(connection !=  null){
240
					try {connection.close();} catch (SQLException e1) { /* IGNORE */ }
241
				}
242
				logger.error(conf.toString() + " has problem : "+ sqlerror );
243
			}
244
			
245
			if(!conf.hasProblems()){
246
				logger.info("binding jndi datasource at " + conf.getJdbcJndiName() + " with "+conf.getUsername() +"@"+ conf.getUrl());
247
				org.eclipse.jetty.plus.jndi.Resource jdbcResource = new org.eclipse.jetty.plus.jndi.Resource(conf.getJdbcJndiName(), datasource);
248
				return true;				
249
			}
250
			
251
		} catch (IllegalArgumentException e) {
252
			logger.error(e);
253
			e.printStackTrace();
254
		} catch (SecurityException e) {
255
			logger.error(e);
256
		} catch (ClassNotFoundException e) {
257
			logger.error(e);
258
		} catch (InstantiationException e) {
259
			logger.error(e);
260
		} catch (IllegalAccessException e) {
261
			logger.error(e);
262
		} catch (InvocationTargetException e) {
263
			logger.error(e);
264
		} catch (NoSuchMethodException e) {
265
			logger.error(e);
266
		} catch (NamingException e) {
267
			logger.error(e);
268
		}
269
		return false;
270
	}
271
	
272
	private void parseCommandOptions(String[] args) throws ParseException {
273
		CommandLineParser parser = new GnuParser();
274
		cmdLine = parser.parse( CommandOptions.getOptions(), args );
275
		
276
		 // print the help message
277
		 if(cmdLine.hasOption(HELP.getOpt())){
278
			 HelpFormatter formatter = new HelpFormatter();
279
			 formatter.printHelp( "java .. ", CommandOptions.getOptions() );
280
			 System.exit(0);
281
		 }
282
	}
283

    
284

    
285
	private File extractWar(String warName) throws IOException, FileNotFoundException {
286
		ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
287
		String warFileName = warName + WAR_POSTFIX;
288

    
289
		// 1. find in classpath
290
		URL resource = classLoader.getResource(warFileName);
291
    	if (resource == null) {
292
    		logger.error("Could not find the " + warFileName + " on classpath!");
293
    		
294
    		File pomxml = new File("pom.xml");
295
    		if(pomxml.exists()){
296
    			// 2. try finding in target folder of maven project
297
    			File warFile = new File("target" + File.separator + warFileName);
298
    			logger.debug("looging for war file at " + warFile.getAbsolutePath());
299
    			if (warFile.canRead()) {
300
    				resource = warFile.toURI().toURL();
301
    			} else {
302
    				logger.error("Also could not find the " + warFileName + " in maven project, try excuting 'mvn install'");
303
    			}
304
    		}	
305
    	}
306
    	
307
    	if (resource == null) {
308
    		// no way finding the war file :-(
309
    		System.exit(1);    		
310
    	}
311
    	
312
    	
313
    	File warFile = new File(TMP_PATH, warName + "-" + WAR_POSTFIX);
314
    	logger.info("Extracting " + warFileName + " to " + warFile + " ...");
315
    	
316
    	writeStreamTo(resource.openStream(), new FileOutputStream(warFile), 8 * KB);
317
    	
318
    	logger.info("Extracted " + warFileName);
319
		return warFile;
320
	}
321
    
322
    
323
	/**
324
	 * MAIN METHOD
325
	 * 
326
	 * @param args
327
	 * @throws Exception
328
	 */
329
	public static void main(String[] args) throws Exception {
330
	
331
		Bootloader bootloader = Bootloader.getBootloader();
332
    	
333
		bootloader.parseCommandOptions(args);
334
		
335
		bootloader.startServer();
336
    }
337

    
338
	private void startServer() throws IOException,
339
			FileNotFoundException, Exception, InterruptedException {
340
		
341
		
342
		//assure LOG_PATH exists
343
		File logPath = new File(LOG_PATH);
344
		if(!logPath.exists()){
345
			FileUtils.forceMkdir(new File(LOG_PATH));
346
		}
347
		
348
		//append logger
349
		configureFileLogger();
350
		
351
		logger.info("Starting "+APPLICATION_NAME);
352
		logger.info("Using  " + System.getProperty("user.home") + " as home directory. Can be specified by -Duser.home=<FOLDER>");
353
    	
354
    	//assure TMP_PATH exists and clean it up
355
    	File tempDir = new File(TMP_PATH);
356
    	if(!tempDir.exists() && !tempDir.mkdirs()){
357
    		logger.error("Error creating temporary directory for webapplications " + tempDir.getAbsolutePath());
358
    		System.exit(-1);
359
    	} else {
360
    		if(FileUtils.deleteQuietly(tempDir)){
361
    			tempDir.mkdirs();
362
    			logger.info("Old webapplications successfully cleared");
363
    		}
364
    	}
365
    	tempDir = null;
366
    	 
367
    	
368
    	 // WARFILE
369
    	 if(cmdLine.hasOption(WEBAPP.getOpt())){
370
    		 webappFile = new File(cmdLine.getOptionValue(WEBAPP.getOpt()));
371
    		 if(webappFile.isDirectory()){
372
    			 logger.info("using user defined web application folder: " + webappFile.getAbsolutePath());    			     			 
373
    		 } else {
374
    			 logger.info("using user defined warfile: " + webappFile.getAbsolutePath());
375
    		 }
376
    		 if(isRunningFromSource()){
377
    			 //FIXME check if all local paths are valid !!!!
378
    	    	defaultWebAppFile = new File("./src/main/webapp");	
379
    	    	
380
    	     } else {
381
    	    	defaultWebAppFile = extractWar(DEFAULT_WEBAPP_WAR_NAME);
382
    	     }
383
    	 } else {    	 
384
    		 webappFile = extractWar(CDM_WEBAPP_WAR_NAME);
385
    		 defaultWebAppFile = extractWar(DEFAULT_WEBAPP_WAR_NAME);
386
    	 }
387
    	 
388
    	 // HTTP Port
389
    	 int httpPort = 8080;
390
    	 if(cmdLine.hasOption(HTTP_PORT.getOpt())){
391
    		 try {
392
				httpPort = Integer.parseInt(cmdLine.getOptionValue(HTTP_PORT.getOpt()));
393
				logger.info(HTTP_PORT.getOpt()+" set to "+cmdLine.getOptionValue(HTTP_PORT.getOpt()));
394
			} catch (NumberFormatException e) {
395
				logger.error("Supplied portnumber is not an integer");
396
				System.exit(-1);
397
			}
398
    	 }
399
    	 
400
    	 if(cmdLine.hasOption(DATASOURCES_FILE.getOpt())){
401
    		 logger.error(DATASOURCES_FILE.getOpt() + " NOT JET IMPLEMENTED!!!");
402
    	 }
403
    	
404
    	loadDataSources();
405
    	
406
    	verifyMemoryRequirements();
407
    	
408
    	
409
		server = new Server(httpPort);
410
		
411
		// JMX support
412
		if(cmdLine.hasOption(JMX.getOpt())){
413
			logger.info("adding JMX support ...");
414
			MBeanContainer mBeanContainer = new MBeanContainer(ManagementFactory.getPlatformMBeanServer());
415
			server.getContainer().addEventListener(mBeanContainer);
416
			mBeanContainer.addBean(Log.getLog());
417
			mBeanContainer.start();
418
		}
419
		
420
		if(cmdLine.hasOption(WIN32SERVICE.getOpt())){
421
			Win32Service win32Service = new Win32Service();
422
			win32Service.setServer(server);
423
			server.setStopAtShutdown(true);
424
			server.addBean(win32Service);
425
		}
426
		
427
		// add servelet contexts
428
		
429
		
430
		//
431
		// 1. default context
432
		//
433
		logger.info("preparing default WebAppContext");
434
    	WebAppContext defaultWebappContext = new WebAppContext();
435
    	
436
    	setWebApp(defaultWebappContext, defaultWebAppFile);
437
        defaultWebappContext.setContextPath("/");
438
        defaultWebappContext.setTempDirectory(DEFAULT_WEBAPP_TEMP_FOLDER);
439
        
440
		// configure security context
441
        // see for reference * http://docs.codehaus.org/display/JETTY/Realms
442
        //                   * http://wiki.eclipse.org/Jetty/Starting/Porting_to_Jetty_7
443
        HashLoginService loginService = new HashLoginService();
444
        loginService.setConfig(USERHOME_CDM_LIBRARY_PATH + REALM_PROPERTIES_FILE);
445
        defaultWebappContext.getSecurityHandler().setLoginService(loginService);
446
        
447
        // Important:
448
        // the defaultWebappContext MUST USE the super classloader 
449
        // otherwise the status page (index.jsp) might not work
450
        defaultWebappContext.setClassLoader(this.getClass().getClassLoader());
451
    	contexts.addHandler(defaultWebappContext);
452
    	
453
    	//
454
		// 2. cdm server contexts
455
    	//
456
    	server.addLifeCycleListener(new LifeCycle.Listener(){
457

    
458
			@Override
459
			public void lifeCycleFailure(LifeCycle event, Throwable cause) {
460
				logger.error("Jetty LifeCycleFailure", cause);
461
			}
462

    
463
			@Override
464
			public void lifeCycleStarted(LifeCycle event) {
465
				logger.info("cdmserver has started, now adding CDM server contexts");
466
				try {
467
					addCdmServerContexts(true);
468
				} catch (IOException e1) {
469
					logger.error(e1);
470
				}		
471
			}
472

    
473
			@Override
474
			public void lifeCycleStarting(LifeCycle event) {
475
			}
476

    
477
			@Override
478
			public void lifeCycleStopped(LifeCycle event) {
479
			}
480

    
481
			@Override
482
			public void lifeCycleStopping(LifeCycle event) {
483
			}
484
			
485
			});
486
        
487
        
488
        logger.info("setting contexts ...");
489
        server.setHandler(contexts);
490
        logger.info("starting jetty ...");
491
//        try {
492
        	
493
        	server.start();
494
        	
495
//        } catch(org.springframework.beans.BeansException e){
496
//        	Throwable rootCause = null;
497
//        	while(e.getCause() != null){
498
//        		rootCause = e.getCause();
499
//        	}
500
//        	if(rootCause != null && rootCause.getClass().getSimpleName().equals("InvalidCdmVersionException")){
501
//        		
502
//        		logger.error("rootCause ----------->" + rootCause.getMessage());
503
////        		for(CdmInstanceProperties props : configAndStatus){
504
////        			if(props.getDataSourceName())
505
////        		}
506
//        	}
507
//        }
508
        
509
        if(cmdLine.hasOption(WIN32SERVICE.getOpt())){
510
        	logger.info("jetty has started as win32 service");
511
        } else {
512
        	server.join();
513
	        logger.info(APPLICATION_NAME+" stopped.");
514
	    	System.exit(0);
515
        }
516
	}
517

    
518
	/**
519
	 * 
520
	 */
521
	private void verifyMemoryRequirements() {
522
		
523
		verifyMemoryRequirement("PermGenSpace", PERM_GEN_SPACE_PER_INSTANCE, JvmManager.getPermGenSpaceUsage().getMax());
524
		verifyMemoryRequirement("HeapSpace", HEAP_PER_INSTANCE, JvmManager.getHeapMemoryUsage().getMax());
525
		
526
	}
527

    
528
	private void verifyMemoryRequirement(String memoryName, long requiredSpacePerIntance, long availableSpace) {
529
		
530

    
531
		long requiredSpace = configAndStatusSet.size() * requiredSpacePerIntance;
532
		
533
		if(requiredSpace > availableSpace){
534
			
535
			String message = memoryName + " (" 
536
				+ (availableSpace / MB)  
537
				+ "MB) insufficient for " 
538
				+ configAndStatusSet.size()
539
				+ " instances. Increase " + memoryName + " by " 
540
				+ ((requiredSpace - availableSpace)/MB) 
541
				+ "MB";
542
				;
543
			logger.error(message + " => disabling some instances!!!");
544
			
545
			// disabling some instances 
546
			int i=0;
547
			for(CdmInstanceProperties instanceProps : configAndStatusSet){
548
				i++;
549
				if(i * requiredSpacePerIntance > availableSpace){
550
					instanceProps.setStatus(Status.disabled);
551
					instanceProps.getProblems().add("Disbled due to: " + message);
552
				}
553
			}
554
		}
555
	}
556

    
557
	/**
558
	 * Configures and adds a {@link RollingFileAppender} to the root logger
559
	 * 
560
	 * The log files of the cdm-remote instances are configured by the 
561
	 * {@link eu.etaxonomy.cdm.remote.config.LoggingConfigurer}
562
	 */
563
	private void configureFileLogger() {
564

    
565
		PatternLayout layout = new PatternLayout("%d %p [%c] - %m%n");
566
		try {
567
			String logFile = LOG_PATH + File.separator + "cdmserver.log";
568
			RollingFileAppender appender = new RollingFileAppender(layout, logFile);
569
			appender.setMaxBackupIndex(3);
570
			appender.setMaxFileSize("2MB");
571
			Logger.getRootLogger().addAppender(appender);
572
			logger.info("logging to :" + logFile);
573
		} catch (IOException e) {
574
			logger.error("Creating RollingFileAppender failed:", e);
575
		}
576
	}
577

    
578
	private void addCdmServerContexts(boolean austostart) throws IOException {
579
		
580
		for(CdmInstanceProperties conf : configAndStatusSet){
581
			
582
			if(!conf.isEnabled()){
583
				logger.info(conf.getDataSourceName() + " is disabled => skipping");
584
				continue;
585
			}
586
			conf.setStatus(CdmInstanceProperties.Status.initializing);
587
        	logger.info("preparing WebAppContext for '"+ conf.getDataSourceName() + "'");
588
        	WebAppContext cdmWebappContext = new WebAppContext();
589
         
590
	        cdmWebappContext.setContextPath("/"+conf.getDataSourceName());
591
	        cdmWebappContext.setTempDirectory(CDM_WEBAPP_TEMP_FOLDER);
592
	        
593
            if(!bindJndiDataSource(conf)){
594
            	// a problem with the datasource occurred skip this webapp
595
            	cdmWebappContext = null;
596
            	logger.error("a problem with the datasource occurred -> skipping /" + conf.getDataSourceName());
597
				conf.setStatus(CdmInstanceProperties.Status.error);
598
            	continue;
599
            }
600
            
601
            cdmWebappContext.setAttribute(ATTRIBUTE_DATASOURCE_NAME, conf.getDataSourceName());
602
            cdmWebappContext.setAttribute(ATTRIBUTE_JDBC_JNDI_NAME, conf.getJdbcJndiName());
603
	        setWebApp(cdmWebappContext, webappFile);
604
	        
605
			cdmWebappContext.setAttribute(ATTRIBUTE_CDM_LOGFILE,
606
					LOG_PATH + File.separator + "cdm-"
607
							+ conf.getDataSourceName() + ".log");
608
   
609
	        if(webappFile.isDirectory() && isRunningFromSource()){
610
        		
611
				/*
612
				 * when running the webapp from {projectpath} src/main/webapp we
613
				 * must assure that each web application is using it's own
614
				 * classloader thus we tell the WebAppClassLoader where the
615
				 * dependencies of the webapplication can be found. Otherwise
616
				 * the system classloader would load these resources.
617
				 */
618
        		logger.info("Running webapp from source folder, thus adding java.class.path to WebAppClassLoader");
619

    
620
        		WebAppClassLoader classLoader = new WebAppClassLoader(cdmWebappContext);
621
	        	
622
	        	String classPath = System.getProperty("java.class.path");
623
	        	classLoader.addClassPath(classPath);
624
	        	cdmWebappContext.setClassLoader(classLoader);
625
        	}
626

    
627
	        cdmWebappContext.addLifeCycleListener(new WebAppContextListener(cdmWebappContext));
628
	        contexts.addHandler(cdmWebappContext);  
629
	        
630
	        if(austostart){
631
		        try {
632
		        	conf.setStatus(CdmInstanceProperties.Status.starting);
633
					cdmWebappContext.start();
634
					if(!conf.getStatus().equals(Status.error)){
635
						conf.setStatus(CdmInstanceProperties.Status.started);
636
					}
637
				} catch (Exception e) {
638
					logger.error("Could not start " + cdmWebappContext.getContextPath());
639
					conf.setStatus(CdmInstanceProperties.Status.error);
640
				}
641
	        }
642

    
643
        }
644
	}
645

    
646
	/**
647
	 * @param context
648
	 * @param webApplicationResource
649
	 */
650
	private void setWebApp(WebAppContext context, File webApplicationResource) {
651
		if(webApplicationResource.isDirectory()){
652
			context.setResourceBase(webApplicationResource.getAbsolutePath());
653
			logger.debug("setting directory " + webApplicationResource.getAbsolutePath() + " as webapplication");
654
		} else {
655
			context.setWar(webApplicationResource.getAbsolutePath());
656
			logger.debug("setting war file " + webApplicationResource.getAbsolutePath() + " as webapplication");
657
		}
658
	}
659

    
660
	/**
661
	 * @return
662
	 */
663
	private boolean isRunningFromSource() {
664
		String webappPathNormalized = webappFile.getAbsolutePath().replace('\\', '/');
665
		return webappPathNormalized.endsWith("src/main/webapp") || webappPathNormalized.endsWith("cdmlib-remote/target/cdmserver");
666
	}
667
	
668
	/**
669
	 * @param dataSourceName
670
	 * @return
671
	 */
672
	private CdmInstanceProperties findConfigAndStatusFor(String dataSourceName){
673
		for(CdmInstanceProperties props : configAndStatusSet){
674
			if(props.getDataSourceName().equals(dataSourceName)){
675
				return props;
676
			}
677
		}
678
		return null;
679
	}
680
	
681
	/**
682
	 * @param <T>
683
	 * @param webAppContext
684
	 * @param attributeName
685
	 * @param type
686
	 * @return
687
	 */
688
	@SuppressWarnings("unchecked")
689
	private <T> T getServletContextAttribute(WebAppContext webAppContext, String attributeName, Class<T> type) {
690
		
691
		Context servletContext = webAppContext.getServletContext();
692
		Object value = servletContext.getAttribute(attributeName);
693
		if( value != null && type.isAssignableFrom(value.getClass())){	
694
			
695
		}
696
		return (T) value;
697
	}
698
}
(1-1/5)