Revision 24841ac2
Added by Andreas Kohlbecker almost 11 years ago
src/main/java/eu/etaxonomy/cdm/server/Bootloader.java | ||
---|---|---|
28 | 28 |
import java.io.InputStreamReader; |
29 | 29 |
import java.io.OutputStream; |
30 | 30 |
import java.lang.management.ManagementFactory; |
31 |
import java.lang.reflect.InvocationTargetException; |
|
32 | 31 |
import java.net.URL; |
33 |
import java.sql.Connection; |
|
34 |
import java.sql.SQLException; |
|
35 | 32 |
import java.util.List; |
36 | 33 |
import java.util.Properties; |
37 | 34 |
|
38 |
import javax.naming.NamingException; |
|
39 |
import javax.sql.DataSource; |
|
40 |
|
|
41 | 35 |
import org.apache.commons.cli.CommandLine; |
42 | 36 |
import org.apache.commons.cli.CommandLineParser; |
43 | 37 |
import org.apache.commons.cli.GnuParser; |
... | ... | |
49 | 43 |
import org.apache.log4j.RollingFileAppender; |
50 | 44 |
import org.eclipse.jetty.jmx.MBeanContainer; |
51 | 45 |
import org.eclipse.jetty.security.HashLoginService; |
46 |
import org.eclipse.jetty.server.Handler; |
|
52 | 47 |
import org.eclipse.jetty.server.Server; |
48 |
import org.eclipse.jetty.server.handler.ContextHandler; |
|
53 | 49 |
import org.eclipse.jetty.server.handler.ContextHandlerCollection; |
54 | 50 |
import org.eclipse.jetty.util.log.Log; |
55 | 51 |
import org.eclipse.jetty.webapp.WebAppClassLoader; |
... | ... | |
58 | 54 |
import eu.etaxonomy.cdm.server.instance.CdmInstance; |
59 | 55 |
import eu.etaxonomy.cdm.server.instance.Configuration; |
60 | 56 |
import eu.etaxonomy.cdm.server.instance.InstanceManager; |
57 |
import eu.etaxonomy.cdm.server.instance.SharedAttributes; |
|
61 | 58 |
import eu.etaxonomy.cdm.server.instance.Status; |
62 | 59 |
import eu.etaxonomy.cdm.server.win32service.Win32Service; |
63 | 60 |
|
... | ... | |
108 | 105 |
private static final File DEFAULT_WEBAPP_TEMP_FOLDER = new File(TMP_PATH + DEFAULT_WEBAPP_WAR_NAME); |
109 | 106 |
private static final File CDM_WEBAPP_TEMP_FOLDER = new File(TMP_PATH + CDMLIB_REMOTE_WEBAPP); |
110 | 107 |
|
111 |
private static final String ATTRIBUTE_JDBC_JNDI_NAME = "cdm.jdbcJndiName"; |
|
112 |
public static final String ATTRIBUTE_DATASOURCE_NAME = "cdm.datasource"; |
|
113 |
private static final String ATTRIBUTE_CDM_LOGFILE = "cdm.logfile"; |
|
114 |
/** |
|
115 |
* same as in eu.etaxonomy.cdm.remote.config.DataSourceConfigurer |
|
116 |
*/ |
|
117 |
public static final String ATTRIBUTE_ERROR_MESSAGES = "cdm.errorMessages"; |
|
118 |
|
|
119 |
|
|
120 | 108 |
private final InstanceManager instanceManager = new InstanceManager(new File(USERHOME_CDM_LIBRARY_PATH, DATASOURCE_BEANDEF_FILE)); |
121 | 109 |
|
122 | 110 |
public List<CdmInstance> getCdmInstances() { |
... | ... | |
124 | 112 |
} |
125 | 113 |
|
126 | 114 |
public InstanceManager getInstanceManager(){ |
127 |
return instanceManager;
|
|
115 |
return instanceManager;
|
|
128 | 116 |
} |
129 | 117 |
|
130 | 118 |
private File cdmRemoteWebAppFile = null; |
... | ... | |
166 | 154 |
return answer; |
167 | 155 |
} |
168 | 156 |
|
169 |
private boolean bindJndiDataSource(CdmInstance instance) { |
|
170 |
try { |
|
171 |
Configuration conf = instance.getConfiguration(); |
|
172 |
Class<DataSource> dsCass = (Class<DataSource>) Thread.currentThread().getContextClassLoader().loadClass("com.mchange.v2.c3p0.ComboPooledDataSource"); |
|
173 |
DataSource datasource = dsCass.newInstance(); |
|
174 |
dsCass.getMethod("setDriverClass", new Class[] {String.class}).invoke(datasource, new Object[] {conf.getDriverClass()}); |
|
175 |
dsCass.getMethod("setJdbcUrl", new Class[] {String.class}).invoke(datasource, new Object[] {conf.getDataSourceUrl()}); |
|
176 |
dsCass.getMethod("setUser", new Class[] {String.class}).invoke(datasource, new Object[] {conf.getUsername()}); |
|
177 |
dsCass.getMethod("setPassword", new Class[] {String.class}).invoke(datasource, new Object[] {conf.getPassword()}); |
|
178 |
|
|
179 |
Connection connection = null; |
|
180 |
String sqlerror = null; |
|
181 |
try { |
|
182 |
connection = datasource.getConnection(); |
|
183 |
connection.close(); |
|
184 |
} catch (SQLException e) { |
|
185 |
sqlerror = e.getMessage() + "["+ e.getSQLState() + "]"; |
|
186 |
instance.getProblems().add(sqlerror); |
|
187 |
if(connection != null){ |
|
188 |
try {connection.close();} catch (SQLException e1) { /* IGNORE */ } |
|
189 |
} |
|
190 |
logger.error(conf.toString() + " has problem : "+ sqlerror ); |
|
191 |
} |
|
192 | 157 |
|
193 |
if(!instance.hasProblems()){ |
|
194 |
logger.info("binding jndi datasource at " + conf.getJdbcJndiName() + " with "+conf.getUsername() +"@"+ conf.getDataSourceUrl()); |
|
195 |
org.eclipse.jetty.plus.jndi.Resource jdbcResource = new org.eclipse.jetty.plus.jndi.Resource(conf.getJdbcJndiName(), datasource); |
|
196 |
return true; |
|
197 |
} |
|
198 |
|
|
199 |
} catch (IllegalArgumentException e) { |
|
200 |
logger.error(e); |
|
201 |
e.printStackTrace(); |
|
202 |
} catch (SecurityException e) { |
|
203 |
logger.error(e); |
|
204 |
} catch (ClassNotFoundException e) { |
|
205 |
logger.error(e); |
|
206 |
} catch (InstantiationException e) { |
|
207 |
logger.error(e); |
|
208 |
} catch (IllegalAccessException e) { |
|
209 |
logger.error(e); |
|
210 |
} catch (InvocationTargetException e) { |
|
211 |
logger.error(e); |
|
212 |
} catch (NoSuchMethodException e) { |
|
213 |
logger.error(e); |
|
214 |
} catch (NamingException e) { |
|
215 |
logger.error(e); |
|
216 |
} |
|
217 |
return false; |
|
218 |
} |
|
219 | 158 |
|
220 | 159 |
public void parseCommandOptions(String[] args) throws ParseException { |
221 | 160 |
CommandLineParser parser = new GnuParser(); |
... | ... | |
438 | 377 |
} |
439 | 378 |
|
440 | 379 |
public String readCdmRemoteVersion() throws IOException { |
441 |
String version = "cdmlib version unreadable";
|
|
380 |
String version = "cdmlib version unreadable";
|
|
442 | 381 |
InputStream versionInStream = Bootloader.class.getClassLoader().getResourceAsStream(VERSION_PROPERTIES_FILE); |
443 | 382 |
if (versionInStream != null){ |
444 |
Properties versionProperties = new Properties();
|
|
445 |
versionProperties.load(versionInStream);
|
|
446 |
version = versionProperties.getProperty(CDMLIB_REMOTE_WEBAPP_VERSION, version);
|
|
383 |
Properties versionProperties = new Properties();
|
|
384 |
versionProperties.load(versionInStream);
|
|
385 |
version = versionProperties.getProperty(CDMLIB_REMOTE_WEBAPP_VERSION, version);
|
|
447 | 386 |
} |
448 | 387 |
return version; |
449 | 388 |
} |
... | ... | |
495 | 434 |
} |
496 | 435 |
|
497 | 436 |
|
498 |
/** |
|
499 |
* @param conf |
|
500 |
* @param austostart |
|
501 |
* @return |
|
502 |
* @throws IOException |
|
503 |
*/ |
|
504 |
public WebAppContext addCdmInstanceContext(CdmInstance instance) throws IOException { |
|
505 |
Configuration conf = instance.getConfiguration(); |
|
506 |
if(!instance.isEnabled()){ |
|
507 |
logger.info(conf.getInstanceName() + " is disabled due to JVM memory limitations => skipping"); |
|
508 |
return null; |
|
509 |
} |
|
510 |
instance.setStatus(Status.initializing); |
|
511 |
logger.info("preparing WebAppContext for '"+ conf.getInstanceName() + "'"); |
|
512 |
WebAppContext cdmWebappContext = new WebAppContext(); |
|
513 |
|
|
514 |
cdmWebappContext.setContextPath("/"+conf.getInstanceName()); |
|
515 |
cdmWebappContext.setTempDirectory(CDM_WEBAPP_TEMP_FOLDER); |
|
516 |
|
|
517 |
if(!bindJndiDataSource(instance)){ |
|
518 |
// a problem with the datasource occurred skip this webapp |
|
519 |
cdmWebappContext = null; |
|
520 |
logger.error("a problem with the datasource occurred -> skipping /" + conf.getInstanceName()); |
|
521 |
instance.setStatus(Status.error); |
|
522 |
return cdmWebappContext; |
|
523 |
} |
|
524 |
|
|
525 |
cdmWebappContext.setAttribute(ATTRIBUTE_DATASOURCE_NAME, conf.getInstanceName()); |
|
526 |
cdmWebappContext.setAttribute(ATTRIBUTE_JDBC_JNDI_NAME, conf.getJdbcJndiName()); |
|
527 |
setWebApp(cdmWebappContext, cdmRemoteWebAppFile); |
|
528 |
|
|
529 |
cdmWebappContext.setAttribute(ATTRIBUTE_CDM_LOGFILE, |
|
530 |
logPath + File.separator + "cdm-" |
|
531 |
+ conf.getInstanceName() + ".log"); |
|
532 |
|
|
533 |
if(cdmRemoteWebAppFile.isDirectory() && isRunningFromCdmRemoteWebAppSource()){ |
|
534 |
|
|
535 |
/* |
|
536 |
* when running the webapp from {projectpath} src/main/webapp we |
|
537 |
* must assure that each web application is using it's own |
|
538 |
* classloader thus we tell the WebAppClassLoader where the |
|
539 |
* dependencies of the webapplication can be found. Otherwise |
|
540 |
* the system classloader would load these resources. |
|
541 |
*/ |
|
542 |
logger.info("Running webapp from source folder, thus adding java.class.path to WebAppClassLoader"); |
|
543 |
|
|
544 |
WebAppClassLoader classLoader = new WebAppClassLoader(cdmWebappContext); |
|
545 |
|
|
546 |
String classPath = System.getProperty("java.class.path"); |
|
547 |
classLoader.addClassPath(classPath); |
|
548 |
cdmWebappContext.setClassLoader(classLoader); |
|
549 |
} |
|
550 |
|
|
551 |
contexts.addHandler(cdmWebappContext); |
|
552 |
instance.setWebAppContext(cdmWebappContext); |
|
553 |
cdmWebappContext.addLifeCycleListener(instance); |
|
554 |
|
|
555 |
return cdmWebappContext; |
|
556 |
} |
|
437 |
/** |
|
438 |
* Adds a new WebAppContext to the contexts of the running jetty instance. |
|
439 |
* <ol> |
|
440 |
* <li>Initialize WebAppContext: |
|
441 |
* <ol> |
|
442 |
* <li>set context path</li> |
|
443 |
* <li>set tmp directory</li> |
|
444 |
* <li>bind JndiDataSource</li> |
|
445 |
* <li>set web app context attributes</li> |
|
446 |
* <li>create and setup individual classloader for the instance</li> |
|
447 |
* </ol> |
|
448 |
* </li> |
|
449 |
* <li> |
|
450 |
* finally add the new webappcontext to the contexts of the jetty instance</li> |
|
451 |
* </ol> |
|
452 |
* |
|
453 |
* @param instance |
|
454 |
* @return the instance given as parameter of null in case the instance has |
|
455 |
* {@link Status.#disabled} or if it is already added. |
|
456 |
* @throws IOException |
|
457 |
*/ |
|
458 |
public WebAppContext addCdmInstanceContext(CdmInstance instance) throws IOException { |
|
459 |
|
|
460 |
Configuration conf = instance.getConfiguration(); |
|
461 |
if(!instance.isEnabled()){ |
|
462 |
logger.info(conf.getInstanceName() + " is disabled, possibly due to JVM memory limitations"); |
|
463 |
return null; |
|
464 |
} |
|
465 |
if(getContextFor(conf) != null){ |
|
466 |
logger.info(conf.getInstanceName() + " is alreaddy added to the contexts - skipping"); |
|
467 |
return null; |
|
468 |
} |
|
469 |
|
|
470 |
instance.setStatus(Status.initializing); |
|
471 |
logger.info("preparing WebAppContext for '"+ conf.getInstanceName() + "'"); |
|
472 |
WebAppContext cdmWebappContext = new WebAppContext(); |
|
473 |
|
|
474 |
cdmWebappContext.setContextPath(constructContextPath(conf)); |
|
475 |
cdmWebappContext.setTempDirectory(CDM_WEBAPP_TEMP_FOLDER); |
|
476 |
|
|
477 |
if(!instance.bindJndiDataSource()){ |
|
478 |
// a problem with the datasource occurred skip this webapp |
|
479 |
cdmWebappContext = null; |
|
480 |
logger.error("a problem with the datasource occurred -> skipping /" + conf.getInstanceName()); |
|
481 |
instance.setStatus(Status.error); |
|
482 |
return cdmWebappContext; |
|
483 |
} |
|
484 |
|
|
485 |
cdmWebappContext.setAttribute(SharedAttributes.ATTRIBUTE_DATASOURCE_NAME, conf.getInstanceName()); |
|
486 |
cdmWebappContext.setAttribute(SharedAttributes.ATTRIBUTE_JDBC_JNDI_NAME, conf.getJdbcJndiName()); |
|
487 |
setWebApp(cdmWebappContext, getCdmRemoteWebAppFile()); |
|
488 |
|
|
489 |
cdmWebappContext.setAttribute(SharedAttributes.ATTRIBUTE_CDM_LOGFILE, |
|
490 |
logPath + File.separator + "cdm-" |
|
491 |
+ conf.getInstanceName() + ".log"); |
|
492 |
|
|
493 |
if( getCdmRemoteWebAppFile().isDirectory() && isRunningFromCdmRemoteWebAppSource()){ |
|
494 |
|
|
495 |
/* |
|
496 |
* when running the webapp from {projectpath} src/main/webapp we |
|
497 |
* must assure that each web application is using it's own |
|
498 |
* classloader thus we tell the WebAppClassLoader where the |
|
499 |
* dependencies of the webapplication can be found. Otherwise |
|
500 |
* the system classloader would load these resources. |
|
501 |
*/ |
|
502 |
String classPath = System.getProperty("java.class.path"); |
|
503 |
logger.info("Running webapp from source folder, thus adding system property 'java.class.path=" + classPath +"' to WebAppClassLoader"); |
|
504 |
WebAppClassLoader classLoader = new WebAppClassLoader(cdmWebappContext); |
|
505 |
classLoader.addClassPath(classPath); |
|
506 |
cdmWebappContext.setClassLoader(classLoader); |
|
507 |
} |
|
508 |
|
|
509 |
contexts.addHandler(cdmWebappContext); |
|
510 |
instance.setWebAppContext(cdmWebappContext); |
|
511 |
cdmWebappContext.addLifeCycleListener(instance); |
|
512 |
instance.setStatus(Status.stopped); |
|
513 |
|
|
514 |
return cdmWebappContext; |
|
515 |
} |
|
516 |
|
|
517 |
/** |
|
518 |
* @param conf |
|
519 |
* @return |
|
520 |
*/ |
|
521 |
private String constructContextPath(Configuration conf) { |
|
522 |
return "/" + conf.getInstanceName(); |
|
523 |
} |
|
524 |
|
|
525 |
/** |
|
526 |
* Removes the given instance from the contexts. If the instance is running |
|
527 |
* at the time of calling this method it will be stopped first. |
|
528 |
* The JndiDataSource and the webapplicationContext will be released and removed. |
|
529 |
* |
|
530 |
* @param instance the instance to be removed |
|
531 |
* |
|
532 |
* @throws Exception in case stopping the instance fails |
|
533 |
*/ |
|
534 |
public void removeCdmInstanceContext(CdmInstance instance) throws Exception { |
|
535 |
|
|
536 |
if(instance.getWebAppContext() != null){ |
|
537 |
if(instance.getWebAppContext().isRunning()){ |
|
538 |
try { |
|
539 |
instance.getWebAppContext().stop(); |
|
540 |
} catch (Exception e) { |
|
541 |
instance.getProblems().add("Error while stopping instance: " + e.getMessage()); |
|
542 |
throw e; |
|
543 |
} |
|
544 |
} |
|
545 |
contexts.removeHandler(instance.getWebAppContext()); |
|
546 |
instance.releaseWebAppContext(); |
|
547 |
} else { |
|
548 |
// maybe something went wrong before, try to find the potentially lost |
|
549 |
// contexts directly in the server |
|
550 |
ContextHandler handler = getContextFor(instance.getConfiguration()); |
|
551 |
if(handler != null){ |
|
552 |
contexts.removeHandler(handler); |
|
553 |
} |
|
554 |
} |
|
555 |
instance.unbindJndiDataSource(); |
|
556 |
} |
|
557 | 557 |
|
558 | 558 |
/** |
559 | 559 |
* Sets the webapp specified by the <code>webApplicationResource</code> to |
... | ... | |
586 | 586 |
public Server getServer() { |
587 | 587 |
return server; |
588 | 588 |
} |
589 |
} |
|
589 |
|
|
590 |
public ContextHandler getContextFor(Configuration conf) { |
|
591 |
return getContextFor(constructContextPath(conf)); |
|
592 |
} |
|
593 |
|
|
594 |
public ContextHandler getContextFor(String contextPath) { |
|
595 |
for( Handler handler : contexts.getHandlers()){ |
|
596 |
if(handler instanceof ContextHandler){ |
|
597 |
if(((ContextHandler)handler).getContextPath().equals(contextPath)){ |
|
598 |
return (ContextHandler)handler; |
|
599 |
} |
|
600 |
} |
|
601 |
} |
|
602 |
return null; |
|
603 |
} |
|
604 |
|
|
605 |
public ContextHandlerCollection getContexts() { |
|
606 |
return contexts; |
|
607 |
} |
|
608 |
|
|
609 |
/** |
|
610 |
* @return a File object pointing to the location of the cdmlib-remote-webapp |
|
611 |
*/ |
|
612 |
public File getCdmRemoteWebAppFile(){ |
|
613 |
if(cdmRemoteWebAppFile == null){ |
|
614 |
throw new RuntimeException("Invalid order of action. Server not yet started. The startServer() method must be called first. "); |
|
615 |
} |
|
616 |
return cdmRemoteWebAppFile; |
|
617 |
} |
|
618 |
} |
Also available in: Unified diff
start, stop of instances and reload of configuration implemented, see #3471 (cdmlib-remote-webapp instances can be stopped / started individually)