2006-11-06 05:16:01 +08:00
package hudson ;
import com.thoughtworks.xstream.converters.reflection.PureJavaReflectionProvider ;
import com.thoughtworks.xstream.core.JVM ;
import hudson.model.Hudson ;
import hudson.model.User ;
2007-08-02 01:05:31 +08:00
import hudson.triggers.SafeTimerTask ;
2008-02-08 14:07:04 +08:00
import hudson.triggers.Trigger ;
import hudson.util.HudsonIsLoading ;
import hudson.util.IncompatibleServletVersionDetected ;
import hudson.util.IncompatibleVMDetected ;
import hudson.util.InsufficientPermissionDetected ;
import hudson.util.NoHomeDir ;
import hudson.util.RingBufferLogHandler ;
import hudson.util.NoTempDir ;
import org.jvnet.localizer.LocaleProvider ;
import org.kohsuke.stapler.Stapler ;
import org.kohsuke.stapler.StaplerRequest ;
2006-11-06 05:16:01 +08:00
import javax.naming.Context ;
import javax.naming.InitialContext ;
import javax.naming.NamingException ;
import javax.servlet.ServletContext ;
import javax.servlet.ServletContextEvent ;
import javax.servlet.ServletContextListener ;
2006-12-10 03:30:15 +08:00
import javax.servlet.ServletResponse ;
2006-11-06 05:16:01 +08:00
import javax.xml.transform.TransformerFactory ;
import javax.xml.transform.TransformerFactoryConfigurationError ;
import java.io.File ;
import java.io.IOException ;
import java.io.InputStream ;
2008-02-08 14:07:04 +08:00
import java.net.URL ;
import java.net.URLClassLoader ;
2007-12-27 08:46:21 +08:00
import java.util.Locale ;
2008-02-08 14:07:04 +08:00
import java.util.Properties ;
2006-11-06 05:16:01 +08:00
import java.util.logging.Level ;
2006-12-30 03:15:53 +08:00
import java.util.logging.Logger ;
2007-12-20 14:35:28 +08:00
2006-11-06 05:16:01 +08:00
/ * *
* Entry point when Hudson is used as a webapp .
*
* @author Kohsuke Kawaguchi
* /
public class WebAppMain implements ServletContextListener {
2007-04-06 22:20:13 +08:00
private final RingBufferLogHandler handler = new RingBufferLogHandler ( ) ;
2007-11-02 06:14:22 +08:00
private static final String APP = " app " ;
2006-11-06 05:16:01 +08:00
/ * *
* Creates the sole instance of { @link Hudson } and register it to the { @link ServletContext } .
* /
public void contextInitialized ( ServletContextEvent event ) {
2007-08-10 12:36:48 +08:00
try {
2007-10-31 10:14:53 +08:00
final ServletContext context = event . getServletContext ( ) ;
2007-08-10 12:36:48 +08:00
2007-12-27 08:46:21 +08:00
// use the current request to determine the language
LocaleProvider . setProvider ( new LocaleProvider ( ) {
public Locale get ( ) {
Locale locale = null ;
StaplerRequest req = Stapler . getCurrentRequest ( ) ;
if ( req ! = null )
locale = req . getLocale ( ) ;
if ( locale = = null )
locale = Locale . getDefault ( ) ;
return locale ;
}
} ) ;
2007-10-31 10:14:53 +08:00
// quick check to see if we (seem to) have enough permissions to run. (see #719)
JVM jvm ;
try {
jvm = new JVM ( ) ;
new URLClassLoader ( new URL [ 0 ] , getClass ( ) . getClassLoader ( ) ) ;
} catch ( SecurityException e ) {
2007-11-02 06:14:22 +08:00
context . setAttribute ( APP , new InsufficientPermissionDetected ( e ) ) ;
2007-10-31 10:14:53 +08:00
return ;
}
2007-08-10 12:36:48 +08:00
2007-10-31 10:14:53 +08:00
installLogger ( ) ;
2006-11-06 05:16:01 +08:00
2007-10-31 10:14:53 +08:00
final File home = getHomeDir ( event ) . getAbsoluteFile ( ) ;
home . mkdirs ( ) ;
System . out . println ( " hudson home directory: " + home ) ;
2006-11-06 05:16:01 +08:00
2007-10-31 10:14:53 +08:00
// check that home exists (as mkdirs could have failed silently), otherwise throw a meaningful error
if ( ! home . exists ( ) ) {
2007-11-02 06:14:22 +08:00
context . setAttribute ( APP , new NoHomeDir ( home ) ) ;
2007-10-31 10:14:53 +08:00
return ;
}
2006-11-06 05:16:01 +08:00
2007-10-31 10:14:53 +08:00
// make sure that we are using XStream in the "enhanced" (JVM-specific) mode
if ( jvm . bestReflectionProvider ( ) . getClass ( ) = = PureJavaReflectionProvider . class ) {
// nope
2007-11-02 06:14:22 +08:00
context . setAttribute ( APP , new IncompatibleVMDetected ( ) ) ;
2007-10-31 10:14:53 +08:00
return ;
}
2006-12-10 03:30:15 +08:00
2007-10-31 10:14:53 +08:00
// make sure this is servlet 2.4 container or above
2006-11-06 05:16:01 +08:00
try {
2007-10-31 10:14:53 +08:00
ServletResponse . class . getMethod ( " setCharacterEncoding " , String . class ) ;
} catch ( NoSuchMethodException e ) {
2007-11-06 02:59:23 +08:00
context . setAttribute ( APP , new IncompatibleServletVersionDetected ( ServletResponse . class ) ) ;
2007-10-31 10:14:53 +08:00
return ;
2006-11-06 05:16:01 +08:00
}
2007-09-17 07:46:12 +08:00
2008-02-08 14:07:04 +08:00
// some containers (in particular Tomcat) doesn't abort a launch
// even if the temp directory doesn't exist.
// check that and report an error
try {
File f = File . createTempFile ( " test " , " test " ) ;
f . delete ( ) ;
} catch ( IOException e ) {
context . setAttribute ( APP , new NoTempDir ( e ) ) ;
return ;
}
2007-10-31 10:14:53 +08:00
// Tomcat breaks XSLT with JDK 5.0 and onward. Check if that's the case, and if so,
// try to correct it
try {
TransformerFactory . newInstance ( ) ;
// if this works we are all happy
} catch ( TransformerFactoryConfigurationError x ) {
// no it didn't.
Logger logger = Logger . getLogger ( WebAppMain . class . getName ( ) ) ;
2007-09-18 18:51:47 +08:00
2007-10-31 10:14:53 +08:00
logger . log ( Level . WARNING , " XSLT not configured correctly. Hudson will try to fix this. See http://issues.apache.org/bugzilla/show_bug.cgi?id=40895 for more details " , x ) ;
System . setProperty ( TransformerFactory . class . getName ( ) , " com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl " ) ;
2007-09-17 07:46:12 +08:00
try {
2007-10-31 10:14:53 +08:00
TransformerFactory . newInstance ( ) ;
logger . info ( " XSLT is set to the JAXP RI in JRE " ) ;
} catch ( TransformerFactoryConfigurationError y ) {
logger . log ( Level . SEVERE , " Failed to correct the problem. " ) ;
2007-09-17 07:46:12 +08:00
}
2007-10-31 10:14:53 +08:00
}
2007-09-17 07:46:12 +08:00
2007-12-20 14:35:28 +08:00
Stapler . setExpressionFactory ( event , new ExpressionFactory2 ( ) ) ;
2007-11-02 06:14:22 +08:00
context . setAttribute ( APP , new HudsonIsLoading ( ) ) ;
2007-10-31 10:14:53 +08:00
new Thread ( " hudson initialization thread " ) {
public void run ( ) {
try {
computeVersion ( context ) ;
try {
2007-11-02 06:14:22 +08:00
context . setAttribute ( APP , new Hudson ( home , context ) ) ;
2007-10-31 10:14:53 +08:00
} catch ( IOException e ) {
throw new Error ( e ) ;
}
Trigger . init ( ) ; // start running trigger
// trigger the loading of changelogs in the background,
// but give the system 10 seconds so that the first page
// can be served quickly
Trigger . timer . schedule ( new SafeTimerTask ( ) {
public void doRun ( ) {
User . get ( " nobody " ) . getBuilds ( ) ;
}
} , 1000 * 10 ) ;
} catch ( Error e ) {
LOGGER . log ( Level . SEVERE , " Failed to initialize Hudson " , e ) ;
throw e ;
} catch ( RuntimeException e ) {
LOGGER . log ( Level . SEVERE , " Failed to initialize Hudson " , e ) ;
throw e ;
2007-09-17 07:46:12 +08:00
}
2007-10-31 10:14:53 +08:00
}
} . start ( ) ;
} catch ( Error e ) {
LOGGER . log ( Level . SEVERE , " Failed to initialize Hudson " , e ) ;
throw e ;
} catch ( RuntimeException e ) {
LOGGER . log ( Level . SEVERE , " Failed to initialize Hudson " , e ) ;
throw e ;
}
2006-11-06 05:16:01 +08:00
}
2007-09-18 18:51:47 +08:00
protected void computeVersion ( ServletContext context ) {
// set the version
Properties props = new Properties ( ) ;
try {
InputStream is = getClass ( ) . getResourceAsStream ( " hudson-version.properties " ) ;
if ( is ! = null )
props . load ( is ) ;
} catch ( IOException e ) {
e . printStackTrace ( ) ; // if the version properties is missing, that's OK.
}
String ver = props . getProperty ( " version " ) ;
if ( ver = = null ) ver = " ? " ;
Hudson . VERSION = ver ;
context . setAttribute ( " version " , ver ) ;
2007-12-25 06:47:19 +08:00
String verHash = Util . getDigestOf ( ver ) . substring ( 0 , 8 ) ;
2007-09-18 18:51:47 +08:00
if ( ver . equals ( " ? " ) )
Hudson . RESOURCE_PATH = " " ;
else
2007-12-25 06:47:19 +08:00
Hudson . RESOURCE_PATH = " /static/ " + verHash ;
Hudson . VIEW_RESOURCE_PATH = " /resources/ " + verHash ;
}
2007-09-18 18:51:47 +08:00
/ * *
2006-11-06 05:16:01 +08:00
* Installs log handler to monitor all Hudson logs .
* /
private void installLogger ( ) {
Hudson . logRecords = handler . getView ( ) ;
Logger . getLogger ( " hudson " ) . addHandler ( handler ) ;
}
/ * *
* Determines the home directory for Hudson .
*
* People makes configuration mistakes , so we are trying to be nice
* with those by doing { @link String # trim ( ) } .
* /
private File getHomeDir ( ServletContextEvent event ) {
// check JNDI for the home directory first
try {
Context env = ( Context ) new InitialContext ( ) . lookup ( " java:comp/env " ) ;
String value = ( String ) env . lookup ( " HUDSON_HOME " ) ;
if ( value ! = null & & value . trim ( ) . length ( ) > 0 )
2007-08-11 00:00:06 +08:00
return new File ( value . trim ( ) ) ;
2006-11-06 05:16:01 +08:00
} catch ( NamingException e ) {
// ignore
}
// finally check the system property
String sysProp = System . getProperty ( " HUDSON_HOME " ) ;
if ( sysProp ! = null )
2007-08-11 00:00:06 +08:00
return new File ( sysProp . trim ( ) ) ;
2008-01-30 00:35:56 +08:00
// look at the env var next
String env = EnvVars . masterEnvVars . get ( " HUDSON_HOME " ) ;
if ( env ! = null )
return new File ( env . trim ( ) ) . getAbsoluteFile ( ) ;
2006-11-06 05:16:01 +08:00
// otherwise pick a place by ourselves
String root = event . getServletContext ( ) . getRealPath ( " /WEB-INF/workspace " ) ;
if ( root ! = null ) {
File ws = new File ( root . trim ( ) ) ;
if ( ws . exists ( ) )
2006-12-21 03:20:31 +08:00
// Hudson <1.42 used to prefer this before ~/.hudson, so
2006-11-06 05:16:01 +08:00
// check the existence and if it's there, use it.
// otherwise if this is a new installation, prefer ~/.hudson
2007-08-11 00:00:06 +08:00
return ws ;
2006-11-06 05:16:01 +08:00
}
// if for some reason we can't put it within the webapp, use home directory.
2007-08-11 00:00:06 +08:00
return new File ( new File ( System . getProperty ( " user.home " ) ) , " .hudson " ) ;
2006-11-06 05:16:01 +08:00
}
public void contextDestroyed ( ServletContextEvent event ) {
Hudson instance = Hudson . getInstance ( ) ;
if ( instance ! = null )
instance . cleanUp ( ) ;
2007-04-06 22:20:13 +08:00
// Logger is in the system classloader, so if we don't do this
// the whole web app will never be undepoyed.
Logger . getLogger ( " hudson " ) . removeHandler ( handler ) ;
2006-11-06 05:16:01 +08:00
}
2007-10-31 10:14:53 +08:00
private static final Logger LOGGER = Logger . getLogger ( WebAppMain . class . getName ( ) ) ;
2007-12-20 14:35:28 +08:00
2006-11-06 05:16:01 +08:00
}