diff options
author | Jan Bartel | 2014-07-09 05:02:44 +0000 |
---|---|---|
committer | Jan Bartel | 2014-07-09 05:07:39 +0000 |
commit | 10da0e1015effb00d650e7e058a580f0520a9854 (patch) | |
tree | 1fb74dd2196088a5efbf52c01a217bf2ec13d748 | |
parent | 0f70f28839b62d16fd3ad8612e6675180c97bbba (diff) | |
download | org.eclipse.jetty.project-10da0e1015effb00d650e7e058a580f0520a9854.tar.gz org.eclipse.jetty.project-10da0e1015effb00d650e7e058a580f0520a9854.tar.xz org.eclipse.jetty.project-10da0e1015effb00d650e7e058a580f0520a9854.zip |
439194 Refactor mvn jetty:run-forked so that it is configurable the same as the other jetty:run-X goals
11 files changed, 1143 insertions, 978 deletions
diff --git a/jetty-maven-plugin/pom.xml b/jetty-maven-plugin/pom.xml index 462627c328..781c404c4a 100644 --- a/jetty-maven-plugin/pom.xml +++ b/jetty-maven-plugin/pom.xml @@ -82,6 +82,11 @@ </dependency> <dependency> <groupId>org.eclipse.jetty</groupId> + <artifactId>jetty-quickstart</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>org.eclipse.jetty</groupId> <artifactId>jetty-jaas</artifactId> <version>${project.version}</version> </dependency> diff --git a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/AbstractJettyMojo.java b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/AbstractJettyMojo.java index e9e979f50a..177f71bc85 100644 --- a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/AbstractJettyMojo.java +++ b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/AbstractJettyMojo.java @@ -684,7 +684,7 @@ public abstract class AbstractJettyMojo extends AbstractMojo /** * */ - private void printSystemProperties () + protected void printSystemProperties () { // print out which system properties were set up if (getLog().isDebugEnabled()) diff --git a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyRunForkedMojo.java b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyRunForkedMojo.java index efb1284c01..663749d033 100644 --- a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyRunForkedMojo.java +++ b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyRunForkedMojo.java @@ -39,34 +39,35 @@ import java.util.Random; import java.util.Set; import org.apache.maven.artifact.Artifact; -import org.apache.maven.plugin.AbstractMojo; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugin.MojoFailureException; import org.apache.maven.plugin.descriptor.PluginDescriptor; -import org.apache.maven.project.MavenProject; +import org.eclipse.jetty.annotations.AnnotationConfiguration; import org.eclipse.jetty.util.IO; import org.eclipse.jetty.util.resource.Resource; import org.eclipse.jetty.util.resource.ResourceCollection; +import org.eclipse.jetty.util.thread.QueuedThreadPool; /** * <p> - * This goal is used to assemble your webapp into a war and automatically deploy it to Jetty in a forked JVM. + * This goal is used to deploy your unassembled webapp into a forked JVM. * </p> * <p> - * You need to define a jetty.xml file to configure connectors etc and a context xml file that sets up anything special - * about your webapp. This plugin will fill in the: - * <ul> - * <li>context path - * <li>classes - * <li>web.xml - * <li>root of the webapp - * </ul> - * Based on a combination of information that you supply and the location of files in your unassembled webapp. + * You need to define a jetty.xml file to configure connectors etc. You can use the normal setters of o.e.j.webapp.WebAppContext on the <b>webApp</b> + * configuration element for this plugin. You may also need context xml file for any particularly complex webapp setup. + * about your webapp. * </p> * <p> - * There is a <a href="run-war-mojo.html">reference guide</a> to the configuration parameters for this plugin, and more detailed information - * with examples in the <a href="http://docs.codehaus.org/display/JETTY/Maven+Jetty+Plugin/">Configuration Guide</a>. + * Unlike the other jetty goals, this does NOT support the <b>scanIntervalSeconds</b> parameter: the webapp will be deployed only once. + * </p> + * <p> + * The <b>stopKey</b>, <b>stopPort</b> configuration elements can be used to control the stopping of the forked process. By default, this plugin will launch + * the forked jetty instance and wait for it to complete (in which case it acts much like the <b>jetty:run</b> goal, and you will need to Cntrl-C to stop). + * By setting the configuration element <b>waitForChild</b> to <b>false</b>, the plugin will terminate after having forked the jetty process. In this case + * you can use the <b>jetty:stop</b> goal to terminate the process. + * <p> + * See <a href="http://www.eclipse.org/jetty/documentation/">http://www.eclipse.org/jetty/documentation</a> for more information on this and other jetty plugins. * </p> * * @goal run-forked @@ -75,50 +76,18 @@ import org.eclipse.jetty.util.resource.ResourceCollection; * @description Runs Jetty in forked JVM on an unassembled webapp * */ -public class JettyRunForkedMojo extends AbstractMojo +public class JettyRunForkedMojo extends JettyRunMojo { public static final String DEFAULT_WEBAPP_SRC = "src"+File.separator+"main"+File.separator+"webapp"; public static final String FAKE_WEBAPP = "webapp-tmp"; public String PORT_SYSPROPERTY = "jetty.port"; - - /** - * Whether or not to include dependencies on the plugin's classpath with <scope>provided</scope> - * Use WITH CAUTION as you may wind up with duplicate jars/classes. - * @parameter default-value="false" - */ - protected boolean useProvidedScope; - - - /** - * The maven project. - * - * @parameter expression="${project}" - * @required - * @readonly - */ - private MavenProject project; - /** - * If true, the <testOutputDirectory> - * and the dependencies of <scope>test<scope> - * will be put first on the runtime classpath. - * @parameter alias="useTestClasspath" default-value="false" - */ - private boolean useTestScope; + - /** - * The default location of the web.xml file. Will be used - * if <webAppConfig><descriptor> is not set. - * - * @parameter expression="${basedir}/src/main/webapp/WEB-INF/web.xml" - * @readonly - */ - private String webXml; - /** * The target directory @@ -131,118 +100,6 @@ public class JettyRunForkedMojo extends AbstractMojo /** - * The temporary directory to use for the webapp. - * Defaults to target/tmp - * - * @parameter alias="tmpDirectory" expression="${project.build.directory}/tmp" - * @required - * @readonly - */ - protected File tempDirectory; - - - - /** - * Whether temporary directory contents should survive webapp restarts. - * - * @parameter default-value="false" - */ - private boolean persistTempDirectory; - - - /** - * The directory containing generated classes. - * - * @parameter expression="${project.build.outputDirectory}" - * @required - * - */ - private File classesDirectory; - - - /** - * The directory containing generated test classes. - * - * @parameter expression="${project.build.testOutputDirectory}" - * @required - */ - private File testClassesDirectory; - - - /** - * Root directory for all html/jsp etc files - * - * @parameter expression="${basedir}/src/main/webapp" - * - */ - private File webAppSourceDirectory; - - /** - * Resource Bases - * - * @parameter - * - */ - private String[] resourceBases; - - /** - * If true, the webAppSourceDirectory will be first on the list of - * resources that form the resource base for the webapp. If false, - * it will be last. - * - * @parameter default-value="true" - */ - private boolean baseAppFirst; - - - /** - * Location of jetty xml configuration files whose contents - * will be applied before any plugin configuration. Optional. - * @parameter - */ - private String jettyXml; - - /** - * The context path for the webapp. Defaults to / for jetty-9 - * - * @parameter expression="/" - */ - private String contextPath; - - - /** - * Location of a context xml configuration file whose contents - * will be applied to the webapp AFTER anything in <webAppConfig>.Optional. - * @parameter - */ - private String contextXml; - - - /** - * @parameter expression="${jetty.skip}" default-value="false" - */ - private boolean skip; - - - /** - * Port to listen to stop jetty on executing -DSTOP.PORT=<stopPort> - * -DSTOP.KEY=<stopKey> -jar start.jar --stop - * @parameter - * @required - */ - protected int stopPort; - - - /** - * Key to provide when stopping jetty on executing java -DSTOP.KEY=<stopKey> - * -DSTOP.PORT=<stopPort> -jar start.jar --stop - * @parameter - * @required - */ - protected String stopKey; - - - /** * Arbitrary jvm args to pass to the forked process * @parameter */ @@ -284,9 +141,9 @@ public class JettyRunForkedMojo extends AbstractMojo */ private Random random; + - - + private Resource originalBaseResource; /** @@ -360,21 +217,183 @@ public class JettyRunForkedMojo extends AbstractMojo */ public void execute() throws MojoExecutionException, MojoFailureException { - getLog().info("Configuring Jetty for project: " + project.getName()); - if (skip) - { - getLog().info("Skipping Jetty start: jetty.skip==true"); - return; - } - PluginLog.setLog(getLog()); Runtime.getRuntime().addShutdownHook(new ShutdownThread()); random = new Random(); - startJettyRunner(); + super.execute(); } - - + + + @Override + public void startJetty() throws MojoExecutionException + { + //Only do enough setup to be able to produce a quickstart-web.xml file to + //pass onto the forked process to run + try + { + printSystemProperties(); + + //apply any config from a jetty.xml file first to our "fake" server instance + //TODO probably not necessary + applyJettyXml (); + + + server.configureHandlers(); + + //ensure config of the webapp based on settings in plugin + configureWebApplication(); + + //copy the base resource as configured by the plugin + originalBaseResource = webApp.getBaseResource(); + + //set the webapp up to do very little other than generate the quickstart-web.xml + webApp.setCopyWebDir(false); + webApp.setCopyWebInf(false); + webApp.setGenerateQuickStart(true); + webApp.setQuickStartDir(target); + + server.addWebApplication(webApp); + + //if our server has a thread pool associated we can do any annotation scanning multithreaded, + //otherwise scanning will be single threaded + QueuedThreadPool tpool = server.getBean(QueuedThreadPool.class); + if (tpool != null) + tpool.start(); + else + webApp.setAttribute(AnnotationConfiguration.MULTI_THREADED, Boolean.FALSE.toString()); + + webApp.start(); //just enough to generate the quickstart + + //save config of the webapp BEFORE we stop + File props = prepareConfiguration(); + + webApp.stop(); + + if (tpool != null) + tpool.stop(); + + List<String> cmd = new ArrayList<String>(); + cmd.add(getJavaBin()); + + if (jvmArgs != null) + { + String[] args = jvmArgs.split(" "); + for (int i=0;args != null && i<args.length;i++) + { + if (args[i] !=null && !"".equals(args[i])) + cmd.add(args[i].trim()); + } + } + + String classPath = getContainerClassPath(); + if (classPath != null && classPath.length() > 0) + { + cmd.add("-cp"); + cmd.add(classPath); + } + cmd.add(Starter.class.getCanonicalName()); + + if (stopPort > 0 && stopKey != null) + { + cmd.add("--stop-port"); + cmd.add(Integer.toString(stopPort)); + cmd.add("--stop-key"); + cmd.add(stopKey); + } + if (jettyXml != null) + { + cmd.add("--jetty-xml"); + cmd.add(jettyXml); + } + + if (contextXml != null) + { + cmd.add("--context-xml"); + cmd.add(contextXml); + } + + cmd.add("--props"); + cmd.add(props.getAbsolutePath()); + + String token = createToken(); + cmd.add("--token"); + cmd.add(token); + + ProcessBuilder builder = new ProcessBuilder(cmd); + builder.directory(project.getBasedir()); + + if (PluginLog.getLog().isDebugEnabled()) + PluginLog.getLog().debug(Arrays.toString(cmd.toArray())); + + PluginLog.getLog().info("Forked process starting"); + + if (waitForChild) + { + forkedProcess = builder.start(); + startPump("STDOUT",forkedProcess.getInputStream()); + startPump("STDERR",forkedProcess.getErrorStream()); + int exitcode = forkedProcess.waitFor(); + PluginLog.getLog().info("Forked execution exit: "+exitcode); + } + else + { //merge stderr and stdout from child + builder.redirectErrorStream(true); + forkedProcess = builder.start(); + + //wait for the child to be ready before terminating. + //child indicates it has finished starting by printing on stdout the token passed to it + try + { + String line = ""; + try (InputStream is = forkedProcess.getInputStream(); + LineNumberReader reader = new LineNumberReader(new InputStreamReader(is))) + { + int attempts = maxStartupLines; //max lines we'll read trying to get token + while (attempts>0 && line != null) + { + --attempts; + line = reader.readLine(); + if (line != null && line.startsWith(token)) + break; + } + + } + + if (line != null && line.trim().equals(token)) + PluginLog.getLog().info("Forked process started."); + else + { + String err = (line == null?"":(line.startsWith(token)?line.substring(token.length()):line)); + PluginLog.getLog().info("Forked process startup errors"+(!"".equals(err)?", received: "+err:"")); + } + } + catch (Exception e) + { + throw new MojoExecutionException ("Problem determining if forked process is ready: "+e.getMessage()); + } + + } + } + catch (InterruptedException ex) + { + if (forkedProcess != null && waitForChild) + forkedProcess.destroy(); + + throw new MojoExecutionException("Failed to start Jetty within time limit"); + } + catch (Exception ex) + { + if (forkedProcess != null && waitForChild) + forkedProcess.destroy(); + + throw new MojoExecutionException("Failed to create Jetty process", ex); + } + } + + + + /** * @return * @throws MojoExecutionException @@ -428,76 +447,63 @@ public class JettyRunForkedMojo extends AbstractMojo //web.xml - if (webXml != null) - props.put("web.xml", webXml); + if (webApp.getDescriptor() != null) + { + props.put("web.xml", webApp.getDescriptor()); + } + + if (webApp.getQuickStartWebDescriptor() != null) + { + props.put("quickstart.web.xml", webApp.getQuickStartWebDescriptor().getFile().getAbsolutePath()); + } //sort out the context path - if (contextPath != null) - props.put("context.path", contextPath); - - //sort out the tmp directory (make it if it doesn't exist) - if (tempDirectory != null) + if (webApp.getContextPath() != null) { - if (!tempDirectory.exists()) - tempDirectory.mkdirs(); - props.put("tmp.dir", tempDirectory.getAbsolutePath()); + props.put("context.path", webApp.getContextPath()); } - - props.put("tmp.dir.persist", Boolean.toString(persistTempDirectory)); - if (resourceBases == null) + //tmp dir + props.put("tmp.dir", webApp.getTempDirectory().getAbsolutePath()); + props.put("tmp.dir.persist", Boolean.toString(webApp.isPersistTempDirectory())); + + //resource bases - these are what has been configured BEFORE the webapp started and + //potentially reordered them and included any resources from META-INF + if (originalBaseResource != null) { - //sort out base dir of webapp - if (webAppSourceDirectory == null || !webAppSourceDirectory.exists()) + StringBuffer rb = new StringBuffer(); + if (originalBaseResource instanceof ResourceCollection) { - webAppSourceDirectory = new File (project.getBasedir(), DEFAULT_WEBAPP_SRC); - if (!webAppSourceDirectory.exists()) + ResourceCollection resources = ((ResourceCollection)originalBaseResource); + for (Resource r:resources.getResources()) { - //try last resort of making a fake empty dir - File target = new File(project.getBuild().getDirectory()); - webAppSourceDirectory = new File(target, FAKE_WEBAPP); - if (!webAppSourceDirectory.exists()) - webAppSourceDirectory.mkdirs(); - } + if (rb.length() > 0) rb.append(","); + rb.append(r.toString()); + } } - resourceBases = new String[] { webAppSourceDirectory.getAbsolutePath() }; + else + rb.append(originalBaseResource.toString()); + + props.put("base.dirs", rb.toString()); } - StringBuffer rb = new StringBuffer(resourceBases[0]); - for (int i=1; i<resourceBases.length; i++) - { - rb.append(File.pathSeparator); - rb.append(resourceBases[i]); - } - props.put("base.dirs", rb.toString()); //sort out the resource base directories of the webapp - StringBuilder builder = new StringBuilder(); - props.put("base.first", Boolean.toString(baseAppFirst)); + props.put("base.first", Boolean.toString(webApp.getBaseAppFirst())); //web-inf classes - List<File> classDirs = getClassesDirs(); - StringBuffer strbuff = new StringBuffer(); - for (int i=0; i<classDirs.size(); i++) + if (webApp.getClasses() != null) { - File f = classDirs.get(i); - strbuff.append(f.getAbsolutePath()); - if (i < classDirs.size()-1) - strbuff.append(","); - } - - if (classesDirectory != null) - { - props.put("classes.dir", classesDirectory.getAbsolutePath()); + props.put("classes.dir",webApp.getClasses().getAbsolutePath()); } - if (useTestScope && testClassesDirectory != null) + if (useTestScope && webApp.getTestClasses() != null) { - props.put("testClasses.dir", testClassesDirectory.getAbsolutePath()); + props.put("testClasses.dir", webApp.getTestClasses().getAbsolutePath()); } //web-inf lib - List<File> deps = getDependencyFiles(); - strbuff.setLength(0); + List<File> deps = webApp.getWebInfLib(); + StringBuffer strbuff = new StringBuffer(); for (int i=0; i<deps.size(); i++) { File d = deps.get(i); @@ -550,25 +556,6 @@ public class JettyRunForkedMojo extends AbstractMojo - /** - * @return - */ - private List<File> getClassesDirs () - { - List<File> classesDirs = new ArrayList<File>(); - - //if using the test classes, make sure they are first - //on the list - if (useTestScope && (testClassesDirectory != null)) - classesDirs.add(testClassesDirectory); - - if (classesDirectory != null) - classesDirs.add(classesDirectory); - - return classesDirs; - } - - /** @@ -594,30 +581,6 @@ public class JettyRunForkedMojo extends AbstractMojo - /** - * @return - */ - private List<File> getDependencyFiles () - { - List<File> dependencyFiles = new ArrayList<File>(); - - for ( Iterator<Artifact> iter = project.getArtifacts().iterator(); iter.hasNext(); ) - { - Artifact artifact = (Artifact) iter.next(); - // Test never appears here ! - if (((!Artifact.SCOPE_PROVIDED.equals(artifact.getScope())) && (!Artifact.SCOPE_TEST.equals( artifact.getScope()))) - || - (useTestScope && Artifact.SCOPE_TEST.equals( artifact.getScope()))) - { - dependencyFiles.add(artifact.getFile()); - getLog().debug( "Adding artifact " + artifact.getFile().getName() + " for WEB-INF/lib " ); - } - } - - return dependencyFiles; - } - - /** @@ -675,143 +638,14 @@ public class JettyRunForkedMojo extends AbstractMojo - - /** - * @throws MojoExecutionException - */ - public void startJettyRunner() throws MojoExecutionException - { - try - { - - File props = prepareConfiguration(); - - List<String> cmd = new ArrayList<String>(); - cmd.add(getJavaBin()); - - if (jvmArgs != null) - { - String[] args = jvmArgs.split(" "); - for (int i=0;args != null && i<args.length;i++) - { - if (args[i] !=null && !"".equals(args[i])) - cmd.add(args[i].trim()); - } - } - - String classPath = getClassPath(); - if (classPath != null && classPath.length() > 0) - { - cmd.add("-cp"); - cmd.add(classPath); - } - cmd.add(Starter.class.getCanonicalName()); - - if (stopPort > 0 && stopKey != null) - { - cmd.add("--stop-port"); - cmd.add(Integer.toString(stopPort)); - cmd.add("--stop-key"); - cmd.add(stopKey); - } - if (jettyXml != null) - { - cmd.add("--jetty-xml"); - cmd.add(jettyXml); - } - - if (contextXml != null) - { - cmd.add("--context-xml"); - cmd.add(contextXml); - } - - cmd.add("--props"); - cmd.add(props.getAbsolutePath()); - - String token = createToken(); - cmd.add("--token"); - cmd.add(token); - - ProcessBuilder builder = new ProcessBuilder(cmd); - builder.directory(project.getBasedir()); - - if (PluginLog.getLog().isDebugEnabled()) - PluginLog.getLog().debug(Arrays.toString(cmd.toArray())); - - PluginLog.getLog().info("Forked process starting"); - - if (waitForChild) - { - forkedProcess = builder.start(); - startPump("STDOUT",forkedProcess.getInputStream()); - startPump("STDERR",forkedProcess.getErrorStream()); - int exitcode = forkedProcess.waitFor(); - PluginLog.getLog().info("Forked execution exit: "+exitcode); - } - else - { //merge stderr and stdout from child - builder.redirectErrorStream(true); - forkedProcess = builder.start(); - - //wait for the child to be ready before terminating. - //child indicates it has finished starting by printing on stdout the token passed to it - try - { - String line = ""; - try (InputStream is = forkedProcess.getInputStream(); - LineNumberReader reader = new LineNumberReader(new InputStreamReader(is))) - { - int attempts = maxStartupLines; //max lines we'll read trying to get token - while (attempts>0 && line != null) - { - --attempts; - line = reader.readLine(); - if (line != null && line.startsWith(token)) - break; - } - - } - - if (line != null && line.trim().equals(token)) - PluginLog.getLog().info("Forked process started."); - else - { - String err = (line == null?"":(line.startsWith(token)?line.substring(token.length()):line)); - PluginLog.getLog().info("Forked process startup errors"+(!"".equals(err)?", received: "+err:"")); - } - } - catch (Exception e) - { - throw new MojoExecutionException ("Problem determining if forked process is ready: "+e.getMessage()); - } - - } - } - catch (InterruptedException ex) - { - if (forkedProcess != null && waitForChild) - forkedProcess.destroy(); - - throw new MojoExecutionException("Failed to start Jetty within time limit"); - } - catch (Exception ex) - { - if (forkedProcess != null && waitForChild) - forkedProcess.destroy(); - - throw new MojoExecutionException("Failed to create Jetty process", ex); - } - } - - + /** * @return * @throws Exception */ - public String getClassPath() throws Exception + public String getContainerClassPath() throws Exception { StringBuilder classPath = new StringBuilder(); for (Object obj : pluginArtifacts) diff --git a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyWebAppContext.java b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyWebAppContext.java index 700d0b9372..4b8a76bc7d 100644 --- a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyWebAppContext.java +++ b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyWebAppContext.java @@ -32,6 +32,8 @@ import java.util.TreeSet; 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.PreconfigureDescriptorProcessor; +import org.eclipse.jetty.quickstart.QuickStartDescriptorGenerator; import org.eclipse.jetty.servlet.FilterHolder; import org.eclipse.jetty.servlet.FilterMapping; import org.eclipse.jetty.servlet.ServletHolder; @@ -61,20 +63,40 @@ import org.eclipse.jetty.webapp.WebXmlConfiguration; public class JettyWebAppContext extends WebAppContext { private static final Logger LOG = Log.getLogger(JettyWebAppContext.class); + + private static final String DEFAULT_CONTAINER_INCLUDE_JAR_PATTERN = ".*/javax.servlet-[^/]*\\.jar$|.*/servlet-api-[^/]*\\.jar$|.*javax.servlet.jsp.jstl-[^/]*\\.jar"; private static final String WEB_INF_CLASSES_PREFIX = "/WEB-INF/classes"; private static final String WEB_INF_LIB_PREFIX = "/WEB-INF/lib"; + private final Configuration[] _defaultConfigurations = { + new MavenWebInfConfiguration(), + new WebXmlConfiguration(), + new MetaInfConfiguration(), + new FragmentConfiguration(), + new EnvConfiguration(), + new PlusConfiguration(), + new AnnotationConfiguration(), + new JettyWebXmlConfiguration() + }; + + private final Configuration[] _quickStartConfigurations = { + new MavenQuickStartConfiguration(), + new EnvConfiguration(), + new PlusConfiguration(), + new JettyWebXmlConfiguration() + }; + private File _classes = null; private File _testClasses = null; private final List<File> _webInfClasses = new ArrayList<File>(); private final List<File> _webInfJars = new ArrayList<File>(); private final Map<String, File> _webInfJarMap = new HashMap<String, File>(); - private final EnvConfiguration _envConfig; private List<File> _classpathFiles; //webInfClasses+testClasses+webInfJars private String _jettyEnvXml; private List<Overlay> _overlays; + private Resource _quickStartWebXml; @@ -99,22 +121,18 @@ public class JettyWebAppContext extends WebAppContext */ private boolean _baseAppFirst = true; + + + private boolean _isGenerateQuickStart; + private PreconfigureDescriptorProcessor _preconfigProcessor; + private File _quickStartDir; + public JettyWebAppContext () throws Exception { super(); - setConfigurations(new Configuration[]{ - new MavenWebInfConfiguration(), - new WebXmlConfiguration(), - new MetaInfConfiguration(), - new FragmentConfiguration(), - _envConfig = new EnvConfiguration(), - new PlusConfiguration(), - new AnnotationConfiguration(), - new JettyWebXmlConfiguration() - }); // Turn off copyWebInf option as it is not applicable for plugin. super.setCopyWebInf(false); } @@ -209,7 +227,19 @@ public class JettyWebAppContext extends WebAppContext { return _baseAppFirst; } - + + /* ------------------------------------------------------------ */ + public void setQuickStartWebDescriptor (Resource quickStartWebXml) + { + _quickStartWebXml = quickStartWebXml; + } + + /* ------------------------------------------------------------ */ + public Resource getQuickStartWebDescriptor () + { + return _quickStartWebXml; + } + /* ------------------------------------------------------------ */ /** * This method is provided as a convenience for jetty maven plugin configuration @@ -233,9 +263,68 @@ public class JettyWebAppContext extends WebAppContext { return _webInfJars; } + + public void setGenerateQuickStart (boolean quickStart) + { + _isGenerateQuickStart = quickStart; + } + + public boolean isGenerateQuickStart() + { + return _isGenerateQuickStart; + } + + public void setQuickStartDir (File dir) + { + _quickStartDir = dir; + } + + public File getQuickStartDir() + { + return _quickStartDir; + } + + + @Override + protected void startWebapp() throws Exception + { + if (isGenerateQuickStart()) + { + QuickStartDescriptorGenerator generator = new QuickStartDescriptorGenerator(this, (getQuickStartDir()==null?getTempDirectory():getQuickStartDir()), _preconfigProcessor.getXML()); + File f = generator.generateQuickStartWebXml(); + setQuickStartWebDescriptor(Resource.newResource(f)); + } + else + super.startWebapp(); + } + + @Override public void doStart () throws Exception { + //choose if this will be a quickstart or normal start + if (getQuickStartWebDescriptor() == null) + { + setConfigurations(_defaultConfigurations); + if (isGenerateQuickStart()) + { + _preconfigProcessor = new PreconfigureDescriptorProcessor(); + getMetaData().addDescriptorProcessor(_preconfigProcessor); + } + } + else + setConfigurations(_quickStartConfigurations); + + + //inject configurations with config from maven plugin + for (Configuration c:getConfigurations()) + { + if (c instanceof EnvConfiguration && getJettyEnvXml() != null) + ((EnvConfiguration)c).setJettyEnvXml(Resource.toURL(new File(getJettyEnvXml()))); + else if (c instanceof MavenQuickStartConfiguration && getQuickStartWebDescriptor() != null) + ((MavenQuickStartConfiguration)c).setQuickStartWebXml(getQuickStartWebDescriptor()); + } + //Set up the pattern that tells us where the jars are that need scanning //Allow user to set up pattern for names of jars from the container classpath @@ -273,9 +362,6 @@ public class JettyWebAppContext extends WebAppContext if (fileName.endsWith(".jar")) _webInfJarMap.put(fileName, file); } - - if (this._jettyEnvXml != null) - _envConfig.setJettyEnvXml(Resource.toURL(new File(this._jettyEnvXml))); // CHECK setShutdown(false); super.doStart(); diff --git a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/MavenQuickStartConfiguration.java b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/MavenQuickStartConfiguration.java new file mode 100644 index 0000000000..295bd06242 --- /dev/null +++ b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/MavenQuickStartConfiguration.java @@ -0,0 +1,92 @@ +// +// ======================================================================== +// Copyright (c) 1995-2014 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.maven.plugin; + +import java.io.File; +import java.util.Iterator; + +import org.eclipse.jetty.quickstart.QuickStartConfiguration; +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.webapp.WebAppClassLoader; +import org.eclipse.jetty.webapp.WebAppContext; + +/** + * MavenQuickStartConfiguration + * + * + */ +public class MavenQuickStartConfiguration extends QuickStartConfiguration +{ + private static final Logger LOG = Log.getLogger(QuickStartConfiguration.class); + + private Resource _quickStartWebXml; + + + public void setQuickStartWebXml (Resource r) + { + _quickStartWebXml = r; + } + + + + @Override + public Resource getQuickStartWebXml(WebAppContext context) throws Exception + { + return _quickStartWebXml; + } + + + + @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 ..."); + Iterator itor = jwac.getClassPathFiles().iterator(); + while (itor.hasNext()) + ((WebAppClassLoader)context.getClassLoader()).addClassPath(((File)itor.next()).getCanonicalPath()); + } + + //Set up the quickstart environment for the context + super.configure(context); + + // knock out environmental maven and plexus classes from webAppContext + String[] existingServerClasses = context.getServerClasses(); + String[] newServerClasses = new String[2+(existingServerClasses==null?0:existingServerClasses.length)]; + newServerClasses[0] = "org.apache.maven."; + newServerClasses[1] = "org.codehaus.plexus."; + System.arraycopy( existingServerClasses, 0, newServerClasses, 2, existingServerClasses.length ); + if (LOG.isDebugEnabled()) + { + LOG.debug("Server classes:"); + for (int i=0;i<newServerClasses.length;i++) + LOG.debug(newServerClasses[i]); + } + context.setServerClasses( newServerClasses ); + } + +} diff --git a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/Starter.java b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/Starter.java index d16b89d842..2ee9022f04 100644 --- a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/Starter.java +++ b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/Starter.java @@ -156,6 +156,14 @@ 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)); + } + //set up the webapp from the context xml file provided //NOTE: just like jetty:run mojo this means that the context file can //potentially override settings made in the pom. Ideally, we'd like @@ -203,6 +211,9 @@ public class Starter if (str != null) webApp.setDescriptor(str); + str = (String)props.get("quickstart.web.xml"); + if (str != null) + webApp.setQuickStartWebDescriptor(Resource.newResource(new File(str))); // - the tmp directory str = (String)props.getProperty("tmp.dir"); @@ -217,8 +228,9 @@ public class Starter str = (String)props.getProperty("base.dirs"); if (str != null && !"".equals(str.trim())) { - webApp.setWar(str); - webApp.setBaseResource(new ResourceCollection(str.split(File.pathSeparator))); + ResourceCollection bases = new ResourceCollection(str.split(",")); + webApp.setWar(bases.getResources()[0].toString()); + webApp.setBaseResource(bases); } // - put virtual webapp base resource first on resource path or not diff --git a/jetty-quickstart/src/main/java/org/eclipse/jetty/quickstart/QuickStartConfiguration.java b/jetty-quickstart/src/main/java/org/eclipse/jetty/quickstart/QuickStartConfiguration.java index 313c5c03c8..85f95dcb6a 100644 --- a/jetty-quickstart/src/main/java/org/eclipse/jetty/quickstart/QuickStartConfiguration.java +++ b/jetty-quickstart/src/main/java/org/eclipse/jetty/quickstart/QuickStartConfiguration.java @@ -72,7 +72,23 @@ public class QuickStartConfiguration extends WebInfConfiguration LOG.debug("webapp={}",webApp); - //look for effective-web.xml in WEB-INF of webapp + //look for quickstart-web.xml in WEB-INF of webapp + Resource quickStartWebXml = getQuickStartWebXml(context); + LOG.debug("quickStartWebXml={}",quickStartWebXml); + + context.getMetaData().setWebXml(quickStartWebXml); + } + + + /** + * Get the quickstart-web.xml file as a Resource. + * + * @param context + * @return + * @throws Exception + */ + public Resource getQuickStartWebXml (WebAppContext context) throws Exception + { Resource webInf = context.getWebInf(); if (webInf == null || !webInf.exists()) throw new IllegalStateException("No WEB-INF"); @@ -81,11 +97,10 @@ public class QuickStartConfiguration extends WebInfConfiguration Resource quickStartWebXml = webInf.addPath("quickstart-web.xml"); if (!quickStartWebXml.exists()) throw new IllegalStateException ("No WEB-INF/quickstart-web.xml"); - LOG.debug("quickStartWebXml={}",quickStartWebXml); - - context.getMetaData().setWebXml(quickStartWebXml); + return quickStartWebXml; } - + + /** * @see org.eclipse.jetty.webapp.AbstractConfiguration#configure(org.eclipse.jetty.webapp.WebAppContext) diff --git a/jetty-quickstart/src/main/java/org/eclipse/jetty/quickstart/QuickStartDescriptorGenerator.java b/jetty-quickstart/src/main/java/org/eclipse/jetty/quickstart/QuickStartDescriptorGenerator.java new file mode 100644 index 0000000000..a138ae6e57 --- /dev/null +++ b/jetty-quickstart/src/main/java/org/eclipse/jetty/quickstart/QuickStartDescriptorGenerator.java @@ -0,0 +1,669 @@ +// +// ======================================================================== +// Copyright (c) 1995-2014 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 java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.Collection; +import java.util.Collections; +import java.util.EventListener; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +import javax.servlet.DispatcherType; +import javax.servlet.MultipartConfigElement; +import javax.servlet.ServletContext; +import javax.servlet.SessionCookieConfig; +import javax.servlet.SessionTrackingMode; +import javax.servlet.descriptor.JspPropertyGroupDescriptor; +import javax.servlet.descriptor.TaglibDescriptor; + +import org.eclipse.jetty.annotations.AnnotationConfiguration; +import org.eclipse.jetty.http.MimeTypes; +import org.eclipse.jetty.plus.annotation.LifeCycleCallback; +import org.eclipse.jetty.plus.annotation.LifeCycleCallbackCollection; +import org.eclipse.jetty.security.ConstraintAware; +import org.eclipse.jetty.security.ConstraintMapping; +import org.eclipse.jetty.security.SecurityHandler; +import org.eclipse.jetty.security.authentication.FormAuthenticator; +import org.eclipse.jetty.servlet.ErrorPageErrorHandler; +import org.eclipse.jetty.servlet.FilterHolder; +import org.eclipse.jetty.servlet.FilterMapping; +import org.eclipse.jetty.servlet.Holder; +import org.eclipse.jetty.servlet.ServletHandler; +import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.servlet.ServletMapping; +import org.eclipse.jetty.servlet.ServletContextHandler.JspConfig; +import org.eclipse.jetty.util.QuotedStringTokenizer; +import org.eclipse.jetty.util.log.Log; +import org.eclipse.jetty.util.log.Logger; +import org.eclipse.jetty.util.security.Constraint; +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.xml.XmlAppendable; + + + +/** + * QuickStartDescriptorGenerator + * + * Generate an effective web.xml from a WebAppContext, including all components + * from web.xml, web-fragment.xmls annotations etc. + */ +public class QuickStartDescriptorGenerator +{ + private static final Logger LOG = Log.getLogger(QuickStartDescriptorGenerator.class); + protected WebAppContext _webApp; + protected File _descriptorDir; + protected String _extraXML; + + + + /** + * @param w the source WebAppContext + * @param descriptorDir the directory where quickstart-web.xml should be saved + * @param extraXML any extra xml snippet to append + */ + public QuickStartDescriptorGenerator (WebAppContext w, File descriptorDir, String extraXML) + { + _webApp = w; + _descriptorDir = descriptorDir; + _extraXML = extraXML; + } + + + /** + * Perform the generation of the xml file + * @throws IOException + * @throws FileNotFoundException + * @throws Exception + */ + public File generateQuickStartWebXml () throws FileNotFoundException, IOException + { + if (_webApp == null) + throw new IllegalStateException("No webapp for quickstart-web.xml"); + if (_descriptorDir == null) + throw new IllegalStateException("No location for quickstart-web.xml"); + + _webApp.getMetaData().getOrigins(); + + if (_webApp.getBaseResource()==null) + throw new IllegalArgumentException("No base resource for "+this); + + if (!_descriptorDir.exists()) + _descriptorDir.mkdirs(); + + File webXml = new File(_descriptorDir,"quickstart-web.xml"); + + LOG.info("Quickstart generate {}",webXml); + + try (FileOutputStream fos = new FileOutputStream(webXml)) + { + XmlAppendable out = new XmlAppendable(fos,"UTF-8"); + + MetaData md = _webApp.getMetaData(); + + Map<String, String> webappAttr = new HashMap<>(); + webappAttr.put("xmlns","http://xmlns.jcp.org/xml/ns/javaee"); + webappAttr.put("xmlns:xsi","http://www.w3.org/2001/XMLSchema-instance"); + webappAttr.put("xsi:schemaLocation","http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"); + webappAttr.put("metadata-complete","true"); + webappAttr.put("version","3.1"); + + out.openTag("web-app",webappAttr); + if (_webApp.getDisplayName() != null) + out.tag("display-name",_webApp.getDisplayName()); + + // Set some special context parameters + + // The location of the war file on disk + String resourceBase = _webApp.getBaseResource().getFile().getCanonicalFile().getAbsoluteFile().toURI().toString(); + + // The library order + addContextParamFromAttribute(out,ServletContext.ORDERED_LIBS); + //the servlet container initializers + addContextParamFromAttribute(out,AnnotationConfiguration.CONTAINER_INITIALIZERS); + //the tlds discovered + addContextParamFromAttribute(out,MetaInfConfiguration.METAINF_TLDS,resourceBase); + //the META-INF/resources discovered + addContextParamFromAttribute(out,MetaInfConfiguration.METAINF_RESOURCES,resourceBase); + + + // init params + for (String p : _webApp.getInitParams().keySet()) + out.openTag("context-param",origin(md,"context-param." + p)) + .tag("param-name",p) + .tag("param-value",_webApp.getInitParameter(p)) + .closeTag(); + + if (_webApp.getEventListeners() != null) + for (EventListener e : _webApp.getEventListeners()) + out.openTag("listener",origin(md,e.getClass().getCanonicalName() + ".listener")) + .tag("listener-class",e.getClass().getCanonicalName()) + .closeTag(); + + ServletHandler servlets = _webApp.getServletHandler(); + + if (servlets.getFilters() != null) + { + for (FilterHolder holder : servlets.getFilters()) + outholder(out,md,"filter",holder); + } + + if (servlets.getFilterMappings() != null) + { + for (FilterMapping mapping : servlets.getFilterMappings()) + { + out.openTag("filter-mapping"); + out.tag("filter-name",mapping.getFilterName()); + if (mapping.getPathSpecs() != null) + for (String s : mapping.getPathSpecs()) + out.tag("url-pattern",s); + if (mapping.getServletNames() != null) + for (String n : mapping.getServletNames()) + out.tag("servlet-name",n); + + if (!mapping.isDefaultDispatches()) + { + if (mapping.appliesTo(DispatcherType.REQUEST)) + out.tag("dispatcher","REQUEST"); + if (mapping.appliesTo(DispatcherType.ASYNC)) + out.tag("dispatcher","ASYNC"); + if (mapping.appliesTo(DispatcherType.ERROR)) + out.tag("dispatcher","ERROR"); + if (mapping.appliesTo(DispatcherType.FORWARD)) + out.tag("dispatcher","FORWARD"); + if (mapping.appliesTo(DispatcherType.INCLUDE)) + out.tag("dispatcher","INCLUDE"); + } + out.closeTag(); + } + } + + if (servlets.getServlets() != null) + { + for (ServletHolder holder : servlets.getServlets()) + outholder(out,md,"servlet",holder); + } + + if (servlets.getServletMappings() != null) + { + for (ServletMapping mapping : servlets.getServletMappings()) + { + out.openTag("servlet-mapping",origin(md,mapping.getServletName() + ".servlet.mappings")); + out.tag("servlet-name",mapping.getServletName()); + if (mapping.getPathSpecs() != null) + for (String s : mapping.getPathSpecs()) + out.tag("url-pattern",s); + out.closeTag(); + } + } + + // Security elements + SecurityHandler security =_webApp. getSecurityHandler(); + + if (security!=null && (security.getRealmName()!=null || security.getAuthMethod()!=null)) + { + out.openTag("login-config"); + if (security.getAuthMethod()!=null) + out.tag("auth-method",origin(md,"auth-method"),security.getAuthMethod()); + if (security.getRealmName()!=null) + out.tag("realm-name",origin(md,"realm-name"),security.getRealmName()); + + + if (Constraint.__FORM_AUTH.equalsIgnoreCase(security.getAuthMethod())) + { + out.openTag("form-login-config"); + out.tag("form-login-page",origin(md,"form-login-page"),security.getInitParameter(FormAuthenticator.__FORM_LOGIN_PAGE)); + out.tag("form-error-page",origin(md,"form-error-page"),security.getInitParameter(FormAuthenticator.__FORM_ERROR_PAGE)); + out.closeTag(); + } + + out.closeTag(); + } + + if (security instanceof ConstraintAware) + { + ConstraintAware ca = (ConstraintAware)security; + for (String r:ca.getRoles()) + out.openTag("security-role") + .tag("role-name",r) + .closeTag(); + + for (ConstraintMapping m : ca.getConstraintMappings()) + { + out.openTag("security-constraint"); + + if (m.getConstraint().getAuthenticate()) + { + out.openTag("auth-constraint"); + if (m.getConstraint().getRoles()!=null) + for (String r : m.getConstraint().getRoles()) + out.tag("role-name",r); + + out.closeTag(); + } + + switch (m.getConstraint().getDataConstraint()) + { + case Constraint.DC_NONE: + out.openTag("user-data-constraint").tag("transport-guarantee","NONE").closeTag(); + break; + + case Constraint.DC_INTEGRAL: + out.openTag("user-data-constraint").tag("transport-guarantee","INTEGRAL").closeTag(); + break; + + case Constraint.DC_CONFIDENTIAL: + out.openTag("user-data-constraint").tag("transport-guarantee","CONFIDENTIAL").closeTag(); + break; + + default: + break; + + } + + out.openTag("web-resource-collection"); + { + if (m.getConstraint().getName()!=null) + out.tag("web-resource-name",m.getConstraint().getName()); + if (m.getPathSpec()!=null) + out.tag("url-pattern",origin(md,"constraint.url."+m.getPathSpec()),m.getPathSpec()); + if (m.getMethod()!=null) + out.tag("http-method",m.getMethod()); + + if (m.getMethodOmissions()!=null) + for (String o:m.getMethodOmissions()) + out.tag("http-method-omission",o); + + out.closeTag(); + } + + out.closeTag(); + + } + } + + if (_webApp.getWelcomeFiles() != null) + { + out.openTag("welcome-file-list"); + for (String welcomeFile:_webApp.getWelcomeFiles()) + { + out.tag("welcome-file", welcomeFile); + } + out.closeTag(); + } + + Map<String,String> localeEncodings = _webApp.getLocaleEncodings(); + if (localeEncodings != null && !localeEncodings.isEmpty()) + { + out.openTag("locale-encoding-mapping-list"); + for (Map.Entry<String, String> entry:localeEncodings.entrySet()) + { + out.openTag("locale-encoding-mapping", origin(md,"locale-encoding."+entry.getKey())); + out.tag("locale", entry.getKey()); + out.tag("encoding", entry.getValue()); + out.closeTag(); + } + out.closeTag(); + } + + //session-config + if (_webApp.getSessionHandler().getSessionManager() != null) + { + out.openTag("session-config"); + int maxInactiveSec = _webApp.getSessionHandler().getSessionManager().getMaxInactiveInterval(); + out.tag("session-timeout", (maxInactiveSec==0?"0":Integer.toString(maxInactiveSec/60))); + + Set<SessionTrackingMode> modes =_webApp. getSessionHandler().getSessionManager().getEffectiveSessionTrackingModes(); + if (modes != null) + { + for (SessionTrackingMode mode:modes) + out.tag("tracking-mode", mode.toString()); + } + + //cookie-config + SessionCookieConfig cookieConfig = _webApp.getSessionHandler().getSessionManager().getSessionCookieConfig(); + if (cookieConfig != null) + { + out.openTag("cookie-config"); + if (cookieConfig.getName() != null) + out.tag("name", origin(md,"cookie-config.name"), cookieConfig.getName()); + + if (cookieConfig.getDomain() != null) + out.tag("domain", origin(md, "cookie-config.domain"), cookieConfig.getDomain()); + + if (cookieConfig.getPath() != null) + out.tag("path", origin(md, "cookie-config.path"), cookieConfig.getPath()); + + if (cookieConfig.getComment() != null) + out.tag("comment", origin(md, "cookie-config.comment"), cookieConfig.getComment()); + + out.tag("http-only", origin(md, "cookie-config.http-only"), Boolean.toString(cookieConfig.isHttpOnly())); + out.tag("secure", origin(md, "cookie-config.secure"), Boolean.toString(cookieConfig.isSecure())); + out.tag("max-age", origin(md, "cookie-config.max-age"), Integer.toString(cookieConfig.getMaxAge())); + out.closeTag(); + } + out.closeTag(); + } + + //error-pages + Map<String,String> errorPages = ((ErrorPageErrorHandler)_webApp.getErrorHandler()).getErrorPages(); + if (errorPages != null) + { + for (Map.Entry<String, String> entry:errorPages.entrySet()) + { + out.openTag("error-page", origin(md, "error."+entry.getKey())); + //a global or default error page has no code or exception + if (!ErrorPageErrorHandler.GLOBAL_ERROR_PAGE.equals(entry.getKey())) + { + if (entry.getKey().matches("\\d{3}")) + out.tag("error-code", entry.getKey()); + else + out.tag("exception-type", entry.getKey()); + } + out.tag("location", entry.getValue()); + out.closeTag(); + } + } + + //mime-types + MimeTypes mimeTypes = _webApp.getMimeTypes(); + if (mimeTypes != null) + { + for (Map.Entry<String, String> entry:mimeTypes.getMimeMap().entrySet()) + { + out.openTag("mime-mapping"); + out.tag("extension", origin(md, "extension."+entry.getKey()), entry.getKey()); + out.tag("mime-type", entry.getValue()); + out.closeTag(); + } + } + + //jsp-config + JspConfig jspConfig = (JspConfig)_webApp.getServletContext().getJspConfigDescriptor(); + if (jspConfig != null) + { + out.openTag("jsp-config"); + Collection<TaglibDescriptor> tlds = jspConfig.getTaglibs(); + if (tlds != null && !tlds.isEmpty()) + { + for (TaglibDescriptor tld:tlds) + { + out.openTag("taglib"); + out.tag("taglib-uri", tld.getTaglibURI()); + out.tag("taglib-location", tld.getTaglibLocation()); + out.closeTag(); + } + } + + Collection<JspPropertyGroupDescriptor> jspPropertyGroups = jspConfig.getJspPropertyGroups(); + if (jspPropertyGroups != null && !jspPropertyGroups.isEmpty()) + { + for (JspPropertyGroupDescriptor jspPropertyGroup:jspPropertyGroups) + { + out.openTag("jsp-property-group"); + Collection<String> strings = jspPropertyGroup.getUrlPatterns(); + if (strings != null && !strings.isEmpty()) + { + for (String urlPattern:strings) + out.tag("url-pattern", urlPattern); + } + + if (jspPropertyGroup.getElIgnored() != null) + out.tag("el-ignored", jspPropertyGroup.getElIgnored()); + + if (jspPropertyGroup.getPageEncoding() != null) + out.tag("page-encoding", jspPropertyGroup.getPageEncoding()); + + if (jspPropertyGroup.getScriptingInvalid() != null) + out.tag("scripting-invalid", jspPropertyGroup.getScriptingInvalid()); + + if (jspPropertyGroup.getIsXml() != null) + out.tag("is-xml", jspPropertyGroup.getIsXml()); + + if (jspPropertyGroup.getDeferredSyntaxAllowedAsLiteral() != null) + out.tag("deferred-syntax-allowed-as-literal", jspPropertyGroup.getDeferredSyntaxAllowedAsLiteral()); + + if (jspPropertyGroup.getTrimDirectiveWhitespaces() != null) + out.tag("trim-directive-whitespaces", jspPropertyGroup.getTrimDirectiveWhitespaces()); + + if (jspPropertyGroup.getDefaultContentType() != null) + out.tag("default-content-type", jspPropertyGroup.getDefaultContentType()); + + if (jspPropertyGroup.getBuffer() != null) + out.tag("buffer", jspPropertyGroup.getBuffer()); + + if (jspPropertyGroup.getErrorOnUndeclaredNamespace() != null) + out.tag("error-on-undeclared-namespace", jspPropertyGroup.getErrorOnUndeclaredNamespace()); + + strings = jspPropertyGroup.getIncludePreludes(); + if (strings != null && !strings.isEmpty()) + { + for (String prelude:strings) + out.tag("include-prelude", prelude); + } + + strings = jspPropertyGroup.getIncludeCodas(); + if (strings != null && !strings.isEmpty()) + { + for (String coda:strings) + out.tag("include-coda", coda); + } + + out.closeTag(); + } + } + + out.closeTag(); + } + + //lifecycle: post-construct, pre-destroy + LifeCycleCallbackCollection lifecycles = ((LifeCycleCallbackCollection)_webApp.getAttribute(LifeCycleCallbackCollection.LIFECYCLE_CALLBACK_COLLECTION)); + if (lifecycles != null) + { + Collection<LifeCycleCallback> tmp = lifecycles.getPostConstructCallbacks(); + + for (LifeCycleCallback c:tmp) + { + out.openTag("post-construct"); + out.tag("lifecycle-callback-class", c.getTargetClassName()); + out.tag("lifecycle-callback-method", c.getMethodName()); + out.closeTag(); + } + + tmp = lifecycles.getPreDestroyCallbacks(); + for (LifeCycleCallback c:tmp) + { + out.openTag("pre-destroy"); + out.tag("lifecycle-callback-class", c.getTargetClassName()); + out.tag("lifecycle-callback-method", c.getMethodName()); + out.closeTag(); + } + } + + out.literal(_extraXML); + + out.closeTag(); + } + catch (Exception e) + { + if (e.getSuppressed() != null) + { + for (Throwable t:e.getSuppressed()) + t.printStackTrace(); + } + } + + return webXml; + } + + /** + * Turn attribute into context-param to store. + * + * @param out + * @param attribute + * @throws IOException + */ + private void addContextParamFromAttribute(XmlAppendable out, String attribute) throws IOException + { + addContextParamFromAttribute(out,attribute,null); + } + + /** + * Turn context attribute into context-param to store. + * + * @param out + * @param attribute + * @param resourceBase + * @throws IOException + */ + private void addContextParamFromAttribute(XmlAppendable out, String attribute, String resourceBase) throws IOException + { + Object o = _webApp.getAttribute(attribute); + if (o == null) + return; + + Collection<?> c = (o instanceof Collection)? (Collection<?>)o:Collections.singletonList(o); + StringBuilder v=new StringBuilder(); + for (Object i:c) + { + if (i!=null) + { + if (v.length()>0) + v.append(",\n "); + else + v.append("\n "); + if (resourceBase==null) + QuotedStringTokenizer.quote(v,i.toString()); + else + QuotedStringTokenizer.quote(v,i.toString().replace(resourceBase,"${WAR}/")); + } + } + out.openTag("context-param") + .tag("param-name",attribute) + .tagCDATA("param-value",v.toString()) + .closeTag(); + } + + /** + * Generate xml for a Holder (Filter/Servlet) + * + * @param out + * @param md + * @param tag + * @param holder + * @throws IOException + */ + private void outholder(XmlAppendable out, MetaData md, String tag, Holder<?> holder) throws IOException + { + out.openTag(tag,Collections.singletonMap("source",holder.getSource().toString())); + String n = holder.getName(); + out.tag(tag + "-name",n); + + String ot = n + "." + tag + "."; + + out.tag(tag + "-class",origin(md,ot + tag + "-class"),holder.getClassName()); + + for (String p : holder.getInitParameters().keySet()) + { + if ("scratchdir".equalsIgnoreCase(p)) //don't preconfigure the temp dir for jsp output + continue; + out.openTag("init-param",origin(md,ot + "init-param." + p)) + .tag("param-name",p) + .tag("param-value",holder.getInitParameter(p)) + .closeTag(); + } + + if (holder instanceof ServletHolder) + { + ServletHolder s = (ServletHolder)holder; + if (s.getForcedPath() != null) + out.tag("jsp-file",s.getForcedPath()); + + if (s.getInitOrder() != 0) + out.tag("load-on-startup",Integer.toString(s.getInitOrder())); + + if (s.getRunAsRole() != null) + out.openTag("run-as",origin(md,ot + "run-as")) + .tag("role-name",s.getRunAsRole()) + .closeTag(); + + Map<String,String> roles = s.getRoleRefMap(); + if (roles!=null) + { + for (Map.Entry<String, String> e : roles.entrySet()) + { + out.openTag("security-role-ref",origin(md,ot+"role-name."+e.getKey())) + .tag("role-name",e.getKey()) + .tag("role-link",e.getValue()) + .closeTag(); + } + } + + if (!s.isEnabled()) + out.tag("enabled",origin(md,ot + "enabled"),"false"); + + //multipart-config + MultipartConfigElement multipartConfig = ((ServletHolder.Registration)s.getRegistration()).getMultipartConfig(); + if (multipartConfig != null) + { + out.openTag("multipart-config", origin(md, s.getName()+".servlet.multipart-config")); + if (multipartConfig.getLocation() != null) + out.tag("location", multipartConfig.getLocation()); + out.tag("max-file-size", Long.toString(multipartConfig.getMaxFileSize())); + out.tag("max-request-size", Long.toString(multipartConfig.getMaxRequestSize())); + out.tag("file-size-threshold", Long.toString(multipartConfig.getFileSizeThreshold())); + out.closeTag(); + } + } + + out.tag("async-supported",origin(md,ot + "async-supported"),holder.isAsyncSupported()?"true":"false"); + out.closeTag(); + } + + + + /** + * Find the origin (web.xml, fragment, annotation etc) of a web artifact from MetaData. + * + * @param md + * @param name + * @return + */ + public Map<String, String> origin(MetaData md, String name) + { + if (!LOG.isDebugEnabled()) + return Collections.emptyMap(); + if (name == null) + return Collections.emptyMap(); + OriginInfo origin = md.getOriginInfo(name); + if (LOG.isDebugEnabled()) LOG.debug("origin of "+name+" is "+origin); + if (origin == null) + return Collections.emptyMap(); + return Collections.singletonMap("origin",origin.toString()); + } + +} diff --git a/jetty-quickstart/src/main/java/org/eclipse/jetty/quickstart/QuickStartWebApp.java b/jetty-quickstart/src/main/java/org/eclipse/jetty/quickstart/QuickStartWebApp.java index bcbcab8080..959cf8015c 100644 --- a/jetty-quickstart/src/main/java/org/eclipse/jetty/quickstart/QuickStartWebApp.java +++ b/jetty-quickstart/src/main/java/org/eclipse/jetty/quickstart/QuickStartWebApp.java @@ -18,51 +18,11 @@ package org.eclipse.jetty.quickstart; -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.EventListener; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; - -import javax.servlet.DispatcherType; -import javax.servlet.MultipartConfigElement; -import javax.servlet.ServletContext; -import javax.servlet.SessionCookieConfig; -import javax.servlet.SessionTrackingMode; -import javax.servlet.descriptor.JspPropertyGroupDescriptor; -import javax.servlet.descriptor.TaglibDescriptor; - -import org.eclipse.jetty.annotations.AnnotationConfiguration; -import org.eclipse.jetty.http.MimeTypes; -import org.eclipse.jetty.plus.annotation.LifeCycleCallback; -import org.eclipse.jetty.plus.annotation.LifeCycleCallbackCollection; -import org.eclipse.jetty.security.ConstraintAware; -import org.eclipse.jetty.security.ConstraintMapping; -import org.eclipse.jetty.security.SecurityHandler; -import org.eclipse.jetty.security.authentication.FormAuthenticator; -import org.eclipse.jetty.servlet.ErrorPageErrorHandler; -import org.eclipse.jetty.servlet.FilterHolder; -import org.eclipse.jetty.servlet.FilterMapping; -import org.eclipse.jetty.servlet.Holder; -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.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.util.security.Constraint; -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.xml.XmlAppendable; /** * QuickStartWar @@ -190,8 +150,7 @@ public class QuickStartWebApp extends WebAppContext { LOG.info("Quickstart preconfigure: {}(war={},dir={})",this,war,dir); - _preconfigProcessor = new PreconfigureDescriptorProcessor(); - + _preconfigProcessor = new PreconfigureDescriptorProcessor(); getMetaData().addDescriptorProcessor(_preconfigProcessor); setPreconfigure(true); _startWebapp=true; @@ -203,518 +162,11 @@ public class QuickStartWebApp extends WebAppContext } - public void generateQuickstartWebXml(String extraXML) throws IOException + public void generateQuickstartWebXml(String extraXML) throws Exception { - getMetaData().getOrigins(); - // dumpStdErr(); - - if (getBaseResource()==null) - throw new IllegalArgumentException("No base resource for "+this); - - File webxml = new File(getWebInf().getFile(),"quickstart-web.xml"); - - LOG.info("Quickstart generate {}",webxml); - - XmlAppendable out = new XmlAppendable(new FileOutputStream(webxml),"UTF-8"); - MetaData md = getMetaData(); - - Map<String, String> webappAttr = new HashMap<>(); - webappAttr.put("xmlns","http://xmlns.jcp.org/xml/ns/javaee"); - webappAttr.put("xmlns:xsi","http://www.w3.org/2001/XMLSchema-instance"); - webappAttr.put("xsi:schemaLocation","http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"); - webappAttr.put("metadata-complete","true"); - webappAttr.put("version","3.1"); - - out.open("web-app",webappAttr); - - if (getDisplayName() != null) - out.tag("display-name",getDisplayName()); - - // Set some special context parameters - - // The location of the war file on disk - String resourceBase=getBaseResource().getFile().getCanonicalFile().getAbsoluteFile().toURI().toString(); - - // The library order - addContextParamFromAttribute(out,ServletContext.ORDERED_LIBS); - //the servlet container initializers - addContextParamFromAttribute(out,AnnotationConfiguration.CONTAINER_INITIALIZERS); - //the tlds discovered - addContextParamFromAttribute(out,MetaInfConfiguration.METAINF_TLDS,resourceBase); - //the META-INF/resources discovered - addContextParamFromAttribute(out,MetaInfConfiguration.METAINF_RESOURCES,resourceBase); - - - // init params - for (String p : getInitParams().keySet()) - out.open("context-param",origin(md,"context-param." + p)) - .tag("param-name",p) - .tag("param-value",getInitParameter(p)) - .close(); - - if (getEventListeners() != null) - for (EventListener e : getEventListeners()) - out.open("listener",origin(md,e.getClass().getCanonicalName() + ".listener")) - .tag("listener-class",e.getClass().getCanonicalName()) - .close(); - - ServletHandler servlets = getServletHandler(); - - if (servlets.getFilters() != null) - { - for (FilterHolder holder : servlets.getFilters()) - outholder(out,md,"filter",holder); - } - - if (servlets.getFilterMappings() != null) - { - for (FilterMapping mapping : servlets.getFilterMappings()) - { - out.open("filter-mapping"); - out.tag("filter-name",mapping.getFilterName()); - if (mapping.getPathSpecs() != null) - for (String s : mapping.getPathSpecs()) - out.tag("url-pattern",s); - if (mapping.getServletNames() != null) - for (String n : mapping.getServletNames()) - out.tag("servlet-name",n); - - if (!mapping.isDefaultDispatches()) - { - if (mapping.appliesTo(DispatcherType.REQUEST)) - out.tag("dispatcher","REQUEST"); - if (mapping.appliesTo(DispatcherType.ASYNC)) - out.tag("dispatcher","ASYNC"); - if (mapping.appliesTo(DispatcherType.ERROR)) - out.tag("dispatcher","ERROR"); - if (mapping.appliesTo(DispatcherType.FORWARD)) - out.tag("dispatcher","FORWARD"); - if (mapping.appliesTo(DispatcherType.INCLUDE)) - out.tag("dispatcher","INCLUDE"); - } - out.close(); - } - } - - if (servlets.getServlets() != null) - { - for (ServletHolder holder : servlets.getServlets()) - outholder(out,md,"servlet",holder); - } - - if (servlets.getServletMappings() != null) - { - for (ServletMapping mapping : servlets.getServletMappings()) - { - out.open("servlet-mapping",origin(md,mapping.getServletName() + ".servlet.mappings")); - out.tag("servlet-name",mapping.getServletName()); - if (mapping.getPathSpecs() != null) - for (String s : mapping.getPathSpecs()) - out.tag("url-pattern",s); - out.close(); - } - } - - // Security elements - SecurityHandler security = getSecurityHandler(); - - if (security!=null && (security.getRealmName()!=null || security.getAuthMethod()!=null)) - { - out.open("login-config"); - if (security.getAuthMethod()!=null) - out.tag("auth-method",origin(md,"auth-method"),security.getAuthMethod()); - if (security.getRealmName()!=null) - out.tag("realm-name",origin(md,"realm-name"),security.getRealmName()); - - - if (Constraint.__FORM_AUTH.equalsIgnoreCase(security.getAuthMethod())) - { - out.open("form-login-config"); - out.tag("form-login-page",origin(md,"form-login-page"),security.getInitParameter(FormAuthenticator.__FORM_LOGIN_PAGE)); - out.tag("form-error-page",origin(md,"form-error-page"),security.getInitParameter(FormAuthenticator.__FORM_ERROR_PAGE)); - out.close(); - } - - out.close(); - } - - if (security instanceof ConstraintAware) - { - ConstraintAware ca = (ConstraintAware)security; - for (String r:ca.getRoles()) - out.open("security-role") - .tag("role-name",r) - .close(); - - for (ConstraintMapping m : ca.getConstraintMappings()) - { - out.open("security-constraint"); - - if (m.getConstraint().getAuthenticate()) - { - out.open("auth-constraint"); - if (m.getConstraint().getRoles()!=null) - for (String r : m.getConstraint().getRoles()) - out.tag("role-name",r); - - out.close(); - } - - switch (m.getConstraint().getDataConstraint()) - { - case Constraint.DC_NONE: - out.open("user-data-constraint").tag("transport-guarantee","NONE").close(); - break; - - case Constraint.DC_INTEGRAL: - out.open("user-data-constraint").tag("transport-guarantee","INTEGRAL").close(); - break; - - case Constraint.DC_CONFIDENTIAL: - out.open("user-data-constraint").tag("transport-guarantee","CONFIDENTIAL").close(); - break; - - default: - break; - - } - - out.open("web-resource-collection"); - { - if (m.getConstraint().getName()!=null) - out.tag("web-resource-name",m.getConstraint().getName()); - if (m.getPathSpec()!=null) - out.tag("url-pattern",origin(md,"constraint.url."+m.getPathSpec()),m.getPathSpec()); - if (m.getMethod()!=null) - out.tag("http-method",m.getMethod()); - - if (m.getMethodOmissions()!=null) - for (String o:m.getMethodOmissions()) - out.tag("http-method-omission",o); - - out.close(); - } - - out.close(); - - } - } - - if (getWelcomeFiles() != null) - { - out.open("welcome-file-list"); - for (String welcomeFile:getWelcomeFiles()) - { - out.tag("welcome-file", welcomeFile); - } - out.close(); - } - - Map<String,String> localeEncodings = getLocaleEncodings(); - if (localeEncodings != null && !localeEncodings.isEmpty()) - { - out.open("locale-encoding-mapping-list"); - for (Map.Entry<String, String> entry:localeEncodings.entrySet()) - { - out.open("locale-encoding-mapping", origin(md,"locale-encoding."+entry.getKey())); - out.tag("locale", entry.getKey()); - out.tag("encoding", entry.getValue()); - out.close(); - } - out.close(); - } - - //session-config - if (getSessionHandler().getSessionManager() != null) - { - out.open("session-config"); - int maxInactiveSec = getSessionHandler().getSessionManager().getMaxInactiveInterval(); - out.tag("session-timeout", (maxInactiveSec==0?"0":Integer.toString(maxInactiveSec/60))); - - Set<SessionTrackingMode> modes = getSessionHandler().getSessionManager().getEffectiveSessionTrackingModes(); - if (modes != null) - { - for (SessionTrackingMode mode:modes) - out.tag("tracking-mode", mode.toString()); - } - - //cookie-config - SessionCookieConfig cookieConfig = getSessionHandler().getSessionManager().getSessionCookieConfig(); - if (cookieConfig != null) - { - out.open("cookie-config"); - if (cookieConfig.getName() != null) - out.tag("name", origin(md,"cookie-config.name"), cookieConfig.getName()); - - if (cookieConfig.getDomain() != null) - out.tag("domain", origin(md, "cookie-config.domain"), cookieConfig.getDomain()); - - if (cookieConfig.getPath() != null) - out.tag("path", origin(md, "cookie-config.path"), cookieConfig.getPath()); - - if (cookieConfig.getComment() != null) - out.tag("comment", origin(md, "cookie-config.comment"), cookieConfig.getComment()); - - out.tag("http-only", origin(md, "cookie-config.http-only"), Boolean.toString(cookieConfig.isHttpOnly())); - out.tag("secure", origin(md, "cookie-config.secure"), Boolean.toString(cookieConfig.isSecure())); - out.tag("max-age", origin(md, "cookie-config.max-age"), Integer.toString(cookieConfig.getMaxAge())); - out.close(); - } - out.close(); - } - - //error-pages - Map<String,String> errorPages = ((ErrorPageErrorHandler)getErrorHandler()).getErrorPages(); - if (errorPages != null) - { - for (Map.Entry<String, String> entry:errorPages.entrySet()) - { - out.open("error-page", origin(md, "error."+entry.getKey())); - //a global or default error page has no code or exception - if (!ErrorPageErrorHandler.GLOBAL_ERROR_PAGE.equals(entry.getKey())) - { - if (entry.getKey().matches("\\d{3}")) - out.tag("error-code", entry.getKey()); - else - out.tag("exception-type", entry.getKey()); - } - out.tag("location", entry.getValue()); - out.close(); - } - } - - //mime-types - MimeTypes mimeTypes = getMimeTypes(); - if (mimeTypes != null) - { - for (Map.Entry<String, String> entry:mimeTypes.getMimeMap().entrySet()) - { - out.open("mime-mapping"); - out.tag("extension", origin(md, "extension."+entry.getKey()), entry.getKey()); - out.tag("mime-type", entry.getValue()); - out.close(); - } - } - - //jsp-config - JspConfig jspConfig = (JspConfig)getServletContext().getJspConfigDescriptor(); - if (jspConfig != null) - { - out.open("jsp-config"); - Collection<TaglibDescriptor> tlds = jspConfig.getTaglibs(); - if (tlds != null && !tlds.isEmpty()) - { - for (TaglibDescriptor tld:tlds) - { - out.open("taglib"); - out.tag("taglib-uri", tld.getTaglibURI()); - out.tag("taglib-location", tld.getTaglibLocation()); - out.close(); - } - } - - Collection<JspPropertyGroupDescriptor> jspPropertyGroups = jspConfig.getJspPropertyGroups(); - if (jspPropertyGroups != null && !jspPropertyGroups.isEmpty()) - { - for (JspPropertyGroupDescriptor jspPropertyGroup:jspPropertyGroups) - { - out.open("jsp-property-group"); - Collection<String> strings = jspPropertyGroup.getUrlPatterns(); - if (strings != null && !strings.isEmpty()) - { - for (String urlPattern:strings) - out.tag("url-pattern", urlPattern); - } - - if (jspPropertyGroup.getElIgnored() != null) - out.tag("el-ignored", jspPropertyGroup.getElIgnored()); - - if (jspPropertyGroup.getPageEncoding() != null) - out.tag("page-encoding", jspPropertyGroup.getPageEncoding()); - - if (jspPropertyGroup.getScriptingInvalid() != null) - out.tag("scripting-invalid", jspPropertyGroup.getScriptingInvalid()); - - if (jspPropertyGroup.getIsXml() != null) - out.tag("is-xml", jspPropertyGroup.getIsXml()); - - if (jspPropertyGroup.getDeferredSyntaxAllowedAsLiteral() != null) - out.tag("deferred-syntax-allowed-as-literal", jspPropertyGroup.getDeferredSyntaxAllowedAsLiteral()); - - if (jspPropertyGroup.getTrimDirectiveWhitespaces() != null) - out.tag("trim-directive-whitespaces", jspPropertyGroup.getTrimDirectiveWhitespaces()); - - if (jspPropertyGroup.getDefaultContentType() != null) - out.tag("default-content-type", jspPropertyGroup.getDefaultContentType()); - - if (jspPropertyGroup.getBuffer() != null) - out.tag("buffer", jspPropertyGroup.getBuffer()); - - if (jspPropertyGroup.getErrorOnUndeclaredNamespace() != null) - out.tag("error-on-undeclared-namespace", jspPropertyGroup.getErrorOnUndeclaredNamespace()); - - strings = jspPropertyGroup.getIncludePreludes(); - if (strings != null && !strings.isEmpty()) - { - for (String prelude:strings) - out.tag("include-prelude", prelude); - } - - strings = jspPropertyGroup.getIncludeCodas(); - if (strings != null && !strings.isEmpty()) - { - for (String coda:strings) - out.tag("include-coda", coda); - } - - out.close(); - } - } - - out.close(); - } - - //lifecycle: post-construct, pre-destroy - LifeCycleCallbackCollection lifecycles = ((LifeCycleCallbackCollection)getAttribute(LifeCycleCallbackCollection.LIFECYCLE_CALLBACK_COLLECTION)); - if (lifecycles != null) - { - Collection<LifeCycleCallback> tmp = lifecycles.getPostConstructCallbacks(); - - for (LifeCycleCallback c:tmp) - { - out.open("post-construct"); - out.tag("lifecycle-callback-class", c.getTargetClassName()); - out.tag("lifecycle-callback-method", c.getMethodName()); - out.close(); - } - - tmp = lifecycles.getPreDestroyCallbacks(); - for (LifeCycleCallback c:tmp) - { - out.open("pre-destroy"); - out.tag("lifecycle-callback-class", c.getTargetClassName()); - out.tag("lifecycle-callback-method", c.getMethodName()); - out.close(); - } - } - - out.literal(extraXML); - - out.close(); - } - - private void addContextParamFromAttribute(XmlAppendable out, String attribute) throws IOException - { - addContextParamFromAttribute(out,attribute,null); - } - - private void addContextParamFromAttribute(XmlAppendable out, String attribute, String resourceBase) throws IOException - { - Object o=getAttribute(attribute); - if (o==null) - return; - - Collection<?> c = (o instanceof Collection)? (Collection<?>)o:Collections.singletonList(o); - StringBuilder v=new StringBuilder(); - for (Object i:c) - { - if (i!=null) - { - if (v.length()>0) - v.append(",\n "); - else - v.append("\n "); - if (resourceBase==null) - QuotedStringTokenizer.quote(v,i.toString()); - else - QuotedStringTokenizer.quote(v,i.toString().replace(resourceBase,"${WAR}/")); - } - } - out.open("context-param") - .tag("param-name",attribute) - .tagCDATA("param-value",v.toString()) - .close(); - } - - private static void outholder(XmlAppendable out, MetaData md, String tag, Holder<?> holder) throws IOException - { - out.open(tag,Collections.singletonMap("source",holder.getSource().toString())); - String n = holder.getName(); - out.tag(tag + "-name",n); - - String ot = n + "." + tag + "."; - - out.tag(tag + "-class",origin(md,ot + tag + "-class"),holder.getClassName()); - - for (String p : holder.getInitParameters().keySet()) - { - if ("scratchdir".equalsIgnoreCase(p)) //don't preconfigure the temp dir for jsp output - continue; - out.open("init-param",origin(md,ot + "init-param." + p)) - .tag("param-name",p) - .tag("param-value",holder.getInitParameter(p)) - .close(); - } - - if (holder instanceof ServletHolder) - { - ServletHolder s = (ServletHolder)holder; - if (s.getForcedPath() != null) - out.tag("jsp-file",s.getForcedPath()); - - if (s.getInitOrder() != 0) - out.tag("load-on-startup",Integer.toString(s.getInitOrder())); - - if (s.getRunAsRole() != null) - out.open("run-as",origin(md,ot + "run-as")) - .tag("role-name",s.getRunAsRole()) - .close(); - - Map<String,String> roles = s.getRoleRefMap(); - if (roles!=null) - { - for (Map.Entry<String, String> e : roles.entrySet()) - { - out.open("security-role-ref",origin(md,ot+"role-name."+e.getKey())) - .tag("role-name",e.getKey()) - .tag("role-link",e.getValue()) - .close(); - } - } - - if (!s.isEnabled()) - out.tag("enabled",origin(md,ot + "enabled"),"false"); - - //multipart-config - MultipartConfigElement multipartConfig = ((ServletHolder.Registration)s.getRegistration()).getMultipartConfig(); - if (multipartConfig != null) - { - out.open("multipart-config", origin(md, s.getName()+".servlet.multipart-config")); - if (multipartConfig.getLocation() != null) - out.tag("location", multipartConfig.getLocation()); - out.tag("max-file-size", Long.toString(multipartConfig.getMaxFileSize())); - out.tag("max-request-size", Long.toString(multipartConfig.getMaxRequestSize())); - out.tag("file-size-threshold", Long.toString(multipartConfig.getFileSizeThreshold())); - out.close(); - } - } - - out.tag("async-supported",origin(md,ot + "async-supported"),holder.isAsyncSupported()?"true":"false"); - out.close(); + QuickStartDescriptorGenerator generator = new QuickStartDescriptorGenerator(this, this.getWebInf().getFile(), extraXML); + generator.generateQuickStartWebXml(); } - public static Map<String, String> origin(MetaData md, String name) - { - if (!LOG.isDebugEnabled()) - return Collections.emptyMap(); - if (name == null) - return Collections.emptyMap(); - OriginInfo origin = md.getOriginInfo(name); - // System.err.println("origin of "+name+" is "+origin); - if (origin == null) - return Collections.emptyMap(); - return Collections.singletonMap("origin",origin.toString()); - - } - + } diff --git a/jetty-xml/src/main/java/org/eclipse/jetty/xml/XmlAppendable.java b/jetty-xml/src/main/java/org/eclipse/jetty/xml/XmlAppendable.java index 79711a66a0..221b75914f 100644 --- a/jetty-xml/src/main/java/org/eclipse/jetty/xml/XmlAppendable.java +++ b/jetty-xml/src/main/java/org/eclipse/jetty/xml/XmlAppendable.java @@ -61,7 +61,7 @@ public class XmlAppendable _out.append("<?xml version=\"1.0\" encoding=\""+encoding+"\"?>\n"); } - public XmlAppendable open(String tag, Map<String,String> attributes) throws IOException + public XmlAppendable openTag(String tag, Map<String,String> attributes) throws IOException { _out.append(_space).append('<').append(tag); attributes(attributes); @@ -72,7 +72,7 @@ public class XmlAppendable return this; } - public XmlAppendable open(String tag) throws IOException + public XmlAppendable openTag(String tag) throws IOException { _out.append(_space).append('<').append(tag).append(">\n"); _space=_space+SPACES.substring(0,_indent); @@ -159,7 +159,7 @@ public class XmlAppendable return this; } - public XmlAppendable close() throws IOException + public XmlAppendable closeTag() throws IOException { if (_tags.isEmpty()) throw new IllegalStateException("Tags closed"); diff --git a/jetty-xml/src/test/java/org/eclipse/jetty/xml/XmlAppendableTest.java b/jetty-xml/src/test/java/org/eclipse/jetty/xml/XmlAppendableTest.java index 6a1556e4dd..0e491f6b74 100644 --- a/jetty-xml/src/test/java/org/eclipse/jetty/xml/XmlAppendableTest.java +++ b/jetty-xml/src/test/java/org/eclipse/jetty/xml/XmlAppendableTest.java @@ -33,7 +33,7 @@ public class XmlAppendableTest XmlAppendable out = new XmlAppendable(b); Map<String, String> attr = new LinkedHashMap<>(); - out.open("test"); + out.openTag("test"); attr.put("name", "attr value"); attr.put("noval", null); @@ -43,10 +43,10 @@ public class XmlAppendableTest out.tag("tag", attr); out.tag("tag", attr, "content"); - out.open("level1").tag("tag", "content").tag("tag", "content").close(); - out.open("level1", attr).open("level2").tag("tag", "content").tag("tag", "content").close().close(); + out.openTag("level1").tag("tag", "content").tag("tag", "content").closeTag(); + out.openTag("level1", attr).openTag("level2").tag("tag", "content").tag("tag", "content").closeTag().closeTag(); - out.close(); + out.closeTag(); String expected = "" + "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + |