2009-02-07 04:05:24 +08:00
/ *
* The MIT License
*
* Copyright ( c ) 2004 - 2009 , Sun Microsystems , Inc . , Kohsuke Kawaguchi , Jean - Baptiste Quenot , Tom Huybrechts
*
* Permission is hereby granted , free of charge , to any person obtaining a copy
* of this software and associated documentation files ( the " Software " ) , to deal
* in the Software without restriction , including without limitation the rights
* to use , copy , modify , merge , publish , distribute , sublicense , and / or sell
* copies of the Software , and to permit persons to whom the Software is
* furnished to do so , subject to the following conditions :
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software .
*
* THE SOFTWARE IS PROVIDED " AS IS " , WITHOUT WARRANTY OF ANY KIND , EXPRESS OR
* IMPLIED , INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY ,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT . IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM , DAMAGES OR OTHER
* LIABILITY , WHETHER IN AN ACTION OF CONTRACT , TORT OR OTHERWISE , ARISING FROM ,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE .
* /
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 ;
2011-05-29 01:11:48 +08:00
import hudson.model.Jenkins ;
2006-11-06 05:16:01 +08:00
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 ;
2008-02-29 22:10:14 +08:00
import hudson.util.IncompatibleAntVersionDetected ;
2008-06-06 02:29:38 +08:00
import hudson.util.HudsonFailedToLoad ;
2008-12-23 11:10:41 +08:00
import hudson.util.ChartUtil ;
import hudson.util.AWTProblem ;
2008-02-08 14:07:04 +08:00
import org.jvnet.localizer.LocaleProvider ;
2009-03-23 02:56:36 +08:00
import org.kohsuke.stapler.jelly.JellyFacet ;
2008-02-29 22:10:14 +08:00
import org.apache.tools.ant.types.FileSet ;
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 ;
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 ;
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 ;
2009-02-14 01:25:14 +08:00
import java.security.Security ;
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
* /
2008-08-07 05:14:41 +08:00
public final 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
/ * *
2011-05-29 01:11:48 +08:00
* Creates the sole instance of { @link hudson . model . Jenkins } and register it to the { @link ServletContext } .
2006-11-06 05:16:01 +08:00
* /
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 ( ) {
2011-03-01 16:15:15 +08:00
return Functions . getCurrentLocale ( ) ;
2007-12-27 08:46:21 +08:00
}
} ) ;
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
2011-01-31 15:44:52 +08:00
try { // remove Sun PKCS11 provider if present. See http://wiki.jenkins-ci.org/display/JENKINS/Solaris+Issue+6276483
2009-02-14 01:25:14 +08:00
Security . removeProvider ( " SunPKCS11-Solaris " ) ;
} catch ( SecurityException e ) {
// ignore this error.
}
2007-10-31 10:14:53 +08:00
installLogger ( ) ;
2006-11-06 05:16:01 +08:00
2011-02-03 11:47:07 +08:00
final FileAndDescription describedHomeDir = getHomeDir ( event ) ;
final File home = describedHomeDir . file . getAbsoluteFile ( ) ;
2007-10-31 10:14:53 +08:00
home . mkdirs ( ) ;
2011-02-03 11:47:07 +08:00
System . out . println ( " hudson home directory: " + home + " found at: " + describedHomeDir . description ) ;
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
2009-11-26 07:21:17 +08:00
// JNA is no longer a hard requirement. It's just nice to have. See HUDSON-4820 for more context.
// // make sure JNA works. this can fail if
// // - platform is unsupported
// // - JNA is already loaded in another classloader
2011-01-31 15:44:52 +08:00
// // see http://wiki.jenkins-ci.org/display/JENKINS/JNA+is+already+loaded
2009-11-26 07:21:17 +08:00
// // TODO: or shall we instead modify Hudson to work gracefully without JNA?
// try {
// /*
// java.lang.UnsatisfiedLinkError: Native Library /builds/apps/glassfish/domains/hudson-domain/generated/jsp/j2ee-modules/hudson-1.309/loader/com/sun/jna/sunos-sparc/libjnidispatch.so already loaded in another classloader
// at java.lang.ClassLoader.loadLibrary0(ClassLoader.java:1743)
// at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1674)
// at java.lang.Runtime.load0(Runtime.java:770)
// at java.lang.System.load(System.java:1005)
// at com.sun.jna.Native.loadNativeLibraryFromJar(Native.java:746)
// at com.sun.jna.Native.loadNativeLibrary(Native.java:680)
// at com.sun.jna.Native.<clinit>(Native.java:108)
// at hudson.util.jna.GNUCLibrary.<clinit>(GNUCLibrary.java:86)
// at hudson.Util.createSymlink(Util.java:970)
// at hudson.model.Run.run(Run.java:1174)
// at hudson.matrix.MatrixBuild.run(MatrixBuild.java:149)
// at hudson.model.ResourceController.execute(ResourceController.java:88)
// at hudson.model.Executor.run(Executor.java:123)
// */
// String.valueOf(Native.POINTER_SIZE); // this meaningless operation forces the classloading and initialization
// } catch (LinkageError e) {
// if (e.getMessage().contains("another classloader"))
// context.setAttribute(APP,new JNADoublyLoaded(e));
// else
// context.setAttribute(APP,new HudsonFailedToLoad(e));
// }
2009-11-20 06:24:43 +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-29 22:10:14 +08:00
// make sure that we see Ant 1.7
try {
FileSet . class . getMethod ( " getDirectoryScanner " ) ;
} catch ( NoSuchMethodException e ) {
context . setAttribute ( APP , new IncompatibleAntVersionDetected ( FileSet . class ) ) ;
return ;
}
2008-12-23 11:10:41 +08:00
// make sure AWT is functioning, or else JFreeChart won't even load.
2008-12-25 02:11:44 +08:00
if ( ChartUtil . awtProblemCause ! = null ) {
context . setAttribute ( APP , new AWTProblem ( ChartUtil . awtProblemCause ) ) ;
2008-12-23 11:10:41 +08:00
return ;
}
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.
2010-03-09 10:12:50 +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 ) ;
2007-10-31 10:14:53 +08:00
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 ( ) ;
2010-03-09 10:12:50 +08:00
LOGGER . info ( " XSLT is set to the JAXP RI in JRE " ) ;
2007-10-31 10:14:53 +08:00
} catch ( TransformerFactoryConfigurationError y ) {
2010-03-09 10:12:50 +08:00
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
2008-12-25 02:11:44 +08:00
installExpressionFactory ( event ) ;
2007-12-20 14:35:28 +08:00
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 " ) {
2009-09-15 02:08:34 +08:00
@Override
2007-10-31 10:14:53 +08:00
public void run ( ) {
try {
2009-06-20 04:54:39 +08:00
context . setAttribute ( APP , new Hudson ( home , context ) ) ;
2007-10-31 10:14:53 +08:00
// 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 ( ) {
2008-06-10 07:11:47 +08:00
User . getUnknown ( ) . getBuilds ( ) ;
2007-10-31 10:14:53 +08:00
}
} , 1000 * 10 ) ;
} catch ( Error e ) {
LOGGER . log ( Level . SEVERE , " Failed to initialize Hudson " , e ) ;
2008-06-06 02:29:38 +08:00
context . setAttribute ( APP , new HudsonFailedToLoad ( e ) ) ;
2007-10-31 10:14:53 +08:00
throw e ;
2009-06-20 04:54:39 +08:00
} catch ( Exception e ) {
2007-10-31 10:14:53 +08:00
LOGGER . log ( Level . SEVERE , " Failed to initialize Hudson " , e ) ;
2008-06-06 02:29:38 +08:00
context . setAttribute ( APP , new HudsonFailedToLoad ( 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
}
2008-12-25 02:11:44 +08:00
public static void installExpressionFactory ( ServletContextEvent event ) {
2009-03-23 02:56:36 +08:00
JellyFacet . setExpressionFactory ( event , new ExpressionFactory2 ( ) ) ;
2008-12-25 02:11:44 +08:00
}
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 ( ) {
2011-05-29 01:11:48 +08:00
Jenkins . logRecords = handler . getView ( ) ;
2006-11-06 05:16:01 +08:00
Logger . getLogger ( " hudson " ) . addHandler ( handler ) ;
}
2011-02-03 11:47:07 +08:00
/** Add some metadata to a File, allowing to trace setup issues */
2011-02-03 12:38:50 +08:00
private static class FileAndDescription {
2011-02-03 11:47:07 +08:00
File file ;
String description ;
public FileAndDescription ( File file , String description ) {
this . file = file ;
this . description = description ;
}
}
2006-11-06 05:16:01 +08:00
/ * *
2011-01-31 08:25:58 +08:00
* Determines the home directory for Jenkins .
2006-11-06 05:16:01 +08:00
*
2011-01-31 08:25:58 +08:00
* < p >
* We look for a setting that affects the smallest scope first , then bigger ones later .
*
* < p >
2006-11-06 05:16:01 +08:00
* People makes configuration mistakes , so we are trying to be nice
* with those by doing { @link String # trim ( ) } .
2011-02-03 11:47:07 +08:00
*
* < p >
* @return the File alongside with some description to help the user troubleshoot issues
2006-11-06 05:16:01 +08:00
* /
2011-02-03 11:47:07 +08:00
private FileAndDescription getHomeDir ( ServletContextEvent event ) {
2006-11-06 05:16:01 +08:00
// check JNDI for the home directory first
2011-01-31 08:25:58 +08:00
for ( String name : HOME_NAMES ) {
try {
InitialContext iniCtxt = new InitialContext ( ) ;
Context env = ( Context ) iniCtxt . lookup ( " java:comp/env " ) ;
String value = ( String ) env . lookup ( name ) ;
if ( value ! = null & & value . trim ( ) . length ( ) > 0 )
2011-02-03 11:47:07 +08:00
return new FileAndDescription ( new File ( value . trim ( ) ) , " JNDI/java:comp/env/ " + name ) ;
2011-01-31 08:25:58 +08:00
// look at one more place. See issue #1314
value = ( String ) iniCtxt . lookup ( name ) ;
if ( value ! = null & & value . trim ( ) . length ( ) > 0 )
2011-02-03 11:47:07 +08:00
return new FileAndDescription ( new File ( value . trim ( ) ) , " JNDI/ " + name ) ;
2011-01-31 08:25:58 +08:00
} catch ( NamingException e ) {
// ignore
}
}
// next the system property
for ( String name : HOME_NAMES ) {
String sysProp = System . getProperty ( name ) ;
if ( sysProp ! = null )
2011-02-03 11:47:07 +08:00
return new FileAndDescription ( new File ( sysProp . trim ( ) ) , " System.getProperty( \" " + name + " \" ) " ) ;
2006-11-06 05:16:01 +08:00
}
2008-01-30 00:35:56 +08:00
// look at the env var next
2011-01-31 08:25:58 +08:00
for ( String name : HOME_NAMES ) {
String env = EnvVars . masterEnvVars . get ( name ) ;
if ( env ! = null )
2011-02-03 11:47:07 +08:00
return new FileAndDescription ( new File ( env . trim ( ) ) . getAbsoluteFile ( ) , " EnvVars.masterEnvVars.get( \" " + name + " \" ) " ) ;
2011-01-31 08:25:58 +08:00
}
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
2011-02-03 11:47:07 +08:00
return new FileAndDescription ( ws , " getServletContext().getRealPath( \" /WEB-INF/workspace \" ) " ) ;
2006-11-06 05:16:01 +08:00
}
2011-02-03 11:47:07 +08:00
File legacyHome = new File ( new File ( System . getProperty ( " user.home " ) ) , " .hudson " ) ;
if ( legacyHome . exists ( ) ) {
return new FileAndDescription ( legacyHome , " $user.home/.hudson " ) ; // before rename, this is where it was stored
}
2011-01-31 08:25:58 +08:00
2011-02-03 11:47:07 +08:00
File newHome = new File ( new File ( System . getProperty ( " user.home " ) ) , " .jenkins " ) ;
return new FileAndDescription ( newHome , " $user.home/.jenkins " ) ;
2006-11-06 05:16:01 +08:00
}
public void contextDestroyed ( ServletContextEvent event ) {
2011-05-29 01:11:48 +08:00
Jenkins instance = Jenkins . getInstance ( ) ;
2006-11-06 05:16:01 +08:00
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
2011-01-31 08:25:58 +08:00
private static final String [ ] HOME_NAMES = { " JENKINS_HOME " , " HUDSON_HOME " } ;
2006-11-06 05:16:01 +08:00
}