jenkins/core/src/main/java/hudson/WebAppMain.java

285 lines
11 KiB
Java
Raw Normal View History

package hudson;
import com.thoughtworks.xstream.converters.reflection.PureJavaReflectionProvider;
import com.thoughtworks.xstream.core.JVM;
import hudson.model.Hudson;
import hudson.model.User;
import hudson.triggers.SafeTimerTask;
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 hudson.util.IncompatibleAntVersionDetected;
import hudson.util.HudsonFailedToLoad;
import org.jvnet.localizer.LocaleProvider;
import org.kohsuke.stapler.Stapler;
import org.kohsuke.stapler.StaplerRequest;
import org.apache.tools.ant.types.FileSet;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.ServletResponse;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.TransformerFactoryConfigurationError;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Locale;
import java.util.Properties;
Merged revisions 9727,9739,9765-9766,9927,10332,10334-10338,10340,10416,10421,10553,10839-10845,10876 via svnmerge from https://www.dev.java.net/svn/hudson/branches/tom ........ r9727 | huybrechts | 2008-05-31 12:49:18 -0700 (Sat, 31 May 2008) | 3 lines Issue 1659 - cleaning up static Trigger, Hudson instances - resetting commons-logging ........ r9739 | huybrechts | 2008-06-01 12:05:51 -0700 (Sun, 01 Jun 2008) | 1 line #1770 catching and logging InstantiationError ........ r9765 | huybrechts | 2008-06-02 11:34:45 -0700 (Mon, 02 Jun 2008) | 1 line project-based security: fixed a new isAdmin call ........ r9766 | huybrechts | 2008-06-02 11:35:27 -0700 (Mon, 02 Jun 2008) | 1 line "theInstance = null" caused some reloading problems ........ r9927 | huybrechts | 2008-06-07 15:31:20 -0700 (Sat, 07 Jun 2008) | 8 lines Initial commit of parameterized builds. Provided functionality: - users can define parameters per project (only string paramters for now) - if a project has parameters, the 'build' button will redirect to an input page - parameter values can be used as ${variable} in Ant, Maven and CommandInterpreter builders - after the build, parameter values can be viewed in a separate page - tasks for parameterized builds are persisted in the queue over restarts - queue persistence is now in XML (with backwards compatibility for text format) ........ r10332 | huybrechts | 2008-06-23 05:32:45 -0700 (Mon, 23 Jun 2008) | 1 line made EmbedderLoggerImpl public for use in plugins ........ r10334 | huybrechts | 2008-06-23 05:48:10 -0700 (Mon, 23 Jun 2008) | 1 line [HUDSON-1914] additional parameter types (job, run), bugfixes ........ r10335 | huybrechts | 2008-06-23 05:49:10 -0700 (Mon, 23 Jun 2008) | 1 line [HUDSON-1573] initialize servlet filters ........ r10336 | huybrechts | 2008-06-23 05:52:14 -0700 (Mon, 23 Jun 2008) | 1 line [HUDSON-1915] onStarted onevent ........ r10337 | huybrechts | 2008-06-23 06:07:32 -0700 (Mon, 23 Jun 2008) | 4 lines [HUDSON-1504] basic ui for job-based access control This allows assigning permissions per user and per job. It is still possible to provide defaults using a global matrix. ........ r10338 | huybrechts | 2008-06-23 06:18:13 -0700 (Mon, 23 Jun 2008) | 3 lines [HUDSON-1504] basic ui for job-based access control adding it to the list... ........ r10340 | huybrechts | 2008-06-23 07:23:12 -0700 (Mon, 23 Jun 2008) | 1 line [HUDSON-1504] fix compile error ........ r10416 | huybrechts | 2008-06-25 01:13:11 -0700 (Wed, 25 Jun 2008) | 1 line initial commit staging plugin: supports doing a maven release to a staging repository, serving that repository for Hudson, and uploading it on demand ........ r10421 | huybrechts | 2008-06-25 12:32:06 -0700 (Wed, 25 Jun 2008) | 1 line [HUDSON-1954] initial commit jbpm plugin and staging workflow example ........ r10553 | huybrechts | 2008-07-01 14:13:57 -0700 (Tue, 01 Jul 2008) | 1 line moving registration for parameter definition out of registered class ........ r10839 | kohsuke | 2008-07-15 13:48:05 -0700 (Tue, 15 Jul 2008) | 6 lines Making a few adjustments in preparation of merging back to the trunk. - doc updates - added QueueTaskFilter as a plain delegation implementation to simplify ParameterizedProjectTask. - renamed ParameterDefinition.newInstance(...) to createValue, to avoid having two newInstance methods in this part of the system that does different things ........ r10840 | kohsuke | 2008-07-15 13:51:16 -0700 (Tue, 15 Jul 2008) | 1 line Hudson's coding convention is to use WS and no TAB. ........ r10841 | kohsuke | 2008-07-15 14:34:37 -0700 (Tue, 15 Jul 2008) | 2 lines - making ParameterValue an abstract class to allow evolution without breaking plugins in the future - tweaked the UI a bit so that parameterization don't get too much visibility. ........ r10842 | kohsuke | 2008-07-15 14:49:35 -0700 (Tue, 15 Jul 2008) | 2 lines improving the UI a bit. Added an icon ........ r10843 | kohsuke | 2008-07-15 15:08:28 -0700 (Tue, 15 Jul 2008) | 1 line adding help ........ r10844 | kohsuke | 2008-07-15 15:10:42 -0700 (Tue, 15 Jul 2008) | 1 line TAB->WS ........ r10845 | kohsuke | 2008-07-15 15:44:47 -0700 (Tue, 15 Jul 2008) | 2 lines - Use HTTP status code to notify the automated clients that the build triggering failed. - Overload the "/build" URL to render the parameter entry form. ........ r10876 | kohsuke | 2008-07-18 13:52:51 -0700 (Fri, 18 Jul 2008) | 10 lines Giving more hooks to ParameterValue to affect a build - abstracting the variable substitution process so that the actual syntax for marking variables (e.g., %VAR% vs ${var} vs #{xyz}) and the actual resolution process are orthogonal. - parameter values can now contribute BuildWrapper to a build. - parameter values can now contribute environment variables to a build. ........ git-svn-id: https://hudson.dev.java.net/svn/hudson/trunk/hudson/main@10950 71c3de6d-444a-0410-be80-ed276b4c234a
2008-07-22 08:48:13 +08:00
import java.util.Timer;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Entry point when Hudson is used as a webapp.
*
* @author Kohsuke Kawaguchi
*/
public class WebAppMain implements ServletContextListener {
private final RingBufferLogHandler handler = new RingBufferLogHandler();
private static final String APP = "app";
/**
* Creates the sole instance of {@link Hudson} and register it to the {@link ServletContext}.
*/
public void contextInitialized(ServletContextEvent event) {
try {
final ServletContext context = event.getServletContext();
// 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;
}
});
// 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) {
context.setAttribute(APP,new InsufficientPermissionDetected(e));
return;
}
installLogger();
final File home = getHomeDir(event).getAbsoluteFile();
home.mkdirs();
System.out.println("hudson home directory: "+home);
// check that home exists (as mkdirs could have failed silently), otherwise throw a meaningful error
if (! home.exists()) {
context.setAttribute(APP,new NoHomeDir(home));
return;
}
// make sure that we are using XStream in the "enhanced" (JVM-specific) mode
if(jvm.bestReflectionProvider().getClass()==PureJavaReflectionProvider.class) {
// nope
context.setAttribute(APP,new IncompatibleVMDetected());
return;
}
// make sure this is servlet 2.4 container or above
try {
ServletResponse.class.getMethod("setCharacterEncoding",String.class);
} catch (NoSuchMethodException e) {
context.setAttribute(APP,new IncompatibleServletVersionDetected(ServletResponse.class));
return;
}
// make sure that we see Ant 1.7
try {
FileSet.class.getMethod("getDirectoryScanner");
} catch (NoSuchMethodException e) {
context.setAttribute(APP,new IncompatibleAntVersionDetected(FileSet.class));
return;
}
// 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;
}
// 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());
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");
try {
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.");
}
}
Stapler.setExpressionFactory(event, new ExpressionFactory2());
context.setAttribute(APP,new HudsonIsLoading());
new Thread("hudson initialization thread") {
public void run() {
try {
computeVersion(context);
try {
context.setAttribute(APP,new Hudson(home,context));
} 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.getUnknown().getBuilds();
}
}, 1000*10);
} catch (Error e) {
LOGGER.log(Level.SEVERE, "Failed to initialize Hudson",e);
context.setAttribute(APP,new HudsonFailedToLoad(e));
throw e;
} catch (RuntimeException e) {
LOGGER.log(Level.SEVERE, "Failed to initialize Hudson",e);
context.setAttribute(APP,new HudsonFailedToLoad(e));
throw e;
}
}
}.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;
}
}
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);
String verHash = Util.getDigestOf(ver).substring(0, 8);
if(ver.equals("?"))
Hudson.RESOURCE_PATH = "";
else
Hudson.RESOURCE_PATH = "/static/"+verHash;
Hudson.VIEW_RESOURCE_PATH = "/resources/"+ verHash;
}
/**
* 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 {
InitialContext iniCtxt = new InitialContext();
Context env = (Context) iniCtxt.lookup("java:comp/env");
String value = (String) env.lookup("HUDSON_HOME");
if(value!=null && value.trim().length()>0)
return new File(value.trim());
// look at one more place. See issue #1314
value = (String) iniCtxt.lookup("HUDSON_HOME");
if(value!=null && value.trim().length()>0)
return new File(value.trim());
} catch (NamingException e) {
// ignore
}
// finally check the system property
String sysProp = System.getProperty("HUDSON_HOME");
if(sysProp!=null)
return new File(sysProp.trim());
// look at the env var next
String env = EnvVars.masterEnvVars.get("HUDSON_HOME");
if(env!=null)
return new File(env.trim()).getAbsoluteFile();
// 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())
// Hudson <1.42 used to prefer this before ~/.hudson, so
// check the existence and if it's there, use it.
// otherwise if this is a new installation, prefer ~/.hudson
return ws;
}
// if for some reason we can't put it within the webapp, use home directory.
return new File(new File(System.getProperty("user.home")),".hudson");
}
public void contextDestroyed(ServletContextEvent event) {
Hudson instance = Hudson.getInstance();
if(instance!=null)
instance.cleanUp();
// 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);
}
private static final Logger LOGGER = Logger.getLogger(WebAppMain.class.getName());
}