Project

General

Profile

Download (22.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

    
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.Enumeration;
32
import java.util.List;
33
import java.util.Set;
34

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

    
38
import org.apache.commons.cli.CommandLine;
39
import org.apache.commons.cli.CommandLineParser;
40
import org.apache.commons.cli.GnuParser;
41
import org.apache.commons.cli.HelpFormatter;
42
import org.apache.commons.cli.ParseException;
43
import org.apache.commons.io.FileUtils;
44
import org.apache.log4j.Logger;
45
import org.apache.log4j.PatternLayout;
46
import org.apache.log4j.RollingFileAppender;
47
import org.apache.tools.ant.types.CommandlineJava.SysProperties;
48
import org.eclipse.jetty.jmx.MBeanContainer;
49
import org.eclipse.jetty.server.Handler;
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
	//private static final String DEFAULT_WARFILE = "target/";
82

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

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

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

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

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

    
135

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

    
142
	private static final Logger logger = Logger.getLogger(Bootloader.class);
143
	
144
	private static final String DATASOURCE_BEANDEF_FILE = "datasources.xml";
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
    	setWebApp(defaultWebappContext, defaultWebAppFile);
436
        defaultWebappContext.setContextPath("/");
437
        defaultWebappContext.setTempDirectory(DEFAULT_WEBAPP_TEMP_FOLDER);
438
        // Important:
439
        // the defaultWebappContext MUST USE the super classloader 
440
        // otherwise the status page (index.jsp) might not work
441
        defaultWebappContext.setClassLoader(this.getClass().getClassLoader());
442
    	contexts.addHandler(defaultWebappContext);
443
    	
444
    	//
445
		// 2. cdm server contexts
446
    	//
447
    	server.addLifeCycleListener(new LifeCycle.Listener(){
448

    
449
			@Override
450
			public void lifeCycleFailure(LifeCycle event, Throwable cause) {
451
				logger.error("Jetty LifeCycleFailure", cause);
452
			}
453

    
454
			@Override
455
			public void lifeCycleStarted(LifeCycle event) {
456
				logger.info("cdmserver has started, now adding CDM server contexts");
457
				try {
458
					addCdmServerContexts(true);
459
				} catch (IOException e1) {
460
					logger.error(e1);
461
				}		
462
			}
463

    
464
			@Override
465
			public void lifeCycleStarting(LifeCycle event) {
466
			}
467

    
468
			@Override
469
			public void lifeCycleStopped(LifeCycle event) {
470
			}
471

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

    
509
	/**
510
	 * 
511
	 */
512
	private void verifyMemoryRequirements() {
513
		
514
		verifyMemoryRequirement("PermGenSpace", PERM_GEN_SPACE_PER_INSTANCE, JvmManager.getPermGenSpaceUsage().getMax());
515
		verifyMemoryRequirement("HeapSpace", HEAP_PER_INSTANCE, JvmManager.getHeapMemoryUsage().getMax());
516
		
517
	}
518

    
519
	private void verifyMemoryRequirement(String memoryName, long requiredSpacePerIntance, long availableSpace) {
520
		
521

    
522
		long requiredSpace = configAndStatusSet.size() * requiredSpacePerIntance;
523
		
524
		if(requiredSpace > availableSpace){
525
			
526
			String message = memoryName + " (" 
527
				+ (availableSpace / MB)  
528
				+ "MB) insufficient for " 
529
				+ configAndStatusSet.size()
530
				+ " instances. Increase " + memoryName + " by " 
531
				+ ((requiredSpace - availableSpace)/MB) 
532
				+ "MB";
533
				;
534
			logger.error(message + " => disabling some instances!!!");
535
			
536
			// disabling some instances 
537
			int i=0;
538
			for(CdmInstanceProperties instanceProps : configAndStatusSet){
539
				i++;
540
				if(i * requiredSpacePerIntance > availableSpace){
541
					instanceProps.setStatus(Status.disabled);
542
					instanceProps.getProblems().add("Disbled due to: " + message);
543
				}
544
			}
545
		}
546
	}
547

    
548
	/**
549
	 * Configures and adds a {@link RollingFileAppender} to the root logger
550
	 * 
551
	 * The log files of the cdm-remote instances are configured by the 
552
	 * {@link eu.etaxonomy.cdm.remote.config.LoggingConfigurer}
553
	 */
554
	private void configureFileLogger() {
555

    
556
		PatternLayout layout = new PatternLayout("%d %p [%c] - %m%n");
557
		try {
558
			String logFile = LOG_PATH + File.separator + "cdmserver.log";
559
			RollingFileAppender appender = new RollingFileAppender(layout, logFile);
560
			appender.setMaxBackupIndex(3);
561
			appender.setMaxFileSize("2MB");
562
			Logger.getRootLogger().addAppender(appender);
563
			logger.info("logging to :" + logFile);
564
		} catch (IOException e) {
565
			logger.error("Creating RollingFileAppender failed:", e);
566
		}
567
	}
568

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

    
611
        		WebAppClassLoader classLoader = new WebAppClassLoader(cdmWebappContext);
612
	        	
613
	        	String classPath = System.getProperty("java.class.path");
614
	        	classLoader.addClassPath(classPath);
615
	        	cdmWebappContext.setClassLoader(classLoader);
616
        	}
617

    
618
	        cdmWebappContext.addLifeCycleListener(new WebAppContextListener(cdmWebappContext));
619
	        contexts.addHandler(cdmWebappContext);  
620
	        
621
	        if(austostart){
622
		        try {
623
		        	conf.setStatus(CdmInstanceProperties.Status.starting);
624
					cdmWebappContext.start();
625
					if(!conf.getStatus().equals(Status.error)){
626
						conf.setStatus(CdmInstanceProperties.Status.started);
627
					}
628
				} catch (Exception e) {
629
					logger.error("Could not start " + cdmWebappContext.getContextPath());
630
					conf.setStatus(CdmInstanceProperties.Status.error);
631
				}
632
	        }
633

    
634
        }
635
	}
636

    
637
	/**
638
	 * @param context
639
	 * @param webApplicationResource
640
	 */
641
	private void setWebApp(WebAppContext context, File webApplicationResource) {
642
		if(webApplicationResource.isDirectory()){
643
			context.setResourceBase(webApplicationResource.getAbsolutePath());
644
			logger.debug("setting directory " + webApplicationResource.getAbsolutePath() + " as webapplication");
645
		} else {
646
			context.setWar(webApplicationResource.getAbsolutePath());
647
			logger.debug("setting war file " + webApplicationResource.getAbsolutePath() + " as webapplication");
648
		}
649
	}
650

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