Issue #4003 Fix quickstart (#4008)

* Issue #4003 Fix quickstart.

Modes:
AUTO: use quickstart xml if present, start normally otherwise
GENERATE: re/generate quickstart xml
QUICKSTART: use a pregenerated quickstart xml, fail otherwise

Signed-off-by: Jan Bartel <janb@webtide.com>

* Issue #4003 Cleanup quickstart

Removed TerminateException in favour of Server.setDryRun(boolean)
and AbstractLifeCycle.StopException

Signed-off-by: Greg Wilkins <gregw@webtide.com>
This commit is contained in:
Jan Bartel 2019-09-10 16:04:01 +10:00 committed by GitHub
parent b272b36908
commit b851af0a5b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
40 changed files with 612 additions and 543 deletions

View File

@ -36,9 +36,9 @@
</Arg>
</Call> -->
<Call id="webappprovider" name="addAppProvider">
<Call name="addAppProvider">
<Arg>
<New class="org.eclipse.jetty.deploy.providers.WebAppProvider">
<New id="webappprovider" class="org.eclipse.jetty.deploy.providers.WebAppProvider">
<Set name="monitoredDirName">
<Property>
<Name>jetty.deploy.monitoredPath</Name>

View File

@ -172,7 +172,7 @@ public class DeploymentManager extends ContainerLifeCycle
for (AppProvider provider : providers)
{
if (_providers.add(provider))
addBean(provider);
addBean(provider, true);
}
}
@ -186,7 +186,7 @@ public class DeploymentManager extends ContainerLifeCycle
if (isRunning())
throw new IllegalStateException();
_providers.add(provider);
addBean(provider);
addBean(provider, true);
}
public void setLifeCycleBindings(Collection<AppLifeCycle.Binding> bindings)
@ -523,6 +523,7 @@ public class DeploymentManager extends ContainerLifeCycle
catch (Throwable t)
{
LOG.warn("Unable to reach node goal: " + nodeName, t);
// migrate to FAILED node
Node failed = _lifecycle.getNodeByName(AppLifeCycle.FAILED);
appentry.setLifeCycleNode(failed);

View File

@ -20,7 +20,6 @@ package org.eclipse.jetty.deploy;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.MalformedURLException;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
@ -29,6 +28,7 @@ import org.eclipse.jetty.util.annotation.ManagedAttribute;
import org.eclipse.jetty.util.annotation.ManagedObject;
import org.eclipse.jetty.util.annotation.ManagedOperation;
import org.eclipse.jetty.util.annotation.Name;
import org.eclipse.jetty.util.component.Dumpable;
import org.eclipse.jetty.util.resource.Resource;
/**
@ -37,21 +37,33 @@ import org.eclipse.jetty.util.resource.Resource;
* Supplies properties defined in a file.
*/
@ManagedObject("Configure deployed webapps via properties")
public class PropertiesConfigurationManager implements ConfigurationManager
public class PropertiesConfigurationManager implements ConfigurationManager, Dumpable
{
private String _properties;
private final Map<String, String> _map = new HashMap<String, String>();
private final Map<String, String> _map = new HashMap<>();
public PropertiesConfigurationManager(String properties)
{
if (properties != null)
{
try
{
setFile(properties);
}
catch (IOException e)
{
throw new RuntimeException(e);
}
}
}
public PropertiesConfigurationManager()
{
this(null);
}
@ManagedAttribute("A file or URL of properties")
public void setFile(String resource) throws MalformedURLException, IOException
public void setFile(String resource) throws IOException
{
_properties = resource;
_map.clear();
@ -75,7 +87,7 @@ public class PropertiesConfigurationManager implements ConfigurationManager
@Override
public Map<String, String> getProperties()
{
return new HashMap<>(_map);
return _map;
}
private void loadProperties(String resource) throws FileNotFoundException, IOException
@ -86,9 +98,25 @@ public class PropertiesConfigurationManager implements ConfigurationManager
Properties properties = new Properties();
properties.load(file.getInputStream());
for (Map.Entry<Object, Object> entry : properties.entrySet())
{
_map.put(entry.getKey().toString(), String.valueOf(entry.getValue()));
}
}
@Override
public String toString()
{
return String.format("%s@%x{%s}", this.getClass(), hashCode(), _properties);
}
@Override
public String dump()
{
return Dumpable.dump(this);
}
@Override
public void dump(Appendable out, String indent) throws IOException
{
Dumpable.dumpObjects(out, indent, toString(), _map);
}
}

View File

@ -20,7 +20,9 @@ package org.eclipse.jetty.deploy.bindings;
import org.eclipse.jetty.deploy.App;
import org.eclipse.jetty.deploy.AppLifeCycle;
import org.eclipse.jetty.deploy.AppProvider;
import org.eclipse.jetty.deploy.graph.Node;
import org.eclipse.jetty.deploy.providers.WebAppProvider;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
@ -94,6 +96,13 @@ public class GlobalWebappConfigBinding implements AppLifeCycle.Binding
XmlConfiguration jettyXmlConfig = new XmlConfiguration(globalContextSettings);
Resource resource = Resource.newResource(app.getOriginId());
app.getDeploymentManager().scope(jettyXmlConfig, resource);
AppProvider appProvider = app.getAppProvider();
if (appProvider instanceof WebAppProvider)
{
WebAppProvider webAppProvider = ((WebAppProvider)appProvider);
if (webAppProvider.getConfigurationManager() != null)
jettyXmlConfig.getProperties().putAll(webAppProvider.getConfigurationManager().getProperties());
}
WebAppClassLoader.runWithServerClassAccess(() ->
{
jettyXmlConfig.configure(context);

View File

@ -36,7 +36,7 @@ import org.eclipse.jetty.util.Scanner;
import org.eclipse.jetty.util.annotation.ManagedAttribute;
import org.eclipse.jetty.util.annotation.ManagedObject;
import org.eclipse.jetty.util.annotation.ManagedOperation;
import org.eclipse.jetty.util.component.AbstractLifeCycle;
import org.eclipse.jetty.util.component.ContainerLifeCycle;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.resource.Resource;
@ -45,7 +45,7 @@ import org.eclipse.jetty.util.resource.Resource;
*
*/
@ManagedObject("Abstract Provider for loading webapps")
public abstract class ScanningAppProvider extends AbstractLifeCycle implements AppProvider
public abstract class ScanningAppProvider extends ContainerLifeCycle implements AppProvider
{
private static final Logger LOG = Log.getLogger(ScanningAppProvider.class);
@ -81,11 +81,13 @@ public abstract class ScanningAppProvider extends AbstractLifeCycle implements A
protected ScanningAppProvider()
{
this(null);
}
protected ScanningAppProvider(FilenameFilter filter)
{
_filenameFilter = filter;
addBean(_appMap);
}
protected void setFilenameFilter(FilenameFilter filter)
@ -142,15 +144,19 @@ public abstract class ScanningAppProvider extends AbstractLifeCycle implements A
_scanner.setFilenameFilter(_filenameFilter);
_scanner.setReportDirs(true);
_scanner.addListener(_scannerListener);
_scanner.start();
addBean(_scanner);
super.doStart();
}
@Override
protected void doStop() throws Exception
{
super.doStop();
if (_scanner != null)
{
_scanner.stop();
removeBean(_scanner);
_scanner.removeListener(_scannerListener);
_scanner = null;
}
@ -307,4 +313,10 @@ public abstract class ScanningAppProvider extends AbstractLifeCycle implements A
);
_scanner.scan();
}
@Override
public String toString()
{
return String.format("%s@%x%s", this.getClass(), hashCode(), _monitored);
}
}

View File

@ -206,6 +206,7 @@ public class WebAppProvider extends ScanningAppProvider
*/
public void setConfigurationManager(ConfigurationManager configurationManager)
{
updateBean(_configurationManager, configurationManager);
_configurationManager = configurationManager;
}

View File

@ -28,8 +28,9 @@ import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.plugins.annotations.ResolutionScope;
import org.eclipse.jetty.annotations.AnnotationConfiguration;
import org.eclipse.jetty.quickstart.QuickStartConfiguration;
import org.eclipse.jetty.quickstart.QuickStartConfiguration.Mode;
import org.eclipse.jetty.util.IO;
import org.eclipse.jetty.util.component.LifeCycle;
import org.eclipse.jetty.util.resource.Resource;
import org.eclipse.jetty.util.thread.QueuedThreadPool;
@ -92,30 +93,23 @@ public class JettyEffectiveWebXml extends JettyRunMojo
configureWebApplication();
//set the webapp up to do very little other than generate the quickstart-web.xml
webApp.setCopyWebDir(false);
webApp.setCopyWebInf(false);
webApp.setGenerateQuickStart(true);
//if the user didn't nominate a file to generate into, pick the name and
//make sure that it is deleted on exit
if (webApp.getQuickStartWebDescriptor() == null)
{
if (effectiveWebXml == null)
{
deleteOnExit = true;
effectiveWebXml = new File(target, "effective-web.xml");
effectiveWebXml.deleteOnExit();
}
Resource descriptor = Resource.newResource(effectiveWebXml);
if (!effectiveWebXml.getParentFile().exists())
effectiveWebXml.getParentFile().mkdirs();
if (!effectiveWebXml.exists())
effectiveWebXml.createNewFile();
webApp.setQuickStartWebDescriptor(descriptor);
}
webApp.setCopyWebDir(false);
webApp.setCopyWebInf(false);
webApp.addConfiguration(new QuickStartConfiguration());
webApp.setAttribute(QuickStartConfiguration.MODE, Mode.GENERATE);
webApp.setAttribute(QuickStartConfiguration.QUICKSTART_WEB_XML, descriptor);
ServerSupport.addWebApplication(server, webApp);
@ -158,7 +152,7 @@ public class JettyEffectiveWebXml extends JettyRunMojo
try
{
//just show the result in the log
getLog().info(IO.toString(webApp.getQuickStartWebDescriptor().getInputStream()));
getLog().info(IO.toString(((Resource)webApp.getAttribute(QuickStartConfiguration.QUICKSTART_WEB_XML)).getInputStream()));
}
catch (Exception e)
{

View File

@ -43,6 +43,8 @@ import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.plugins.annotations.ResolutionScope;
import org.eclipse.jetty.annotations.AnnotationConfiguration;
import org.eclipse.jetty.quickstart.QuickStartConfiguration;
import org.eclipse.jetty.quickstart.QuickStartConfiguration.Mode;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.resource.Resource;
@ -209,12 +211,6 @@ public class JettyRunForkedMojo extends JettyRunMojo
configureWebApplication();
//set the webapp up to do very little other than generate the quickstart-web.xml
webApp.setCopyWebDir(false);
webApp.setCopyWebInf(false);
webApp.setGenerateQuickStart(true);
if (webApp.getQuickStartWebDescriptor() == null)
{
if (forkWebXml == null)
forkWebXml = new File(target, "fork-web.xml");
@ -223,8 +219,11 @@ public class JettyRunForkedMojo extends JettyRunMojo
if (!forkWebXml.exists())
forkWebXml.createNewFile();
webApp.setQuickStartWebDescriptor(Resource.newResource(forkWebXml));
}
webApp.addConfiguration(new MavenQuickStartConfiguration());
webApp.setAttribute(QuickStartConfiguration.MODE, Mode.GENERATE);
webApp.setAttribute(QuickStartConfiguration.QUICKSTART_WEB_XML, Resource.newResource(forkWebXml));
webApp.setCopyWebDir(false);
webApp.setCopyWebInf(false);
//add webapp to our fake server instance
ServerSupport.addWebApplication(server, webApp);
@ -240,11 +239,10 @@ public class JettyRunForkedMojo extends JettyRunMojo
//leave everything unpacked for the forked process to use
webApp.setPersistTempDirectory(true);
File props = null;
webApp.start(); //just enough to generate the quickstart
//save config of the webapp BEFORE we stop
final File props = prepareConfiguration();
props = prepareConfiguration();
webApp.stop();
if (tpool != null)

View File

@ -34,7 +34,6 @@ import org.eclipse.jetty.annotations.AnnotationConfiguration;
import org.eclipse.jetty.plus.webapp.EnvConfiguration;
import org.eclipse.jetty.plus.webapp.PlusConfiguration;
import org.eclipse.jetty.quickstart.QuickStartConfiguration;
import org.eclipse.jetty.quickstart.QuickStartConfiguration.Mode;
import org.eclipse.jetty.servlet.FilterHolder;
import org.eclipse.jetty.servlet.FilterMapping;
import org.eclipse.jetty.servlet.ServletHolder;
@ -105,8 +104,6 @@ public class JettyWebAppContext extends WebAppContext
*/
private boolean _baseAppFirst = true;
private boolean _isGenerateQuickStart;
public JettyWebAppContext() throws Exception
{
super();
@ -117,6 +114,8 @@ public class JettyWebAppContext extends WebAppContext
addConfiguration(new EnvConfiguration());
addConfiguration(new PlusConfiguration());
addConfiguration(new AnnotationConfiguration());
setAttribute(QuickStartConfiguration.ORIGIN_ATTRIBUTE, "origin");
}
public void setContainerIncludeJarPattern(String pattern)
@ -210,27 +209,6 @@ public class JettyWebAppContext extends WebAppContext
return attr == null ? null : attr.toString();
}
/**
* Toggle whether or not the origin attribute will be generated into the
* xml.
*
* @param generateOrigin if true then the origin of each xml element is
* added, otherwise it is omitted.
*/
public void setGenerateOrigin(boolean generateOrigin)
{
setAttribute(QuickStartConfiguration.GENERATE_ORIGIN, generateOrigin);
}
/**
* @return true if the origin attribute will be generated, false otherwise
*/
public boolean isGenerateOrigin()
{
Object attr = getAttribute(QuickStartConfiguration.GENERATE_ORIGIN);
return attr == null ? false : Boolean.valueOf(attr.toString());
}
public List<Overlay> getOverlays()
{
return _overlays;
@ -246,35 +224,6 @@ public class JettyWebAppContext extends WebAppContext
return _baseAppFirst;
}
/**
* Set the file to use into which to generate the quickstart output.
*
* @param quickStartWebXml the full path to the file to use
*/
public void setQuickStartWebDescriptor(String quickStartWebXml) throws Exception
{
setQuickStartWebDescriptor(Resource.newResource(quickStartWebXml));
}
/**
* Set the Resource to use into which to generate the quickstart output.
*/
protected void setQuickStartWebDescriptor(Resource quickStartWebXml)
{
setAttribute(QuickStartConfiguration.QUICKSTART_WEB_XML, quickStartWebXml.toString());
}
public Resource getQuickStartWebDescriptor() throws Exception
{
Object o = getAttribute(QuickStartConfiguration.QUICKSTART_WEB_XML);
if (o == null)
return null;
else if (o instanceof Resource)
return (Resource)o;
else
return Resource.newResource((String)o);
}
/**
* This method is provided as a convenience for jetty maven plugin
* configuration
@ -307,41 +256,9 @@ public class JettyWebAppContext extends WebAppContext
return _webInfClasses;
}
/**
* If true, a quickstart for the webapp is generated.
*
* @param quickStart if true the quickstart is generated, false otherwise
*/
public void setGenerateQuickStart(boolean quickStart)
{
_isGenerateQuickStart = quickStart;
}
public boolean isGenerateQuickStart()
{
return _isGenerateQuickStart;
}
@Override
public void doStart() throws Exception
{
// choose if this will be a quickstart or normal start
if (!isGenerateQuickStart() && getQuickStartWebDescriptor() != null)
{
MavenQuickStartConfiguration quickStart = new MavenQuickStartConfiguration();
quickStart.setMode(Mode.QUICKSTART);
quickStart.setQuickStartWebXml(getQuickStartWebDescriptor());
addConfiguration(quickStart);
}
else if (isGenerateQuickStart())
{
MavenQuickStartConfiguration quickStart = new MavenQuickStartConfiguration();
quickStart.setMode(Mode.GENERATE);
quickStart.setQuickStartWebXml(getQuickStartWebDescriptor());
addConfiguration(quickStart);
}
// Set up the pattern that tells us where the jars are that need
// scanning

View File

@ -18,15 +18,12 @@
package org.eclipse.jetty.maven.plugin;
import java.io.File;
import org.eclipse.jetty.quickstart.QuickStartConfiguration;
import org.eclipse.jetty.util.IO;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.resource.Resource;
import org.eclipse.jetty.util.resource.ResourceCollection;
import org.eclipse.jetty.webapp.WebAppClassLoader;
import org.eclipse.jetty.webapp.WebAppContext;
/**
@ -36,56 +33,6 @@ public class MavenQuickStartConfiguration extends QuickStartConfiguration
{
private static final Logger LOG = Log.getLogger(QuickStartConfiguration.class);
private Resource _quickStartWebXml; //the descriptor to use for starting/generating quickstart
public void setQuickStartWebXml(Resource quickStartWebXml)
{
_quickStartWebXml = quickStartWebXml;
}
@Override
public Resource getQuickStartWebXml(WebAppContext context) throws Exception
{
if (_quickStartWebXml == null)
return super.getQuickStartWebXml(context);
return _quickStartWebXml;
}
@Override
public void preConfigure(WebAppContext context) throws Exception
{
//check that webapp is suitable for quick start
if (context.getBaseResource() == null)
throw new IllegalStateException("No location for webapp");
//look for quickstart-web.xml in WEB-INF of webapp
Resource quickStartWebXml = getQuickStartWebXml(context);
if (LOG.isDebugEnabled())
LOG.debug("quickStartWebXml={}", quickStartWebXml);
super.preConfigure(context);
}
@Override
public void configure(WebAppContext context) throws Exception
{
JettyWebAppContext jwac = (JettyWebAppContext)context;
//put the classes dir and all dependencies into the classpath
if (jwac.getClassPathFiles() != null)
{
if (LOG.isDebugEnabled())
LOG.debug("Setting up classpath ...");
for (File classPathFile : jwac.getClassPathFiles())
{
((WebAppClassLoader)context.getClassLoader()).addClassPath(classPathFile.getCanonicalPath());
}
}
//Set up the quickstart environment for the context
super.configure(context);
}
@Override
public void deconfigure(WebAppContext context) throws Exception
{

View File

@ -24,6 +24,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.jetty.quickstart.QuickStartConfiguration;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ShutdownMonitor;
@ -72,13 +73,8 @@ public class Starter
//configure webapp from properties file describing unassembled webapp
configureWebApp();
//make it a quickstart if the quickstart-web.xml file exists
if (webApp.getTempDirectory() != null)
{
File qs = new File(webApp.getTempDirectory(), "quickstart-web.xml");
if (qs.exists() && qs.isFile())
webApp.setQuickStartWebDescriptor(Resource.newResource(qs));
}
webApp.addConfiguration(new QuickStartConfiguration());
webApp.setAttribute(QuickStartConfiguration.MODE, QuickStartConfiguration.Mode.QUICKSTART);
ServerSupport.addWebApplication(server, webApp);
@ -232,7 +228,9 @@ public class Starter
public static final void main(String[] args)
{
if (args == null)
{
System.exit(1);
}
Starter starter = null;
try

View File

@ -27,6 +27,7 @@ import java.util.List;
import java.util.Map;
import java.util.Properties;
import org.eclipse.jetty.quickstart.QuickStartConfiguration;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.resource.Resource;
@ -71,9 +72,10 @@ public class WebAppPropertyConverter
props.put("web.xml", webApp.getDescriptor());
}
if (webApp.getQuickStartWebDescriptor() != null)
Object tmp = webApp.getAttribute(QuickStartConfiguration.QUICKSTART_WEB_XML);
if (tmp != null)
{
props.put("quickstart.web.xml", webApp.getQuickStartWebDescriptor().getFile().getAbsolutePath());
props.put("quickstart.web.xml", tmp.toString());
}
//sort out the context path
@ -183,11 +185,10 @@ public class WebAppPropertyConverter
if (!StringUtil.isBlank(str))
webApp.setDescriptor(str);
//TODO the WebAppStarter class doesn't set up the QUICKSTART_CONFIGURATION_CLASSES, but the Starter class does!!!
str = props.getProperty("quickstart.web.xml");
if (!StringUtil.isBlank(str))
{
webApp.setQuickStartWebDescriptor(Resource.newResource(new File(str)));
webApp.setAttribute(QuickStartConfiguration.QUICKSTART_WEB_XML, Resource.newResource(str));
}
// - the tmp directory

View File

@ -1,27 +0,0 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "https://www.eclipse.org/jetty/configure_10_0.dtd">
<!-- An example context XML for a quickstart webapp
A quick started webapp has all the jar scanning and fragment resolution done in a
Preconfigure phase, with all the discovered configuration encoded into a
WEB-INF/quickstart-web.xml file.
This allows very rapid and precise starting of a webapp, without the risk of accidental
deployment of other discovered resources. This is above and beyond what is available
with web.xml meta-data-complete, as it also prevents scanning for ServletContainer
initialisers and any annotations/classes they require.
If autoPreconfigure is set to true, then the webapp will be preconfigured the first
time it is run.
-->
<Configure class="org.eclipse.jetty.quickstart.QuickStartWebApp">
<Set name="autoPreconfigure">true</Set>
<Set name="contextPath">/</Set>
<Set name="war"><Property name="jetty.webapps" default="."/>/application.war</Set>
<!--
<Call name="setInitParameter">
<Arg>org.eclipse.jetty.jsp.precompiled</Arg>
<Arg>true</Arg>
</Call>
-->
</Configure>

View File

@ -0,0 +1,31 @@
<?xml version="1.0"?>
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "https://www.eclipse.org/jetty/configure_10_0.dtd">
<Configure id="Server" class="org.eclipse.jetty.server.Server">
<Call class="org.eclipse.jetty.quickstart.QuickStartConfiguration" name="configureMode">
<Arg><Ref refid="Server"/></Arg>
<Arg><Property name="jetty.quickstart.mode"/></Arg>
</Call>
<Ref refid="webappprovider">
<Get name="configurationManager">
<Get name="properties">
<Put name="jetty.quickstart.mode"><Property name="jetty.quickstart.mode"/></Put>
<Put name="jetty.quickstart.origin"><Property name="jetty.quickstart.origin"/></Put>
<Put name="jetty.quickstart.xml"><Property name="jetty.quickstart.xml"/></Put>
</Get>
</Get>
</Ref>
<Ref refid="DeploymentManager">
<Call name="addLifeCycleBinding">
<Arg>
<New class="org.eclipse.jetty.deploy.bindings.GlobalWebappConfigBinding">
<Set name="jettyXml">
<Property name="jetty.base"/>/etc/quickstart-webapp.xml
</Set>
</New>
</Arg>
</Call>
</Ref>
</Configure>

View File

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "https://www.eclipse.org/jetty/configure_10_0.dtd">
<Configure class="org.eclipse.jetty.webapp.WebAppContext">
<Call name="setAttribute">
<Arg>org.eclipse.jetty.quickstart.origin</Arg>
<Arg><Property name="jetty.quickstart.origin" default="origin"/></Arg>
</Call>
<Call name="setAttribute">
<Arg>org.eclipse.jetty.quickstart.xml</Arg>
<Arg><Property name="jetty.quickstart.xml"/></Arg>
</Call>
<Call name="setAttribute">
<Arg>org.eclipse.jetty.quickstart.mode</Arg>
<Arg>
<Call class="org.eclipse.jetty.quickstart.QuickStartConfiguration$Mode" name="valueOf">
<Arg><Property name="jetty.quickstart.mode" default="AUTO"/></Arg>
</Call>
</Arg>
</Call>
<Set name="extractWAR">true</Set>
<Set name="copyWebDir">false</Set>
<Set name="copyWebInf">false</Set>
</Configure>

View File

@ -6,8 +6,21 @@ deployment of preconfigured webapplications.
[depend]
server
plus
annotations
deploy
[lib]
lib/jetty-quickstart-${jetty.version}.jar
[xml]
etc/jetty-quickstart.xml
[files]
basehome:modules/jetty-quickstart.d/quickstart-webapp.xml|etc/quickstart-webapp.xml
[ini-template]
# Modes are AUTO, GENERATE, QUICKSTART
# jetty.quickstart.mode=AUTO
# jetty.quickstart.origin=origin
# jetty.quickstart.xml=

View File

@ -20,11 +20,15 @@ package org.eclipse.jetty.quickstart;
import java.util.Locale;
import org.eclipse.jetty.annotations.AnnotationConfiguration;
import org.eclipse.jetty.plus.webapp.EnvConfiguration;
import org.eclipse.jetty.plus.webapp.PlusConfiguration;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.resource.JarResource;
import org.eclipse.jetty.util.resource.Resource;
import org.eclipse.jetty.webapp.WebAppContext;
import org.eclipse.jetty.xml.XmlConfiguration;
public class PreconfigureQuickStartWar
@ -98,7 +102,13 @@ public class PreconfigureQuickStartWar
final Server server = new Server();
QuickStartWebApp webapp = new QuickStartWebApp();
WebAppContext webapp = new WebAppContext();
webapp.addConfiguration(new QuickStartConfiguration(),
new EnvConfiguration(),
new PlusConfiguration(),
new AnnotationConfiguration());
webapp.setAttribute(QuickStartConfiguration.MODE, QuickStartConfiguration.Mode.GENERATE);
webapp.setAttribute(QuickStartConfiguration.ORIGIN_ATTRIBUTE, "");
if (xml != null)
{
@ -108,11 +118,22 @@ public class PreconfigureQuickStartWar
xmlConfiguration.configure(webapp);
}
webapp.setResourceBase(dir.getFile().getAbsolutePath());
webapp.setMode(QuickStartConfiguration.Mode.GENERATE);
server.setHandler(webapp);
try
{
server.setDryRun(true);
server.start();
}
catch (Exception e)
{
throw e;
}
finally
{
if (!server.isStopped())
server.stop();
}
}
private static void error(String message)
{

View File

@ -18,6 +18,7 @@
package org.eclipse.jetty.quickstart;
import java.io.File;
import java.io.IOException;
import java.util.HashSet;
import java.util.Set;
@ -26,6 +27,8 @@ import java.util.stream.Collectors;
import org.eclipse.jetty.annotations.AnnotationConfiguration;
import org.eclipse.jetty.annotations.AnnotationDecorator;
import org.eclipse.jetty.annotations.ServletContainerInitializersStarter;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.resource.Resource;
@ -39,17 +42,16 @@ import org.eclipse.jetty.webapp.WebXmlConfiguration;
/**
* QuickStartConfiguration
* <p>
* Re-inflate a deployable webapp from a saved effective-web.xml
* which combines all pre-parsed web xml descriptors and annotations.
* Prepare for quickstart generation, or usage.
*/
public class QuickStartConfiguration extends AbstractConfiguration
{
private static final Logger LOG = Log.getLogger(QuickStartConfiguration.class);
public static final Set<Class<? extends Configuration>> __replacedConfigurations = new HashSet<>();
public static final String ORIGIN_ATTRIBUTE = "org.eclipse.jetty.quickstart.ORIGIN_ATTRIBUTE";
public static final String GENERATE_ORIGIN = "org.eclipse.jetty.quickstart.GENERATE_ORIGIN";
public static final String QUICKSTART_WEB_XML = "org.eclipse.jetty.quickstart.QUICKSTART_WEB_XML";
public static final String ORIGIN_ATTRIBUTE = "org.eclipse.jetty.quickstart.origin";
public static final String QUICKSTART_WEB_XML = "org.eclipse.jetty.quickstart.xml";
public static final String MODE = "org.eclipse.jetty.quickstart.mode";
static
{
@ -59,41 +61,35 @@ public class QuickStartConfiguration extends AbstractConfiguration
__replacedConfigurations.add(org.eclipse.jetty.annotations.AnnotationConfiguration.class);
}
;
/** Configure the server for the quickstart mode.
* <p>In practise this means calling <code>server.setDryRun(true)</code> for GENERATE mode</p>
* @see Server#setDryRun(boolean)
* @param server The server to configure
* @param mode The quickstart mode
*/
public static void configureMode(Server server, String mode)
{
if (mode != null && Mode.valueOf(mode) == Mode.GENERATE)
server.setDryRun(true);
}
public enum Mode
{
DISABLED, // No Quick start
GENERATE, // Generate quickstart-web.xml and then stop
AUTO, // use or generate depending on the existance of quickstart-web.xml
QUICKSTART // Use quickstart-web.xml
}
;
private Mode _mode = Mode.AUTO;
private boolean _quickStart;
public QuickStartConfiguration()
{
super(true);
super(false);
addDependencies(WebInfConfiguration.class);
addDependents(WebXmlConfiguration.class);
}
public void setMode(Mode mode)
{
_mode = mode;
}
public Mode getMode()
{
return _mode;
}
/**
* @see org.eclipse.jetty.webapp.AbstractConfiguration#preConfigure(org.eclipse.jetty.webapp.WebAppContext)
*/
@Override
public void preConfigure(WebAppContext context) throws Exception
{
@ -106,39 +102,46 @@ public class QuickStartConfiguration extends AbstractConfiguration
Resource quickStartWebXml = getQuickStartWebXml(context);
LOG.debug("quickStartWebXml={} exists={}", quickStartWebXml, quickStartWebXml.exists());
//Get the mode
Mode mode = (Mode)context.getAttribute(MODE);
if (mode != null)
_mode = mode;
_quickStart = false;
switch (_mode)
{
case DISABLED:
super.preConfigure(context);
break;
case GENERATE:
{
if (quickStartWebXml.exists())
LOG.info("Regenerating {}", quickStartWebXml);
else
LOG.info("Generating {}", quickStartWebXml);
super.preConfigure(context);
//generate the quickstart file then abort
QuickStartGeneratorConfiguration generator = new QuickStartGeneratorConfiguration(true);
configure(generator, context);
context.addConfiguration(generator);
break;
}
case AUTO:
{
if (quickStartWebXml.exists())
quickStart(context, quickStartWebXml);
{
quickStart(context);
}
else
{
if (LOG.isDebugEnabled())
LOG.debug("No quickstart xml file, starting webapp {} normally", context);
super.preConfigure(context);
QuickStartGeneratorConfiguration generator = new QuickStartGeneratorConfiguration(false);
configure(generator, context);
context.addConfiguration(generator);
}
break;
}
case QUICKSTART:
if (quickStartWebXml.exists())
quickStart(context, quickStartWebXml);
quickStart(context);
else
throw new IllegalStateException("No " + quickStartWebXml);
break;
@ -151,27 +154,20 @@ public class QuickStartConfiguration extends AbstractConfiguration
protected void configure(QuickStartGeneratorConfiguration generator, WebAppContext context) throws IOException
{
Object attr;
attr = context.getAttribute(GENERATE_ORIGIN);
if (attr != null)
generator.setGenerateOrigin(Boolean.valueOf(attr.toString()));
attr = context.getAttribute(ORIGIN_ATTRIBUTE);
if (attr != null)
generator.setOriginAttribute(attr.toString());
attr = context.getAttribute(QUICKSTART_WEB_XML);
if (attr instanceof Resource)
generator.setQuickStartWebXml((Resource)attr);
else if (attr != null)
generator.setQuickStartWebXml(Resource.newResource(attr.toString()));
generator.setQuickStartWebXml((Resource)context.getAttribute(QUICKSTART_WEB_XML));
}
/**
* @see org.eclipse.jetty.webapp.AbstractConfiguration#configure(org.eclipse.jetty.webapp.WebAppContext)
*/
@Override
public void configure(WebAppContext context) throws Exception
{
if (!_quickStart)
{
super.configure(context);
}
else
{
//add the processor to handle normal web.xml content
@ -195,14 +191,15 @@ public class QuickStartConfiguration extends AbstractConfiguration
}
}
protected void quickStart(WebAppContext context, Resource quickStartWebXml)
protected void quickStart(WebAppContext context)
throws Exception
{
LOG.info("Quickstarting {}", context);
_quickStart = true;
context.setConfigurations(context.getWebAppConfigurations().stream()
.filter(c -> !__replacedConfigurations.contains(c.replaces()) && !__replacedConfigurations.contains(c.getClass()))
.collect(Collectors.toList()).toArray(new Configuration[]{}));
context.getMetaData().setWebXml(quickStartWebXml);
context.getMetaData().setWebXml((Resource)context.getAttribute(QUICKSTART_WEB_XML));
context.getServletContext().setEffectiveMajorVersion(context.getMetaData().getWebXml().getMajorVersion());
context.getServletContext().setEffectiveMinorVersion(context.getMetaData().getWebXml().getMinorVersion());
}
@ -216,12 +213,38 @@ public class QuickStartConfiguration extends AbstractConfiguration
*/
public Resource getQuickStartWebXml(WebAppContext context) throws Exception
{
Object attr = context.getAttribute(QUICKSTART_WEB_XML);
if (attr instanceof Resource)
return (Resource)attr;
Resource webInf = context.getWebInf();
if (webInf == null || !webInf.exists())
throw new IllegalStateException("No WEB-INF");
LOG.debug("webinf={}", webInf);
{
File tmp = new File(context.getBaseResource().getFile(), "WEB-INF");
tmp.mkdirs();
webInf = context.getWebInf();
}
Resource quickStartWebXml = webInf.addPath("quickstart-web.xml");
return quickStartWebXml;
Resource qstart;
if (attr == null || StringUtil.isBlank(attr.toString()))
{
qstart = webInf.addPath("quickstart-web.xml");
}
else
{
try
{
// Try a relative resolution
qstart = Resource.newResource(webInf.getFile().toPath().resolve(attr.toString()));
}
catch (Throwable th)
{
// try as a resource
qstart = (Resource.newResource(attr.toString()));
}
context.setAttribute(QUICKSTART_WEB_XML, qstart);
}
context.setAttribute(QUICKSTART_WEB_XML, qstart);
return qstart;
}
}

View File

@ -52,6 +52,7 @@ import org.eclipse.jetty.servlet.ServletHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.eclipse.jetty.servlet.ServletMapping;
import org.eclipse.jetty.util.QuotedStringTokenizer;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.resource.Resource;
@ -61,10 +62,11 @@ import org.eclipse.jetty.webapp.MetaData;
import org.eclipse.jetty.webapp.MetaData.OriginInfo;
import org.eclipse.jetty.webapp.MetaInfConfiguration;
import org.eclipse.jetty.webapp.WebAppContext;
import org.eclipse.jetty.webapp.WebInfConfiguration;
import org.eclipse.jetty.xml.XmlAppendable;
/**
* QuickStartDescriptorGenerator
* QuickStartGeneratorConfiguration
* <p>
* Generate an effective web.xml from a WebAppContext, including all components
* from web.xml, web-fragment.xmls annotations etc.
@ -81,7 +83,6 @@ public class QuickStartGeneratorConfiguration extends AbstractConfiguration
protected final boolean _abort;
protected String _originAttribute;
protected boolean _generateOrigin;
protected int _count;
protected Resource _quickStartWebXml;
@ -116,22 +117,6 @@ public class QuickStartGeneratorConfiguration extends AbstractConfiguration
return _originAttribute;
}
/**
* @return the generateOrigin
*/
public boolean isGenerateOrigin()
{
return _generateOrigin;
}
/**
* @param generateOrigin the generateOrigin to set
*/
public void setGenerateOrigin(boolean generateOrigin)
{
_generateOrigin = generateOrigin;
}
public Resource getQuickStartWebXml()
{
return _quickStartWebXml;
@ -163,8 +148,6 @@ public class QuickStartGeneratorConfiguration extends AbstractConfiguration
if (context.getBaseResource() == null)
throw new IllegalArgumentException("No base resource for " + this);
LOG.info("Quickstart generating");
MetaData md = context.getMetaData();
Map<String, String> webappAttr = new HashMap<>();
@ -195,13 +178,13 @@ public class QuickStartGeneratorConfiguration extends AbstractConfiguration
//the META-INF/resources discovered
addContextParamFromAttribute(context, out, MetaInfConfiguration.METAINF_RESOURCES, normalizer);
// the default-context-path, if presernt
// the default-context-path, if present
String defaultContextPath = (String)context.getAttribute("default-context-path");
if (defaultContextPath != null)
out.tag("default-context-path", defaultContextPath);
//add the name of the origin attribute, if it is being used
if (_generateOrigin)
if (StringUtil.isNotBlank(_originAttribute))
{
out.openTag("context-param")
.tag("param-name", ORIGIN)
@ -766,7 +749,7 @@ public class QuickStartGeneratorConfiguration extends AbstractConfiguration
*/
public Map<String, String> origin(MetaData md, String name)
{
if (!(_generateOrigin || LOG.isDebugEnabled()))
if (StringUtil.isBlank(_originAttribute))
return Collections.emptyMap();
if (name == null)
return Collections.emptyMap();
@ -792,13 +775,19 @@ public class QuickStartGeneratorConfiguration extends AbstractConfiguration
{
MetaData metadata = context.getMetaData();
metadata.resolve(context);
Resource quickStartWebXml = _quickStartWebXml;
if (_quickStartWebXml == null)
quickStartWebXml = context.getWebInf().addPath("/quickstart-web.xml");
try (FileOutputStream fos = new FileOutputStream(quickStartWebXml.getFile(), false))
try (FileOutputStream fos = new FileOutputStream(_quickStartWebXml.getFile(), false))
{
generateQuickStartWebXml(context, fos);
LOG.info("Generated {}", _quickStartWebXml);
if (context.getAttribute(WebInfConfiguration.TEMPORARY_RESOURCE_BASE) != null && !context.isPersistTempDirectory())
LOG.warn("Generated to non persistent location: " + _quickStartWebXml);
}
}
@Override
public void deconfigure(WebAppContext context) throws Exception
{
super.deconfigure(context);
}
}

View File

@ -1,90 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2019 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.quickstart;
import org.eclipse.jetty.annotations.AnnotationConfiguration;
import org.eclipse.jetty.plus.webapp.EnvConfiguration;
import org.eclipse.jetty.plus.webapp.PlusConfiguration;
import org.eclipse.jetty.quickstart.QuickStartConfiguration.Mode;
import org.eclipse.jetty.webapp.WebAppContext;
/**
* QuickStartWar
*/
public class QuickStartWebApp extends WebAppContext
{
private final QuickStartConfiguration _quickStartConfiguration;
private String _originAttribute;
private boolean _generateOrigin;
public QuickStartWebApp()
{
super();
addConfiguration(
_quickStartConfiguration = new QuickStartConfiguration(),
new EnvConfiguration(),
new PlusConfiguration(),
new AnnotationConfiguration());
setExtractWAR(true);
setCopyWebDir(false);
setCopyWebInf(false);
}
public void setOriginAttribute(String name)
{
setAttribute(QuickStartConfiguration.ORIGIN_ATTRIBUTE, name);
}
/**
* @return the originAttribute
*/
public String getOriginAttribute()
{
Object attr = getAttribute(QuickStartConfiguration.ORIGIN_ATTRIBUTE);
return attr == null ? null : attr.toString();
}
/**
* @param generateOrigin the generateOrigin to set
*/
public void setGenerateOrigin(boolean generateOrigin)
{
setAttribute(QuickStartConfiguration.GENERATE_ORIGIN, generateOrigin);
}
/**
* @return the generateOrigin
*/
public boolean isGenerateOrigin()
{
Object attr = getAttribute(QuickStartConfiguration.GENERATE_ORIGIN);
return attr == null ? false : Boolean.valueOf(attr.toString());
}
public Mode getMode()
{
return _quickStartConfiguration.getMode();
}
public void setMode(Mode mode)
{
_quickStartConfiguration.setMode(mode);
}
}

View File

@ -27,6 +27,7 @@ import org.eclipse.jetty.servlet.ListenerHolder;
import org.eclipse.jetty.servlet.ServletHolder;
import org.eclipse.jetty.toolchain.test.FS;
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
import org.eclipse.jetty.webapp.WebAppContext;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@ -61,10 +62,11 @@ public class TestQuickStart
Server server = new Server();
//generate a quickstart-web.xml
QuickStartWebApp quickstart = new QuickStartWebApp();
WebAppContext quickstart = new WebAppContext();
quickstart.addConfiguration(new QuickStartConfiguration());
quickstart.setAttribute(QuickStartConfiguration.MODE, QuickStartConfiguration.Mode.GENERATE);
quickstart.setAttribute(QuickStartConfiguration.ORIGIN_ATTRIBUTE, "origin");
quickstart.setResourceBase(testDir.getAbsolutePath());
quickstart.setMode(QuickStartConfiguration.Mode.GENERATE);
quickstart.setGenerateOrigin(true);
ServletHolder fooHolder = new ServletHolder();
fooHolder.setServlet(new FooServlet());
fooHolder.setName("foo");
@ -73,19 +75,22 @@ public class TestQuickStart
lholder.setListener(new FooContextListener());
quickstart.getServletHandler().addListener(lholder);
server.setHandler(quickstart);
server.setDryRun(true);
server.start();
server.stop();
assertTrue(quickstartXml.exists());
//now run the webapp again purely from the generated quickstart
QuickStartWebApp webapp = new QuickStartWebApp();
WebAppContext webapp = new WebAppContext();
webapp.setResourceBase(testDir.getAbsolutePath());
webapp.setMode(QuickStartConfiguration.Mode.QUICKSTART);
webapp.addConfiguration(new QuickStartConfiguration());
webapp.setAttribute(QuickStartConfiguration.MODE, QuickStartConfiguration.Mode.QUICKSTART);
webapp.setClassLoader(new URLClassLoader(new URL[0], Thread.currentThread().getContextClassLoader()));
server.setHandler(webapp);
server.setDryRun(false);
server.start();
server.dumpStdErr();
//verify that FooServlet is now mapped to / and not the DefaultServlet
ServletHolder sh = webapp.getServletHandler().getMappedServlet("/").getResource();
@ -104,28 +109,30 @@ public class TestQuickStart
Server server = new Server();
// generate a quickstart-web.xml
QuickStartWebApp quickstart = new QuickStartWebApp();
WebAppContext quickstart = new WebAppContext();
quickstart.setResourceBase(testDir.getAbsolutePath());
quickstart.setMode(QuickStartConfiguration.Mode.GENERATE);
quickstart.setGenerateOrigin(true);
quickstart.addConfiguration(new QuickStartConfiguration());
quickstart.setAttribute(QuickStartConfiguration.MODE, QuickStartConfiguration.Mode.GENERATE);
quickstart.setAttribute(QuickStartConfiguration.ORIGIN_ATTRIBUTE, "origin");
quickstart.setDescriptor(MavenTestingUtils.getTestResourceFile("web.xml").getAbsolutePath());
quickstart.setContextPath("/foo");
server.setHandler(quickstart);
server.setDryRun(true);
server.start();
assertEquals("/foo", quickstart.getContextPath());
assertFalse(quickstart.isContextPathDefault());
server.stop();
assertTrue(quickstartXml.exists());
// quick start
QuickStartWebApp webapp = new QuickStartWebApp();
WebAppContext webapp = new WebAppContext();
webapp.addConfiguration(new QuickStartConfiguration());
quickstart.setAttribute(QuickStartConfiguration.MODE, QuickStartConfiguration.Mode.QUICKSTART);
webapp.setResourceBase(testDir.getAbsolutePath());
webapp.setMode(QuickStartConfiguration.Mode.QUICKSTART);
webapp.setClassLoader(new URLClassLoader(new URL[0], Thread.currentThread().getContextClassLoader()));
server.setHandler(webapp);
server.setDryRun(false);
server.start();
// verify the context path is the default-context-path

View File

@ -79,10 +79,11 @@ public class Server extends HandlerWrapper implements Attributes
private final List<Connector> _connectors = new CopyOnWriteArrayList<>();
private SessionIdManager _sessionIdManager;
private boolean _stopAtShutdown;
private boolean _dumpAfterStart = false;
private boolean _dumpBeforeStop = false;
private boolean _dumpAfterStart;
private boolean _dumpBeforeStop;
private ErrorHandler _errorHandler;
private RequestLog _requestLog;
private boolean _dryRun;
private final Locker _dateLocker = new Locker();
private volatile DateField _dateField;
@ -131,6 +132,16 @@ public class Server extends HandlerWrapper implements Attributes
setServer(this);
}
public boolean isDryRun()
{
return _dryRun;
}
public void setDryRun(boolean dryRun)
{
_dryRun = dryRun;
}
public RequestLog getRequestLog()
{
return _requestLog;
@ -367,6 +378,8 @@ public class Server extends HandlerWrapper implements Attributes
MultiException mex = new MultiException();
// Open network connector to ensure ports are available
if (!_dryRun)
{
_connectors.stream().filter(NetworkConnector.class::isInstance).map(NetworkConnector.class::cast).forEach(connector ->
{
try
@ -378,14 +391,20 @@ public class Server extends HandlerWrapper implements Attributes
mex.add(th);
}
});
// Throw now if verified start sequence and there was an open exception
mex.ifExceptionThrow();
}
// Start the server and components, but not connectors!
// #start(LifeCycle) is overridden so that connectors are not started
super.doStart();
if (_dryRun)
{
LOG.info(String.format("Started(dry run) %s @%dms", this, Uptime.getUptime()));
throw new StopException();
}
// start connectors
for (Connector connector : _connectors)
{
@ -400,9 +419,8 @@ public class Server extends HandlerWrapper implements Attributes
_connectors.stream().filter(LifeCycle::isRunning).map(Object.class::cast).forEach(LifeCycle::stop);
}
}
mex.ifExceptionThrow();
LOG.info(String.format("Started @%dms", Uptime.getUptime()));
LOG.info(String.format("Started %s @%dms", this, Uptime.getUptime()));
}
catch (Throwable th)
{
@ -423,7 +441,7 @@ public class Server extends HandlerWrapper implements Attributes
}
finally
{
if (isDumpAfterStart())
if (isDumpAfterStart() && !(_dryRun && isDumpBeforeStop()))
dumpStdErr();
}
}
@ -442,6 +460,7 @@ public class Server extends HandlerWrapper implements Attributes
if (isDumpBeforeStop())
dumpStdErr();
LOG.info(String.format("Stopped %s", this));
if (LOG.isDebugEnabled())
LOG.debug("doStop {}", this);

View File

@ -123,7 +123,7 @@ public abstract class AbstractHandler extends ContainerLifeCycle implements Hand
if (_server == server)
return;
if (isStarted())
throw new IllegalStateException(STARTED);
throw new IllegalStateException(getState());
_server = server;
}

View File

@ -128,7 +128,7 @@ public abstract class AbstractHandlerContainer extends AbstractHandler implement
return;
if (isStarted())
throw new IllegalStateException(STARTED);
throw new IllegalStateException(getState());
super.setServer(server);
Handler[] handlers = getHandlers();

View File

@ -2231,6 +2231,9 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
@Override
public void log(String message, Throwable throwable)
{
if (throwable == null)
_logger.warn(message);
else
_logger.warn(message, throwable);
}

View File

@ -82,7 +82,7 @@ public class HandlerCollection extends AbstractHandlerContainer
public void setHandlers(Handler[] handlers)
{
if (!_mutableWhenRunning && isStarted())
throw new IllegalStateException(STARTED);
throw new IllegalStateException(getState());
while (true)
{

View File

@ -74,7 +74,7 @@ public class HandlerWrapper extends AbstractHandlerContainer
public void setHandler(Handler handler)
{
if (isStarted())
throw new IllegalStateException(STARTED);
throw new IllegalStateException(getState());
// check for loops
if (handler == this || (handler instanceof HandlerContainer &&

View File

@ -34,27 +34,39 @@ public abstract class AbstractLifeCycle implements LifeCycle
{
private static final Logger LOG = Log.getLogger(AbstractLifeCycle.class);
public static final String STOPPED = "STOPPED";
public static final String FAILED = "FAILED";
public static final String STARTING = "STARTING";
public static final String STARTED = "STARTED";
public static final String STOPPING = "STOPPING";
public static final String RUNNING = "RUNNING";
enum State
{
STOPPED,
STARTING,
STARTED,
STOPPING,
FAILED
}
public static final String STOPPED = State.STOPPED.toString();
public static final String FAILED = State.FAILED.toString();
public static final String STARTING = State.STARTING.toString();
public static final String STARTED = State.STARTED.toString();
public static final String STOPPING = State.STOPPING.toString();
private final CopyOnWriteArrayList<LifeCycle.Listener> _listeners = new CopyOnWriteArrayList<LifeCycle.Listener>();
private final Object _lock = new Object();
private static final int STATE_FAILED = -1;
private static final int STATE_STOPPED = 0;
private static final int STATE_STARTING = 1;
private static final int STATE_STARTED = 2;
private static final int STATE_STOPPING = 3;
private volatile int _state = STATE_STOPPED;
private volatile State _state = State.STOPPED;
private long _stopTimeout = 30000;
/**
* Method to override to start the lifecycle
* @throws StopException If thrown, the lifecycle will immediately be stopped.
* @throws Exception If there was a problem starting. Will cause a transition to FAILED state
*/
protected void doStart() throws Exception
{
}
/**
* Method to override to stop the lifecycle
* @throws Exception If there was a problem stopping. Will cause a transition to FAILED state
*/
protected void doStop() throws Exception
{
}
@ -66,12 +78,32 @@ public abstract class AbstractLifeCycle implements LifeCycle
{
try
{
if (_state == STATE_STARTED || _state == STATE_STARTING)
switch (_state)
{
case STARTED:
return;
case STARTING:
case STOPPING:
throw new IllegalStateException(getState());
default:
try
{
setStarting();
doStart();
setStarted();
}
catch (StopException e)
{
if (LOG.isDebugEnabled())
LOG.debug(e);
setStopping();
doStop();
setStopped();
}
}
}
catch (Throwable e)
{
setFailed(e);
@ -87,12 +119,21 @@ public abstract class AbstractLifeCycle implements LifeCycle
{
try
{
if (_state == STATE_STOPPING || _state == STATE_STOPPED)
switch (_state)
{
case STOPPED:
return;
case STARTING:
case STOPPING:
throw new IllegalStateException(getState());
default:
setStopping();
doStop();
setStopped();
}
}
catch (Throwable e)
{
setFailed(e);
@ -104,39 +145,45 @@ public abstract class AbstractLifeCycle implements LifeCycle
@Override
public boolean isRunning()
{
final int state = _state;
return state == STATE_STARTED || state == STATE_STARTING;
final State state = _state;
switch (state)
{
case STARTED:
case STARTING:
return true;
default:
return false;
}
}
@Override
public boolean isStarted()
{
return _state == STATE_STARTED;
return _state == State.STARTED;
}
@Override
public boolean isStarting()
{
return _state == STATE_STARTING;
return _state == State.STARTING;
}
@Override
public boolean isStopping()
{
return _state == STATE_STOPPING;
return _state == State.STOPPING;
}
@Override
public boolean isStopped()
{
return _state == STATE_STOPPED;
return _state == State.STOPPED;
}
@Override
public boolean isFailed()
{
return _state == STATE_FAILED;
return _state == State.FAILED;
}
@Override
@ -154,43 +201,32 @@ public abstract class AbstractLifeCycle implements LifeCycle
@ManagedAttribute(value = "Lifecycle State for this instance", readonly = true)
public String getState()
{
switch (_state)
{
case STATE_FAILED:
return FAILED;
case STATE_STARTING:
return STARTING;
case STATE_STARTED:
return STARTED;
case STATE_STOPPING:
return STOPPING;
case STATE_STOPPED:
return STOPPED;
default:
return null;
}
return _state.toString();
}
public static String getState(LifeCycle lc)
{
if (lc instanceof AbstractLifeCycle)
return ((AbstractLifeCycle)lc)._state.toString();
if (lc.isStarting())
return STARTING;
return State.STARTING.toString();
if (lc.isStarted())
return STARTED;
return State.STARTED.toString();
if (lc.isStopping())
return STOPPING;
return State.STOPPING.toString();
if (lc.isStopped())
return STOPPED;
return FAILED;
return State.STOPPED.toString();
return State.FAILED.toString();
}
private void setStarted()
{
_state = STATE_STARTED;
if (LOG.isDebugEnabled())
LOG.debug(STARTED + " @{}ms {}", Uptime.getUptime(), this);
for (Listener listener : _listeners)
if (_state == State.STARTING)
{
_state = State.STARTED;
if (LOG.isDebugEnabled())
LOG.debug("STARTED @{}ms {}", Uptime.getUptime(), this);
for (Listener listener : _listeners)
listener.lifeCycleStarted(this);
}
}
@ -198,41 +234,40 @@ public abstract class AbstractLifeCycle implements LifeCycle
private void setStarting()
{
if (LOG.isDebugEnabled())
LOG.debug("starting {}", this);
_state = STATE_STARTING;
LOG.debug("STARTING {}", this);
_state = State.STARTING;
for (Listener listener : _listeners)
{
listener.lifeCycleStarting(this);
}
}
private void setStopping()
{
if (LOG.isDebugEnabled())
LOG.debug("stopping {}", this);
_state = STATE_STOPPING;
LOG.debug("STOPPING {}", this);
_state = State.STOPPING;
for (Listener listener : _listeners)
{
listener.lifeCycleStopping(this);
}
}
private void setStopped()
{
_state = STATE_STOPPED;
if (_state == State.STOPPING)
{
_state = State.STOPPED;
if (LOG.isDebugEnabled())
LOG.debug("{} {}", STOPPED, this);
LOG.debug("STOPPED {}", this);
for (Listener listener : _listeners)
{
listener.lifeCycleStopped(this);
}
}
}
private void setFailed(Throwable th)
{
_state = STATE_FAILED;
_state = State.FAILED;
if (LOG.isDebugEnabled())
LOG.warn(FAILED + " " + this + ": " + th, th);
LOG.warn("FAILED " + this + ": " + th, th);
for (Listener listener : _listeners)
{
listener.lifeCycleFailure(this, th);
@ -290,4 +325,10 @@ public abstract class AbstractLifeCycle implements LifeCycle
}
return String.format("%s@%x{%s}", name, hashCode(), getState());
}
/**
* An exception, which if thrown by doStart will immediately stop the component
*/
public class StopException extends RuntimeException
{}
}

View File

@ -100,6 +100,8 @@ public class ContainerLifeCycle extends AbstractLifeCycle implements Container,
{
for (Bean b : _beans)
{
if (!isStarting())
break;
if (b._bean instanceof LifeCycle)
{
LifeCycle l = (LifeCycle)b._bean;
@ -127,8 +129,6 @@ public class ContainerLifeCycle extends AbstractLifeCycle implements Container,
}
}
}
super.doStart();
}
catch (Throwable th)
{
@ -193,6 +193,8 @@ public class ContainerLifeCycle extends AbstractLifeCycle implements Container,
MultiException mex = new MultiException();
for (Bean b : reverse)
{
if (!isStopping())
break;
if (b._managed == Managed.MANAGED && b._bean instanceof LifeCycle)
{
LifeCycle l = (LifeCycle)b._bean;

View File

@ -403,7 +403,7 @@ public class MetaData
p.process(context, getWebXml());
for (WebDescriptor wd : getOverrideWebs())
{
LOG.debug("process {} {}", context, wd);
LOG.debug("process {} {} {}", context, p, wd);
p.process(context, wd);
}
}

View File

@ -38,6 +38,7 @@ public class WebInfConfiguration extends AbstractConfiguration
private static final Logger LOG = Log.getLogger(WebInfConfiguration.class);
public static final String TEMPDIR_CONFIGURED = "org.eclipse.jetty.tmpdirConfigured";
public static final String TEMPORARY_RESOURCE_BASE = "org.eclipse.jetty.webapp.tmpResourceBase";
protected Resource _preUnpackBaseResource;
@ -338,8 +339,11 @@ public class WebInfConfiguration extends AbstractConfiguration
}
if (extractedWebAppDir == null)
{
// Then extract it if necessary to the temporary location
extractedWebAppDir = new File(context.getTempDirectory(), "webapp");
context.setAttribute(TEMPORARY_RESOURCE_BASE, extractedWebAppDir);
}
if (webApp.getFile() != null && webApp.getFile().isDirectory())
{

View File

@ -36,6 +36,7 @@ import java.nio.file.Paths;
import java.security.AccessController;
import java.security.PrivilegedExceptionAction;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
@ -1747,6 +1748,8 @@ public class XmlConfiguration
properties.putAll(System.getProperties());
// For all arguments, load properties
if (LOG.isDebugEnabled())
LOG.debug("args={}", Arrays.asList(args));
for (String arg : args)
{
if (arg.indexOf('=') >= 0)
@ -1785,17 +1788,34 @@ public class XmlConfiguration
}
}
if (LOG.isDebugEnabled())
LOG.debug("objects={}", Arrays.asList(objects));
// For all objects created by XmlConfigurations, start them if they are lifecycles.
List<LifeCycle> started = new ArrayList<>(objects.size());
for (Object obj : objects)
{
if (obj instanceof LifeCycle)
{
LifeCycle lc = (LifeCycle)obj;
if (!lc.isRunning())
{
lc.start();
if (lc.isStarted())
started.add(lc);
else
{
// Failed to start a component, so stop all started components
Collections.reverse(started);
for (LifeCycle slc : started)
{
slc.stop();
}
break;
}
}
}
}
return null;
});
}

View File

@ -103,7 +103,7 @@ public class BadAppTests extends AbstractDistributionTest
int port = distribution.freePort();
try (DistributionTester.Run run2 = distribution.start("jetty.http.port=" + port))
{
assertTrue(run2.awaitConsoleLogsFor("Started @", 10, TimeUnit.SECONDS));
assertTrue(run2.awaitConsoleLogsFor("Started Server@", 10, TimeUnit.SECONDS));
startHttpClient();
ContentResponse response = client.GET("http://localhost:" + port + "/badapp/");
@ -143,7 +143,7 @@ public class BadAppTests extends AbstractDistributionTest
int port = distribution.freePort();
try (DistributionTester.Run run2 = distribution.start("jetty.http.port=" + port))
{
assertTrue(run2.awaitConsoleLogsFor("Started @", 10, TimeUnit.SECONDS));
assertTrue(run2.awaitConsoleLogsFor("Started Server@", 10, TimeUnit.SECONDS));
startHttpClient();
ContentResponse response = client.GET("http://localhost:" + port + "/badapp/");

View File

@ -101,7 +101,7 @@ public class CDITests extends AbstractDistributionTest
int port = distribution.freePort();
try (DistributionTester.Run run2 = distribution.start("jetty.http.port=" + port))
{
assertTrue(run2.awaitConsoleLogsFor("Started @", 10, TimeUnit.SECONDS));
assertTrue(run2.awaitConsoleLogsFor("Started Server@", 10, TimeUnit.SECONDS));
startHttpClient();
ContentResponse response = client.GET("http://localhost:" + port + "/demo/greetings");

View File

@ -56,7 +56,7 @@ public class DemoBaseTests extends AbstractDistributionTest
try (DistributionTester.Run run1 = distribution.start(args))
{
assertTrue(run1.awaitConsoleLogsFor("Started @", 20, TimeUnit.SECONDS));
assertTrue(run1.awaitConsoleLogsFor("Started Server@", 20, TimeUnit.SECONDS));
startHttpClient();
ContentResponse response = client.GET("http://localhost:" + httpPort + "/test/jsp/dump.jsp");
@ -88,7 +88,7 @@ public class DemoBaseTests extends AbstractDistributionTest
try (DistributionTester.Run run1 = distribution.start(args))
{
assertTrue(run1.awaitConsoleLogsFor("Started @", 20, TimeUnit.SECONDS));
assertTrue(run1.awaitConsoleLogsFor("Started Server@", 20, TimeUnit.SECONDS));
startHttpClient();
ContentResponse response;
@ -133,7 +133,7 @@ public class DemoBaseTests extends AbstractDistributionTest
try (DistributionTester.Run run1 = distribution.start(args))
{
assertTrue(run1.awaitConsoleLogsFor("Started @", 20, TimeUnit.SECONDS));
assertTrue(run1.awaitConsoleLogsFor("Started Server@", 20, TimeUnit.SECONDS));
startHttpClient();
ContentResponse response = client.POST("http://localhost:" + httpPort + "/test-spec/asy/xx").send();

View File

@ -41,6 +41,7 @@ import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.not;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assumptions.assumeTrue;
@ -63,7 +64,7 @@ public class DistributionTests extends AbstractDistributionTest
int port = distribution.freePort();
try (DistributionTester.Run run2 = distribution.start("jetty.http.port=" + port))
{
assertTrue(run2.awaitConsoleLogsFor("Started @", 10, TimeUnit.SECONDS));
assertTrue(run2.awaitConsoleLogsFor("Started Server@", 10, TimeUnit.SECONDS));
startHttpClient();
ContentResponse response = client.GET("http://localhost:" + port);
@ -75,6 +76,57 @@ public class DistributionTests extends AbstractDistributionTest
}
}
@Test
public void testQuickStartGenerationAndRun() throws Exception
{
String jettyVersion = System.getProperty("jettyVersion");
DistributionTester distribution = DistributionTester.Builder.newInstance()
.jettyVersion(jettyVersion)
.mavenLocalRepository(System.getProperty("mavenRepoPath"))
.build();
String[] args1 = {
"--create-startd",
"--approve-all-licenses",
"--add-to-start=resources,server,http,webapp,deploy,jsp,servlet,servlets,quickstart"
};
try (DistributionTester.Run run1 = distribution.start(args1))
{
assertTrue(run1.awaitFor(5, TimeUnit.SECONDS));
assertEquals(0, run1.getExitValue());
File war = distribution.resolveArtifact("org.eclipse.jetty.tests:test-simple-webapp:war:" + jettyVersion);
distribution.installWarFile(war, "test");
try (DistributionTester.Run run2 = distribution.start("jetty.quickstart.mode=GENERATE"))
{
assertTrue(run2.awaitConsoleLogsFor("QuickStartGeneratorConfiguration:main: Generated", 10, TimeUnit.SECONDS));
Path unpackedWebapp = distribution.getJettyBase().resolve("webapps").resolve("test");
assertTrue(Files.exists(unpackedWebapp));
Path webInf = unpackedWebapp.resolve("WEB-INF");
assertTrue(Files.exists(webInf));
Path quickstartWebXml = webInf.resolve("quickstart-web.xml");
assertTrue(Files.exists(quickstartWebXml));
assertNotEquals(0, Files.size(quickstartWebXml));
int port = distribution.freePort();
try (DistributionTester.Run run3 = distribution.start("jetty.http.port=" + port, "jetty.quickstart.mode=QUICKSTART"))
{
assertTrue(run3.awaitConsoleLogsFor("Started Server@", 10, TimeUnit.SECONDS));
startHttpClient();
ContentResponse response = client.GET("http://localhost:" + port + "/test/index.jsp");
assertEquals(HttpStatus.OK_200, response.getStatus());
assertThat(response.getContentAsString(), containsString("Hello"));
assertThat(response.getContentAsString(), not(containsString("<%")));
}
}
}
}
@Test
public void testSimpleWebAppWithJSP() throws Exception
{
@ -100,7 +152,7 @@ public class DistributionTests extends AbstractDistributionTest
int port = distribution.freePort();
try (DistributionTester.Run run2 = distribution.start("jetty.http.port=" + port))
{
assertTrue(run2.awaitConsoleLogsFor("Started @", 10, TimeUnit.SECONDS));
assertTrue(run2.awaitConsoleLogsFor("Started Server@", 10, TimeUnit.SECONDS));
startHttpClient();
ContentResponse response = client.GET("http://localhost:" + port + "/test/index.jsp");
@ -141,7 +193,7 @@ public class DistributionTests extends AbstractDistributionTest
};
try (DistributionTester.Run run2 = distribution.start(args2))
{
assertTrue(run2.awaitConsoleLogsFor("Started @", 10, TimeUnit.SECONDS));
assertTrue(run2.awaitConsoleLogsFor("Started Server@", 10, TimeUnit.SECONDS));
startHttpClient();
ContentResponse response = client.GET("http://localhost:" + port + "/test/index.jsp");
@ -177,7 +229,7 @@ public class DistributionTests extends AbstractDistributionTest
int port = distribution.freePort();
try (DistributionTester.Run run2 = distribution.start("jetty.http.port=" + port))
{
assertTrue(run2.awaitConsoleLogsFor("Started @", 10, TimeUnit.SECONDS));
assertTrue(run2.awaitConsoleLogsFor("Started Server@", 10, TimeUnit.SECONDS));
HTTP2Client h2Client = new HTTP2Client();
startHttpClient(() -> new HttpClient(new HttpClientTransportOverHTTP2(h2Client)));
@ -229,7 +281,7 @@ public class DistributionTests extends AbstractDistributionTest
try (DistributionTester.Run run2 = distribution.start("jetty.unixsocket.path=" + sockFile.toString()))
{
assertTrue(run2.awaitConsoleLogsFor("Started @", 10, TimeUnit.SECONDS));
assertTrue(run2.awaitConsoleLogsFor("Started Server@", 10, TimeUnit.SECONDS));
startHttpClient(() -> new HttpClient(new HttpClientTransportOverUnixSockets(sockFile.toString())));
ContentResponse response = client.GET("http://localhost/test/index.jsp");
@ -272,7 +324,7 @@ public class DistributionTests extends AbstractDistributionTest
int port = distribution.freePort();
try (DistributionTester.Run run2 = distribution.start("jetty.http.port=" + port))
{
assertTrue(run2.awaitConsoleLogsFor("Started @", 10, TimeUnit.SECONDS));
assertTrue(run2.awaitConsoleLogsFor("Started Server@", 10, TimeUnit.SECONDS));
startHttpClient();
ContentResponse response = client.GET("http://localhost:" + port + "/test/index.jsp");

View File

@ -78,7 +78,7 @@ public class DynamicListenerTests
int port = distribution.freePort();
try (DistributionTester.Run run2 = distribution.start("jetty.http.port=" + port))
{
assertTrue(run2.awaitConsoleLogsFor("Started @", 10, TimeUnit.SECONDS));
assertTrue(run2.awaitConsoleLogsFor("Started Server@", 10, TimeUnit.SECONDS));
startHttpClient();
ContentResponse response = client.GET("http://localhost:" + port + "/test/testservlet/foo");

View File

@ -57,7 +57,7 @@ public class OsgiAppTests extends AbstractDistributionTest
int port = distribution.freePort();
try (DistributionTester.Run run2 = distribution.start("jetty.http.port=" + port))
{
assertTrue(run2.awaitConsoleLogsFor("Started @", 10, TimeUnit.SECONDS));
assertTrue(run2.awaitConsoleLogsFor("Started Server@", 10, TimeUnit.SECONDS));
startHttpClient();
ContentResponse response = client.GET("http://localhost:" + port + "/test/info");

View File

@ -24,6 +24,9 @@ import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import org.eclipse.jetty.annotations.AnnotationConfiguration;
import org.eclipse.jetty.plus.webapp.EnvConfiguration;
import org.eclipse.jetty.plus.webapp.PlusConfiguration;
import org.eclipse.jetty.server.NetworkConnector;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
@ -31,6 +34,7 @@ import org.eclipse.jetty.util.IO;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.resource.PathResource;
import org.eclipse.jetty.util.resource.Resource;
import org.eclipse.jetty.webapp.WebAppContext;
import org.eclipse.jetty.webapp.WebDescriptor;
import org.eclipse.jetty.xml.XmlConfiguration;
import org.eclipse.jetty.xml.XmlParser.Node;
@ -47,6 +51,7 @@ public class QuickStartTest
@Test
public void testStandardTestWar() throws Exception
{
//Generate the quickstart
PreconfigureStandardTestWar.main(new String[]{});
WebDescriptor descriptor = new WebDescriptor(Resource.newResource("./target/test-standard-preconfigured/WEB-INF/quickstart-web.xml"));
@ -65,8 +70,12 @@ public class QuickStartTest
Server server = new Server(0);
QuickStartWebApp webapp = new QuickStartWebApp();
webapp.setMode(QuickStartConfiguration.Mode.AUTO);
WebAppContext webapp = new WebAppContext();
webapp.addConfiguration(new QuickStartConfiguration(),
new EnvConfiguration(),
new PlusConfiguration(),
new AnnotationConfiguration());
webapp.setAttribute(QuickStartConfiguration.MODE, QuickStartConfiguration.Mode.QUICKSTART);
webapp.setWar(war);
webapp.setContextPath("/");
@ -93,6 +102,7 @@ public class QuickStartTest
@Test
public void testSpecWar() throws Exception
{
//Generate the quickstart xml
PreconfigureSpecWar.main(new String[]{});
Path webXmlPath = MavenTestingUtils.getTargetPath().resolve("test-spec-preconfigured/WEB-INF/quickstart-web.xml");
@ -114,8 +124,12 @@ public class QuickStartTest
Server server = new Server(0);
QuickStartWebApp webapp = new QuickStartWebApp();
webapp.setMode(QuickStartConfiguration.Mode.AUTO);
WebAppContext webapp = new WebAppContext();
webapp.addConfiguration(new QuickStartConfiguration(),
new EnvConfiguration(),
new PlusConfiguration(),
new AnnotationConfiguration());
webapp.setAttribute(QuickStartConfiguration.MODE, QuickStartConfiguration.Mode.QUICKSTART);
webapp.setWar(war);
webapp.setContextPath("/");
@ -142,6 +156,7 @@ public class QuickStartTest
@Test
public void testJNDIWar() throws Exception
{
//Generate the quickstart
PreconfigureJNDIWar.main(new String[]{});
WebDescriptor descriptor = new WebDescriptor(Resource.newResource("./target/test-jndi-preconfigured/WEB-INF/quickstart-web.xml"));
@ -160,8 +175,12 @@ public class QuickStartTest
Server server = new Server(0);
QuickStartWebApp webapp = new QuickStartWebApp();
webapp.setMode(QuickStartConfiguration.Mode.AUTO);
WebAppContext webapp = new WebAppContext();
webapp.addConfiguration(new QuickStartConfiguration(),
new EnvConfiguration(),
new PlusConfiguration(),
new AnnotationConfiguration());
webapp.setAttribute(QuickStartConfiguration.MODE, QuickStartConfiguration.Mode.QUICKSTART);
webapp.setWar(war);
webapp.setContextPath("/");

View File

@ -18,8 +18,12 @@
package org.eclipse.jetty.quickstart;
import org.eclipse.jetty.annotations.AnnotationConfiguration;
import org.eclipse.jetty.plus.webapp.EnvConfiguration;
import org.eclipse.jetty.plus.webapp.PlusConfiguration;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.util.resource.Resource;
import org.eclipse.jetty.webapp.WebAppContext;
import org.eclipse.jetty.xml.XmlConfiguration;
public class Quickstart
@ -40,8 +44,12 @@ public class Quickstart
Server server = new Server(8080);
QuickStartWebApp webapp = new QuickStartWebApp();
webapp.setMode(QuickStartConfiguration.Mode.AUTO);
WebAppContext webapp = new WebAppContext();
webapp.addConfiguration(new QuickStartConfiguration(),
new EnvConfiguration(),
new PlusConfiguration(),
new AnnotationConfiguration());
webapp.setAttribute(QuickStartConfiguration.MODE, QuickStartConfiguration.Mode.QUICKSTART);
webapp.setWar(war);
webapp.setContextPath("/");