Replay changes after change to jetty-9.4.x base branch.
This commit is contained in:
Jan Bartel 2017-08-10 13:56:05 +10:00
parent 67d93f9f74
commit 2acb97db2f
14 changed files with 1053 additions and 643 deletions

View File

@ -10,8 +10,8 @@
<name>Jetty :: Jetty Maven Plugin</name>
<description>Jetty maven plugins</description>
<properties>
<mavenVersion>3.0.3</mavenVersion>
<pluginToolsVersion>3.4</pluginToolsVersion>
<mavenVersion>3.5.0</mavenVersion>
<pluginToolsVersion>3.5</pluginToolsVersion>
<bundle-symbolic-name>${project.groupId}.maven.plugin</bundle-symbolic-name>
<it.debug>false</it.debug>
<jetty.stopKey>FOOBEER</jetty.stopKey>
@ -80,6 +80,11 @@
<version>${pluginToolsVersion}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.maven.shared</groupId>
<artifactId>maven-artifact-transfer</artifactId>
<version>0.9.1</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-util</artifactId>

View File

@ -18,8 +18,11 @@
package org.eclipse.jetty.maven.plugin;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.MalformedURLException;
@ -55,6 +58,7 @@ import org.eclipse.jetty.server.handler.HandlerCollection;
import org.eclipse.jetty.util.PathWatcher;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.resource.Resource;
import org.eclipse.jetty.util.resource.ResourceCollection;
import org.eclipse.jetty.xml.XmlConfiguration;
/**
@ -538,8 +542,15 @@ public abstract class AbstractJettyMojo extends AbstractMojo
//context xml file can OVERRIDE those settings
if (contextXml != null)
{
File file = FileUtils.getFile(contextXml);
XmlConfiguration xmlConfiguration = new XmlConfiguration(Resource.toURL(file));
Path path = Paths.get(contextXml);
if (!path.isAbsolute())
{
Path workDir = Paths.get(System.getProperty("user.dir"));
path = workDir.resolve(path);
contextXml = path.toFile().getAbsolutePath();
}
XmlConfiguration xmlConfiguration = new XmlConfiguration(Resource.toURL(path.toFile()));
getLog().info("Applying context xml file "+contextXml);
xmlConfiguration.configure(webApp);
}

View File

@ -0,0 +1,514 @@
//
// ========================================================================
// Copyright (c) 1995-2017 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.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.nio.file.FileAlreadyExistsException;
import java.nio.file.FileVisitOption;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.repository.ArtifactRepository;
import org.apache.maven.execution.MavenSession;
import org.apache.maven.model.Dependency;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugin.descriptor.PluginDescriptor;
import org.apache.maven.project.DefaultProjectBuildingRequest;
import org.apache.maven.project.ProjectBuildingRequest;
import org.apache.maven.shared.artifact.DefaultArtifactCoordinate;
import org.apache.maven.shared.artifact.resolve.ArtifactResolver;
import org.apache.maven.shared.artifact.resolve.ArtifactResolverException;
import org.eclipse.jetty.util.IO;
import org.eclipse.jetty.util.TypeUtil;
import org.eclipse.jetty.util.resource.JarResource;
import org.eclipse.jetty.util.resource.Resource;
/**
* JettyRunDistro
*
* @goal run-distro
* @requiresDependencyResolution test
* @execute phase="test-compile"
* @description Runs unassembled webapp in a locally installed jetty distro
*/
public class JettyRunDistro extends JettyRunMojo
{
public static final String JETTY_HOME_GROUPID = "org.eclipse.jetty";
public static final String JETTY_HOME_ARTIFACTID = "jetty-home";
/**
* This plugin
*
* @parameter default-value="${plugin}"
* @readonly
* @required
*/
protected PluginDescriptor plugin;
/**
* The target directory
*
* @parameter default-value="${project.build.directory}"
* @required
* @readonly
*/
protected File target;
/**
*
* @parameter
*/
private File jettyHome;
/**
*
* @parameter
*/
private File jettyBase;
/**
* Optional list of other modules to
* activate.
* @parameter
*/
private String[] modules;
/**
* Optional list of jetty properties to put on the command line
* @parameter
*/
private String[] properties;
/**
* @parameter default-value="${session}"
* @required
* @readonly
*/
private MavenSession session;
/**
* The project's remote repositories to use for the resolution.
*
* @parameter default-value="${project.remoteArtifactRepositories}"
* @required
* @readonly
*/
private List<ArtifactRepository> remoteRepositories;
/**
* @component
*/
private ArtifactResolver artifactResolver;
/**
* @parameter default-value="${plugin.version}"
* @readonly
*/
private String pluginVersion;
/**
* @parameter default-value="true"
*/
private boolean waitForChild;
private File targetBase;
private List<Dependency> libExtJars;
// IDEAS:
// 5. try to use the scanner as normal and remake the properties and context.xml file to get the
// deployer to automatically redeploy it on changes.
/**
* @see org.eclipse.jetty.maven.plugin.JettyRunMojo#execute()
*/
@Override
public void execute() throws MojoExecutionException, MojoFailureException
{
List<Dependency> pdeps = plugin.getPlugin().getDependencies();
if (pdeps != null && !pdeps.isEmpty())
{
boolean warned = false;
for (Dependency d:pdeps)
{
if (d.getGroupId().equalsIgnoreCase("org.eclipse.jetty"))
{
if (!warned)
{
getLog().warn("Jetty jars detected in <pluginDependencies>: use <modules> in <configuration> parameter instead to select appropriate jetty modules.");
warned = true;
}
}
else
{
if (libExtJars == null)
libExtJars = new ArrayList<>();
libExtJars.add(d);
}
}
}
super.execute();
}
/**
* @see org.eclipse.jetty.maven.plugin.AbstractJettyMojo#startJetty()
*/
@Override
public void startJetty() throws MojoExecutionException
{
//don't start jetty locally, set up enough configuration to run a new process
//with a jetty distro
try
{
printSystemProperties();
//download and install jetty-home if necessary
configureJettyHome();
//ensure config of the webapp based on settings in plugin
configureWebApplication();
//configure jetty base
configureJettyBase();
//create the command to run the new process
ProcessBuilder command = configureCommand();
if (waitForChild)
{
command.inheritIO();
}
else
{
command.redirectErrorStream(true);
command.redirectOutput(new File(target, "jetty.out"));
}
Process process = command.start();
if (waitForChild)
process.waitFor();
}
catch (Exception e)
{
throw new MojoExecutionException("Failed to start Jetty", e);
}
}
/**
* If jetty home does not exist, download it and
* unpack to build dir.
*
* @throws Exception
*/
public void configureJettyHome() throws Exception
{
if (jettyHome == null)
{
//no jetty home, download from repo and unpack it. Get the same version as the plugin
Artifact jettyHomeArtifact = resolveArtifact(JETTY_HOME_GROUPID, JETTY_HOME_ARTIFACTID, pluginVersion, "zip");
JarResource res = (JarResource) JarResource.newJarResource(Resource.newResource(jettyHomeArtifact.getFile()));
res.copyTo(target);
//zip will unpack to target/jetty-home-<VERSION>
jettyHome = new File (target, JETTY_HOME_ARTIFACTID+"-"+pluginVersion);
}
else
{
if (!jettyHome.exists())
throw new IllegalStateException(jettyHome.getAbsolutePath()+" does not exist");
}
getLog().info("jetty.home = "+jettyHome.getAbsolutePath());
}
/**
* Resolve an Artifact from remote repo if necessary.
*
* @param groupId the groupid of the artifact
* @param artifactId the artifactId of the artifact
* @param version the version of the artifact
* @param extension the extension type of the artifact eg "zip", "jar"
* @return the artifact from the local or remote repo
* @throws ArtifactResolverException
*/
public Artifact resolveArtifact (String groupId, String artifactId, String version, String extension)
throws ArtifactResolverException
{
DefaultArtifactCoordinate coordinate = new DefaultArtifactCoordinate();
coordinate.setGroupId(groupId);
coordinate.setArtifactId(artifactId);
coordinate.setVersion(version);
coordinate.setExtension(extension);
ProjectBuildingRequest buildingRequest =
new DefaultProjectBuildingRequest(session.getProjectBuildingRequest());
buildingRequest.setRemoteRepositories(remoteRepositories);
return artifactResolver.resolveArtifact( buildingRequest, coordinate ).getArtifact();
}
/**
* Create or configure a jetty base.
*
* @throws Exception
*/
public void configureJettyBase() throws Exception
{
if (jettyBase != null && !jettyBase.exists())
throw new IllegalStateException(jettyBase.getAbsolutePath() +" does not exist");
targetBase = new File(target, "jetty-base");
Path targetBasePath = targetBase.toPath();
if (Files.exists(targetBasePath))
IO.delete(targetBase);
targetBase.mkdirs();
if (jettyBase != null)
{
Path jettyBasePath = jettyBase.toPath();
//copy the existing jetty base
Files.walkFileTree(jettyBasePath,EnumSet.of(FileVisitOption.FOLLOW_LINKS),
Integer.MAX_VALUE,
new SimpleFileVisitor<Path>()
{
/**
* @see java.nio.file.SimpleFileVisitor#preVisitDirectory(java.lang.Object, java.nio.file.attribute.BasicFileAttributes)
*/
@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException
{
Path targetDir = targetBasePath.resolve(jettyBasePath.relativize(dir));
try
{
Files.copy(dir, targetDir);
}
catch (FileAlreadyExistsException e)
{
if (!Files.isDirectory(targetDir)) //ignore attempt to recreate dir
throw e;
}
return FileVisitResult.CONTINUE;
}
/**
* @see java.nio.file.SimpleFileVisitor#visitFile(java.lang.Object, java.nio.file.attribute.BasicFileAttributes)
*/
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException
{
if (contextXml != null && Files.isSameFile(Paths.get(contextXml), file))
return FileVisitResult.CONTINUE; //skip copying the context xml file
Files.copy(file, targetBasePath.resolve(jettyBasePath.relativize(file)));
return FileVisitResult.CONTINUE;
}
});
}
//make the jetty base structure
Path modulesPath = Files.createDirectories(targetBasePath.resolve("modules"));
Path etcPath = Files.createDirectories(targetBasePath.resolve("etc"));
Path libPath = Files.createDirectories(targetBasePath.resolve("lib"));
Path webappPath = Files.createDirectories(targetBasePath.resolve("webapps"));
Path mavenLibPath = Files.createDirectories(libPath.resolve("maven"));
//copy in the jetty-maven-plugin jar
URI thisJar = TypeUtil.getLocationOfClass(this.getClass());
if (thisJar == null)
throw new IllegalStateException("Can't find jar for jetty-maven-plugin");
try(InputStream jarStream = thisJar.toURL().openStream();
FileOutputStream fileStream = new FileOutputStream(mavenLibPath.resolve("plugin.jar").toFile()))
{
IO.copy(jarStream,fileStream);
}
//copy in the maven.xml webapp file
try (InputStream mavenXmlStream = getClass().getClassLoader().getResourceAsStream("maven.xml");
FileOutputStream fileStream = new FileOutputStream(webappPath.resolve("maven.xml").toFile()))
{
IO.copy(mavenXmlStream, fileStream);
}
//copy in the maven.mod file
try (InputStream mavenModStream = getClass().getClassLoader().getResourceAsStream("maven.mod");
FileOutputStream fileStream = new FileOutputStream(modulesPath.resolve("maven.mod").toFile()))
{
IO.copy(mavenModStream, fileStream);
}
//if there were plugin dependencies, copy them into lib/ext
if (libExtJars != null && !libExtJars.isEmpty())
{
Path libExtPath = Files.createDirectories(libPath.resolve("ext"));
for (Dependency d:libExtJars)
{
Artifact a = resolveArtifact(d.getGroupId(), d.getArtifactId(), d.getVersion(), d.getType());
try (InputStream jarStream = new FileInputStream(a.getFile());
FileOutputStream fileStream = new FileOutputStream(libExtPath.resolve(d.getGroupId()+"."+d.getArtifactId()+"-"+d.getVersion()+"."+d.getType()).toFile()))
{
IO.copy(jarStream, fileStream);
}
}
}
//create properties file that describes the webapp
createPropertiesFile(etcPath.resolve("maven.props").toFile());
}
/**
* Convert webapp config to properties
*
* @param file the file to place the properties into
* @throws Exception
*/
public void createPropertiesFile (File file)
throws Exception
{
WebAppPropertyConverter.toProperties(webApp, file, contextXml);
}
/**
* Make the command to spawn a process to
* run jetty from a distro.
*
* @return
*/
public ProcessBuilder configureCommand()
{
List<String> cmd = new ArrayList<>();
cmd.add("java");
cmd.add("-jar");
cmd.add(new File(jettyHome, "start.jar").getAbsolutePath());
cmd.add("-DSTOP.PORT="+stopPort);
if (stopKey != null)
cmd.add("-DSTOP.KEY="+stopKey);
StringBuilder tmp = new StringBuilder();
tmp.append("--module=");
tmp.append("server,http,webapp,deploy");
if (modules != null)
{
for (String m:modules)
{
if (tmp.indexOf(m) < 0)
tmp.append(","+m);
}
}
if (libExtJars != null && !libExtJars.isEmpty() && tmp.indexOf("ext") < 0)
tmp.append(",ext");
tmp.append(",maven");
cmd.add(tmp.toString());
if (properties != null)
{
tmp.delete(0, tmp.length());
for (String p:properties)
tmp.append(" "+p);
cmd.add(tmp.toString());
}
ProcessBuilder builder = new ProcessBuilder(cmd);
builder.directory(targetBase);
return builder;
}
/**
* @see org.eclipse.jetty.maven.plugin.AbstractJettyMojo#startScanner()
*/
@Override
public void startScanner() throws Exception
{
//don't scan
}
/**
* @see org.eclipse.jetty.maven.plugin.AbstractJettyMojo#stopScanner()
*/
@Override
public void stopScanner() throws Exception
{
//don't scan
}
/**
* @see org.eclipse.jetty.maven.plugin.AbstractJettyMojo#restartWebApp(boolean)
*/
@Override
public void restartWebApp(boolean reconfigureScanner) throws Exception
{
//do nothing
}
/**
* @see org.eclipse.jetty.maven.plugin.AbstractJettyMojo#configureScanner()
*/
@Override
public void configureScanner() throws MojoExecutionException
{
//do nothing
}
}

View File

@ -144,8 +144,6 @@ public class JettyRunForkedMojo extends JettyRunMojo
private Random random;
private Resource originalBaseResource;
private boolean originalPersistTemp;
@ -257,9 +255,6 @@ public class JettyRunForkedMojo extends JettyRunMojo
//ensure config of the webapp based on settings in plugin
configureWebApplication();
//copy the base resource as configured by the plugin
originalBaseResource = webApp.getBaseResource();
//get the original persistance setting
originalPersistTemp = webApp.isPersistTempDirectory();
@ -302,6 +297,8 @@ public class JettyRunForkedMojo extends JettyRunMojo
webApp.stop();
if (tpool != null)
tpool.stop();
@ -461,106 +458,7 @@ public class JettyRunForkedMojo extends JettyRunMojo
{
//work out the configuration based on what is configured in the pom
File propsFile = new File (target, "fork.props");
if (propsFile.exists())
propsFile.delete();
propsFile.createNewFile();
//propsFile.deleteOnExit();
Properties props = new Properties();
//web.xml
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 (webApp.getContextPath() != null)
{
props.put("context.path", webApp.getContextPath());
}
//tmp dir
props.put("tmp.dir", webApp.getTempDirectory().getAbsolutePath());
props.put("tmp.dir.persist", Boolean.toString(originalPersistTemp));
//send over the original base resources before any overlays were added
if (originalBaseResource instanceof ResourceCollection)
props.put("base.dirs.orig", toCSV(((ResourceCollection)originalBaseResource).getResources()));
else
props.put("base.dirs.orig", originalBaseResource.toString());
//send over the calculated resource bases that includes unpacked overlays, but none of the
//meta-inf resources
Resource postOverlayResources = (Resource)webApp.getAttribute(MavenWebInfConfiguration.RESOURCE_BASES_POST_OVERLAY);
if (postOverlayResources instanceof ResourceCollection)
props.put("base.dirs", toCSV(((ResourceCollection)postOverlayResources).getResources()));
else
props.put("base.dirs", postOverlayResources.toString());
//web-inf classes
if (webApp.getClasses() != null)
{
props.put("classes.dir",webApp.getClasses().getAbsolutePath());
}
if (useTestScope && webApp.getTestClasses() != null)
{
props.put("testClasses.dir", webApp.getTestClasses().getAbsolutePath());
}
//web-inf lib
List<File> deps = webApp.getWebInfLib();
StringBuffer strbuff = new StringBuffer();
for (int i=0; i<deps.size(); i++)
{
File d = deps.get(i);
strbuff.append(d.getAbsolutePath());
if (i < deps.size()-1)
strbuff.append(",");
}
props.put("lib.jars", strbuff.toString());
//any war files
List<Artifact> warArtifacts = getWarArtifacts();
for (int i=0; i<warArtifacts.size(); i++)
{
strbuff.setLength(0);
Artifact a = warArtifacts.get(i);
strbuff.append(a.getGroupId()+",");
strbuff.append(a.getArtifactId()+",");
strbuff.append(a.getFile().getAbsolutePath());
props.put("maven.war.artifact."+i, strbuff.toString());
}
//any overlay configuration
WarPluginInfo warPlugin = new WarPluginInfo(project);
//add in the war plugins default includes and excludes
props.put("maven.war.includes", toCSV(warPlugin.getDependentMavenWarIncludes()));
props.put("maven.war.excludes", toCSV(warPlugin.getDependentMavenWarExcludes()));
List<OverlayConfig> configs = warPlugin.getMavenWarOverlayConfigs();
int i=0;
for (OverlayConfig c:configs)
{
props.put("maven.war.overlay."+(i++), c.toString());
}
try (OutputStream out = new BufferedOutputStream(new FileOutputStream(propsFile)))
{
props.store(out, "properties for forked webapp");
}
WebAppPropertyConverter.toProperties(webApp, propsFile, contextXml);
return propsFile;
}
catch (Exception e)
@ -571,29 +469,6 @@ public class JettyRunForkedMojo extends JettyRunMojo
/**
* @return
* @throws MalformedURLException
* @throws IOException
*/
private List<Artifact> getWarArtifacts()
throws MalformedURLException, IOException
{
List<Artifact> warArtifacts = new ArrayList<Artifact>();
for ( Iterator<Artifact> iter = project.getArtifacts().iterator(); iter.hasNext(); )
{
Artifact artifact = iter.next();
if (artifact.getType().equals("war"))
warArtifacts.add(artifact);
}
return warArtifacts;
}
public boolean isPluginArtifact(Artifact artifact)
{
if (pluginArtifacts == null || pluginArtifacts.isEmpty())
@ -747,32 +622,4 @@ public class JettyRunForkedMojo extends JettyRunMojo
thread.setDaemon(true);
thread.start();
}
private String toCSV (List<String> strings)
{
if (strings == null)
return "";
StringBuffer strbuff = new StringBuffer();
Iterator<String> itor = strings.iterator();
while (itor.hasNext())
{
strbuff.append(itor.next());
if (itor.hasNext())
strbuff.append(",");
}
return strbuff.toString();
}
private String toCSV (Resource[] resources)
{
StringBuffer rb = new StringBuffer();
for (Resource r:resources)
{
if (rb.length() > 0) rb.append(",");
rb.append(r.toString());
}
return rb.toString();
}
}

View File

@ -18,21 +18,9 @@
package org.eclipse.jetty.maven.plugin;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.project.MavenProject;
import org.codehaus.plexus.util.StringUtils;
import org.eclipse.jetty.util.PathWatcher;
import org.eclipse.jetty.util.PathWatcher.PathWatchEvent;
import org.eclipse.jetty.util.resource.Resource;
import org.eclipse.jetty.webapp.WebAppContext;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collection;
@ -41,8 +29,20 @@ import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.stream.Collector;
import java.util.stream.Collectors;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.project.MavenProject;
import org.codehaus.plexus.util.StringUtils;
import org.eclipse.jetty.util.PathWatcher;
import org.eclipse.jetty.util.PathWatcher.PathWatchEvent;
import org.eclipse.jetty.util.resource.JarResource;
import org.eclipse.jetty.util.resource.Resource;
import org.eclipse.jetty.util.resource.ResourceCollection;
import org.eclipse.jetty.webapp.WebAppContext;
/**
* This goal is used in-situ on a Maven project without first requiring that the project
@ -163,6 +163,10 @@ public class JettyRunMojo extends AbstractJettyMojo
*/
protected List<Artifact> warArtifacts;
protected Resource originalBaseResource;
@Parameter(defaultValue = "${reactorProjects}", readonly = true, required = true)
private List<MavenProject> reactorProjects;
@ -276,8 +280,18 @@ public class JettyRunMojo extends AbstractJettyMojo
if (webApp.getWar() == null)
webApp.setWar(webAppSourceDirectoryResource.toString());
//The first time we run, remember the original base dir
if (originalBaseResource == null)
{
if (webApp.getBaseResource() == null)
webApp.setBaseResource(webAppSourceDirectoryResource);
originalBaseResource = webAppSourceDirectoryResource;
else
originalBaseResource = webApp.getBaseResource();
}
//On every subsequent re-run set it back to the original base dir before
//we might have applied any war overlays onto it
webApp.setBaseResource(originalBaseResource);
if (classesDirectory != null)
webApp.setClasses (classesDirectory);
@ -286,49 +300,6 @@ public class JettyRunMojo extends AbstractJettyMojo
webApp.setWebInfLib(getDependencyFiles());
//get copy of a list of war artifacts
Set<Artifact> matchedWarArtifacts = new HashSet<Artifact>();
//process any overlays and the war type artifacts
List<Overlay> overlays = new ArrayList<>();
for (OverlayConfig config:warPluginInfo.getMavenWarOverlayConfigs())
{
//overlays can be individually skipped
if (config.isSkip())
continue;
//an empty overlay refers to the current project - important for ordering
if (config.isCurrentProject())
{
Overlay overlay = new Overlay(config, null);
overlays.add(overlay);
continue;
}
//if a war matches an overlay config
Artifact a = getArtifactForOverlay(config, getWarArtifacts());
if (a != null)
{
matchedWarArtifacts.add(a);
SelectiveJarResource r = new SelectiveJarResource(new URL("jar:"+Resource.toURL(a.getFile()).toString()+"!/"));
r.setIncludes(config.getIncludes());
r.setExcludes(config.getExcludes());
Overlay overlay = new Overlay(config, r);
overlays.add(overlay);
}
}
//iterate over the left over war artifacts and unpack them (without include/exclude processing) as necessary
for (Artifact a: getWarArtifacts())
{
if (!matchedWarArtifacts.contains(a))
{
Overlay overlay = new Overlay(null, Resource.newResource(new URL("jar:"+Resource.toURL(a.getFile()).toString()+"!/")));
overlays.add(overlay);
}
}
webApp.setOverlays(overlays);
//if we have not already set web.xml location, need to set one up
if (webApp.getDescriptor() == null)
@ -363,6 +334,11 @@ public class JettyRunMojo extends AbstractJettyMojo
}
}
}
//process any overlays and the war type artifacts
List<Overlay> overlays = getOverlays();
unpackOverlays(overlays); //this sets up the base resource collection
getLog().info( "web.xml file = "+webApp.getDescriptor());
getLog().info("Webapp directory = " + webAppSourceDirectory.getCanonicalPath());
}
@ -628,7 +604,125 @@ public class JettyRunMojo extends AbstractJettyMojo
private List<Overlay> getOverlays()
throws Exception
{
//get copy of a list of war artifacts
Set<Artifact> matchedWarArtifacts = new HashSet<Artifact>();
List<Overlay> overlays = new ArrayList<Overlay>();
for (OverlayConfig config:warPluginInfo.getMavenWarOverlayConfigs())
{
//overlays can be individually skipped
if (config.isSkip())
continue;
//an empty overlay refers to the current project - important for ordering
if (config.isCurrentProject())
{
Overlay overlay = new Overlay(config, null);
overlays.add(overlay);
continue;
}
//if a war matches an overlay config
Artifact a = getArtifactForOverlay(config, getWarArtifacts());
if (a != null)
{
matchedWarArtifacts.add(a);
SelectiveJarResource r = new SelectiveJarResource(new URL("jar:"+Resource.toURL(a.getFile()).toString()+"!/"));
r.setIncludes(config.getIncludes());
r.setExcludes(config.getExcludes());
Overlay overlay = new Overlay(config, r);
overlays.add(overlay);
}
}
//iterate over the left over war artifacts and unpack them (without include/exclude processing) as necessary
for (Artifact a: getWarArtifacts())
{
if (!matchedWarArtifacts.contains(a))
{
Overlay overlay = new Overlay(null, Resource.newResource(new URL("jar:"+Resource.toURL(a.getFile()).toString()+"!/")));
overlays.add(overlay);
}
}
return overlays;
}
public void unpackOverlays (List<Overlay> overlays)
throws Exception
{
if (overlays == null || overlays.isEmpty())
return;
List<Resource> resourceBaseCollection = new ArrayList<Resource>();
for (Overlay o:overlays)
{
//can refer to the current project in list of overlays for ordering purposes
if (o.getConfig() != null && o.getConfig().isCurrentProject() && webApp.getBaseResource().exists())
{
resourceBaseCollection.add(webApp.getBaseResource());
continue;
}
Resource unpacked = unpackOverlay(o);
//_unpackedOverlayResources.add(unpacked); //remember the unpacked overlays for later so we can delete the tmp files
resourceBaseCollection.add(unpacked); //add in the selectively unpacked overlay in the correct order to the webapps resource base
}
if (!resourceBaseCollection.contains(webApp.getBaseResource()) && webApp.getBaseResource().exists())
{
if (webApp.getBaseAppFirst())
{
resourceBaseCollection.add(0, webApp.getBaseResource());
}
else
{
resourceBaseCollection.add(webApp.getBaseResource());
}
}
webApp.setBaseResource(new ResourceCollection(resourceBaseCollection.toArray(new Resource[resourceBaseCollection.size()])));
}
public Resource unpackOverlay (Overlay overlay)
throws IOException
{
if (overlay.getResource() == null)
return null; //nothing to unpack
//Get the name of the overlayed war and unpack it to a dir of the
//same name in the temporary directory
String name = overlay.getResource().getName();
if (name.endsWith("!/"))
name = name.substring(0,name.length()-2);
int i = name.lastIndexOf('/');
if (i>0)
name = name.substring(i+1,name.length());
name = name.replace('.', '_');
//name = name+(++COUNTER); //add some digits to ensure uniqueness
File overlaysDir = new File (project.getBuild().getDirectory(), "jetty_overlays");
File dir = new File(overlaysDir, name);
//if specified targetPath, unpack to that subdir instead
File unpackDir = dir;
if (overlay.getConfig() != null && overlay.getConfig().getTargetPath() != null)
unpackDir = new File (dir, overlay.getConfig().getTargetPath());
//only unpack if the overlay is newer
if (!unpackDir.exists() || (overlay.getResource().lastModified() > unpackDir.lastModified()))
{
boolean made=unpackDir.mkdirs();
overlay.getResource().copyTo(unpackDir);
}
//use top level of unpacked content
return Resource.newResource(dir.getCanonicalPath());
}
/**
* @return

View File

@ -31,9 +31,7 @@ import java.util.Map;
import java.util.Set;
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;
@ -47,12 +45,8 @@ 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.Configuration;
import org.eclipse.jetty.webapp.FragmentConfiguration;
import org.eclipse.jetty.webapp.JettyWebXmlConfiguration;
import org.eclipse.jetty.webapp.MetaInfConfiguration;
import org.eclipse.jetty.webapp.WebAppContext;
import org.eclipse.jetty.webapp.WebInfConfiguration;
import org.eclipse.jetty.webapp.WebXmlConfiguration;
/**
* JettyWebAppContext
@ -74,6 +68,13 @@ public class JettyWebAppContext extends WebAppContext
private static final String WEB_INF_LIB_PREFIX = "/WEB-INF/lib";
public static final String[] MINIMUM_CONFIGURATION_CLASSES = {
"org.eclipse.jetty.maven.plugin.MavenWebInfConfiguration",
"org.eclipse.jetty.webapp.WebXmlConfiguration",
"org.eclipse.jetty.webapp.MetaInfConfiguration",
"org.eclipse.jetty.webapp.FragmentConfiguration",
"org.eclipse.jetty.webapp.JettyWebXmlConfiguration"
};
public static final String[] DEFAULT_CONFIGURATION_CLASSES = {
"org.eclipse.jetty.maven.plugin.MavenWebInfConfiguration",
"org.eclipse.jetty.webapp.WebXmlConfiguration",
@ -86,7 +87,7 @@ public class JettyWebAppContext extends WebAppContext
};
private final String[] QUICKSTART_CONFIGURATION_CLASSES = {
public static final String[] QUICKSTART_CONFIGURATION_CLASSES = {
"org.eclipse.jetty.maven.plugin.MavenQuickStartConfiguration",
"org.eclipse.jetty.plus.webapp.EnvConfiguration",
"org.eclipse.jetty.plus.webapp.PlusConfiguration",
@ -375,19 +376,12 @@ public class JettyWebAppContext extends WebAppContext
@Override
public void doStart () throws Exception
{
//choose if this will be a quickstart or normal start
if (!isGenerateQuickStart() && getQuickStartWebDescriptor() != null)
{
setConfigurationClasses(QUICKSTART_CONFIGURATION_CLASSES);
}
else
{
if (isGenerateQuickStart())
{
_preconfigProcessor = new PreconfigureDescriptorProcessor();
getMetaData().addDescriptorProcessor(_preconfigProcessor);
}
}
//Set up the pattern that tells us where the jars are that need scanning
@ -445,8 +439,6 @@ public class JettyWebAppContext extends WebAppContext
{
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());
}
}

View File

@ -40,22 +40,8 @@ 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 preConfigure(WebAppContext context) throws Exception
{
@ -65,7 +51,7 @@ public class MavenQuickStartConfiguration extends QuickStartConfiguration
//look for quickstart-web.xml in WEB-INF of webapp
Resource quickStartWebXml = getQuickStartWebXml(context);
Resource quickStartWebXml = ((JettyWebAppContext)context).getQuickStartWebDescriptor();
LOG.debug("quickStartWebXml={}",quickStartWebXml);
context.getMetaData().setWebXml(quickStartWebXml);

View File

@ -19,17 +19,14 @@
package org.eclipse.jetty.maven.plugin;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
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;
import org.eclipse.jetty.webapp.WebInfConfiguration;
@ -46,11 +43,6 @@ public class MavenWebInfConfiguration extends WebInfConfiguration
{
private static final Logger LOG = Log.getLogger(MavenWebInfConfiguration.class);
public static final String RESOURCE_BASES_POST_OVERLAY = "org.eclipse.jetty.resource.postOverlay";
protected static int COUNTER = 0;
protected Resource _originalResourceBase;
protected List<Resource> _unpackedOverlayResources;
/**
* @see org.eclipse.jetty.webapp.WebInfConfiguration#configure(org.eclipse.jetty.webapp.WebAppContext)
*/
@ -84,91 +76,6 @@ public class MavenWebInfConfiguration extends WebInfConfiguration
context.setServerClasses( newServerClasses );
}
/**
* @see org.eclipse.jetty.webapp.AbstractConfiguration#postConfigure(org.eclipse.jetty.webapp.WebAppContext)
*/
public void postConfigure(WebAppContext context) throws Exception
{
super.postConfigure(context);
}
/**
* @see org.eclipse.jetty.webapp.WebInfConfiguration#deconfigure(org.eclipse.jetty.webapp.WebAppContext)
*/
public void deconfigure(WebAppContext context) throws Exception
{
super.deconfigure(context);
//restore whatever the base resource was before we might have included overlaid wars
context.setBaseResource(_originalResourceBase);
//undo the setting of the overlayed resources
context.removeAttribute(RESOURCE_BASES_POST_OVERLAY);
}
/**
* @see org.eclipse.jetty.webapp.WebInfConfiguration#unpack(org.eclipse.jetty.webapp.WebAppContext)
*/
@Override
public void unpack(WebAppContext context) throws IOException
{
//Unpack and find base resource as normal
super.unpack(context);
//Get the base resource for the "virtual" webapp
_originalResourceBase = context.getBaseResource();
JettyWebAppContext jwac = (JettyWebAppContext)context;
//determine sequencing of overlays
_unpackedOverlayResources = new ArrayList<Resource>();
if (jwac.getOverlays() != null && !jwac.getOverlays().isEmpty())
{
List<Resource> resourceBaseCollection = new ArrayList<Resource>();
for (Overlay o:jwac.getOverlays())
{
//can refer to the current project in list of overlays for ordering purposes
if (o.getConfig() != null && o.getConfig().isCurrentProject() && _originalResourceBase.exists())
{
resourceBaseCollection.add(_originalResourceBase);
LOG.debug("Adding virtual project to resource base list");
continue;
}
Resource unpacked = unpackOverlay(jwac,o);
_unpackedOverlayResources.add(unpacked); //remember the unpacked overlays for later so we can delete the tmp files
resourceBaseCollection.add(unpacked); //add in the selectively unpacked overlay in the correct order to the webapps resource base
LOG.debug("Adding "+unpacked+" to resource base list");
}
if (!resourceBaseCollection.contains(_originalResourceBase) && _originalResourceBase.exists())
{
if (jwac.getBaseAppFirst())
{
LOG.debug("Adding virtual project first in resource base list");
resourceBaseCollection.add(0, _originalResourceBase);
}
else
{
LOG.debug("Adding virtual project last in resource base list");
resourceBaseCollection.add(_originalResourceBase);
}
}
jwac.setBaseResource(new ResourceCollection(resourceBaseCollection.toArray(new Resource[resourceBaseCollection.size()])));
}
jwac.setAttribute(RESOURCE_BASES_POST_OVERLAY, jwac.getBaseResource());
}
/**
* Get the jars to examine from the files from which we have
* synthesized the classpath. Note that the classpath is not
@ -249,38 +156,4 @@ public class MavenWebInfConfiguration extends WebInfConfiguration
protected Resource unpackOverlay (WebAppContext context, Overlay overlay)
throws IOException
{
LOG.debug("Unpacking overlay: " + overlay);
if (overlay.getResource() == null)
return null; //nothing to unpack
//Get the name of the overlayed war and unpack it to a dir of the
//same name in the temporary directory
String name = overlay.getResource().getName();
if (name.endsWith("!/"))
name = name.substring(0,name.length()-2);
int i = name.lastIndexOf('/');
if (i>0)
name = name.substring(i+1,name.length());
name = name.replace('.', '_');
name = name+(++COUNTER); //add some digits to ensure uniqueness
File dir = new File(context.getTempDirectory(), name);
//if specified targetPath, unpack to that subdir instead
File unpackDir = dir;
if (overlay.getConfig() != null && overlay.getConfig().getTargetPath() != null)
unpackDir = new File (dir, overlay.getConfig().getTargetPath());
overlay.getResource().copyTo(unpackDir);
//use top level of unpacked content
Resource unpackedOverlay = Resource.newResource(dir.getCanonicalPath());
LOG.debug("Unpacked overlay: "+overlay+" to "+unpackedOverlay);
return unpackedOverlay;
}
}

View File

@ -19,21 +19,8 @@
package org.eclipse.jetty.maven.plugin;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.net.URL;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import java.util.Set;
import java.util.TreeMap;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.Server;
@ -43,8 +30,6 @@ 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;
import org.eclipse.jetty.util.resource.ResourceCollection;
import org.eclipse.jetty.xml.XmlConfiguration;
@ -55,8 +40,7 @@ public class Starter
{
private static final Logger LOG = Log.getLogger(Starter.class);
private List<File> jettyXmls; // list of jetty.xml config files to apply - Mandatory
private File contextXml; //name of context xml file to configure the webapp - Mandatory
private List<File> jettyXmls; // list of jetty.xml config files to apply
private Server server;
private JettyWebAppContext webApp;
@ -64,57 +48,10 @@ public class Starter
private int stopPort=0;
private String stopKey=null;
private Properties props;
private File propsFile;
private String token;
/**
* Artifact
*
* A mock maven Artifact class as the maven jars are not put onto the classpath for the
* execution of this class.
*
*/
public class Artifact
{
public String gid;
public String aid;
public String path;
public Resource resource;
public Artifact (String csv)
{
if (csv != null && !"".equals(csv))
{
String[] atoms = StringUtil.csvSplit(csv);
if (atoms.length >= 3)
{
gid = atoms[0].trim();
aid = atoms[1].trim();
path = atoms[2].trim();
}
}
}
public Artifact (String gid, String aid, String path)
{
this.gid = gid;
this.aid = aid;
this.path = path;
}
public boolean equals(Object o)
{
if (!(o instanceof Artifact))
return false;
Artifact ao = (Artifact)o;
return (((gid == null && ao.gid == null) || (gid != null && gid.equals(ao.gid)))
&& ((aid == null && ao.aid == null) || (aid != null && aid.equals(ao.aid))));
}
}
public void configureJetty () throws Exception
{
@ -146,19 +83,6 @@ public class Starter
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
//the pom to override the context xml file, but as the other mojos all
//configure a WebAppContext in the pom (the <webApp> element), it is
//already configured by the time the context xml file is applied.
if (contextXml != null)
{
XmlConfiguration xmlConfiguration = new XmlConfiguration(Resource.toURL(contextXml));
xmlConfiguration.getIdMap().put("Server",server);
xmlConfiguration.configure(webApp);
}
ServerSupport.addWebApplication(server, webApp);
if(stopPort>0 && stopKey!=null)
@ -173,147 +97,11 @@ public class Starter
public void configureWebApp ()
throws Exception
{
if (props == null)
if (propsFile == null)
return;
//apply a properties file that defines the things that we configure in the jetty:run plugin:
// - the context path
String str = props.getProperty("context.path");
if (str != null)
webApp.setContextPath(str);
// - web.xml
str = props.getProperty("web.xml");
if (str != null)
webApp.setDescriptor(str);
str = props.getProperty("quickstart.web.xml");
if (str != null)
webApp.setQuickStartWebDescriptor(Resource.newResource(new File(str)));
// - the tmp directory
str = props.getProperty("tmp.dir");
if (str != null)
webApp.setTempDirectory(new File(str.trim()));
str = props.getProperty("tmp.dir.persist");
if (str != null)
webApp.setPersistTempDirectory(Boolean.valueOf(str));
//Get the calculated base dirs which includes the overlays
str = props.getProperty("base.dirs");
if (str != null && !"".equals(str.trim()))
{
ResourceCollection bases = new ResourceCollection(StringUtil.csvSplit(str));
webApp.setWar(null);
webApp.setBaseResource(bases);
}
//Get the original base dirs without the overlays
str = props.getProperty("base.dirs.orig");
if (str != null && !"".equals(str.trim()))
{
ResourceCollection bases = new ResourceCollection(StringUtil.csvSplit(str));
webApp.setAttribute ("org.eclipse.jetty.resources.originalBases", bases);
}
//For overlays
str = props.getProperty("maven.war.includes");
List<String> defaultWarIncludes = fromCSV(str);
str = props.getProperty("maven.war.excludes");
List<String> defaultWarExcludes = fromCSV(str);
//List of war artifacts
List<Artifact> wars = new ArrayList<Artifact>();
//List of OverlayConfigs
TreeMap<String, OverlayConfig> orderedConfigs = new TreeMap<String, OverlayConfig>();
Enumeration<String> pnames = (Enumeration<String>)props.propertyNames();
while (pnames.hasMoreElements())
{
String n = pnames.nextElement();
if (n.startsWith("maven.war.artifact"))
{
Artifact a = new Artifact((String)props.get(n));
a.resource = Resource.newResource("jar:"+Resource.toURL(new File(a.path)).toString()+"!/");
wars.add(a);
}
else if (n.startsWith("maven.war.overlay"))
{
OverlayConfig c = new OverlayConfig ((String)props.get(n), defaultWarIncludes, defaultWarExcludes);
orderedConfigs.put(n,c);
}
}
Set<Artifact> matchedWars = new HashSet<Artifact>();
//process any overlays and the war type artifacts
List<Overlay> overlays = new ArrayList<>();
for (OverlayConfig config:orderedConfigs.values())
{
//overlays can be individually skipped
if (config.isSkip())
continue;
//an empty overlay refers to the current project - important for ordering
if (config.isCurrentProject())
{
Overlay overlay = new Overlay(config, null);
overlays.add(overlay);
continue;
}
//if a war matches an overlay config
Artifact a = getArtifactForOverlayConfig(config, wars);
if (a != null)
{
matchedWars.add(a);
SelectiveJarResource r = new SelectiveJarResource(new URL("jar:"+Resource.toURL(new File(a.path)).toString()+"!/"));
r.setIncludes(config.getIncludes());
r.setExcludes(config.getExcludes());
Overlay overlay = new Overlay(config, r);
overlays.add(overlay);
}
}
//iterate over the left over war artifacts and unpack them (without include/exclude processing) as necessary
for (Artifact a: wars)
{
if (!matchedWars.contains(a))
{
Overlay overlay = new Overlay(null, a.resource);
overlays.add(overlay);
}
}
webApp.setOverlays(overlays);
// - the equivalent of web-inf classes
str = props.getProperty("classes.dir");
if (str != null && !"".equals(str.trim()))
{
webApp.setClasses(new File(str));
}
str = props.getProperty("testClasses.dir");
if (str != null && !"".equals(str.trim()))
{
webApp.setTestClasses(new File(str));
}
// - the equivalent of web-inf lib
str = props.getProperty("lib.jars");
if (str != null && !"".equals(str.trim()))
{
List<File> jars = new ArrayList<File>();
String[] names = StringUtil.csvSplit(str);
for (int j=0; names != null && j < names.length; j++)
jars.add(new File(names[j].trim()));
webApp.setWebInfLib(jars);
}
//apply a properties file that defines the things that we configure in the jetty:run plugin
WebAppPropertyConverter.fromProperties(webApp, propsFile, server);
}
public void getConfiguration (String[] args)
@ -340,21 +128,10 @@ public class Starter
}
}
//--context-xml
if ("--context-xml".equals(args[i]))
{
contextXml = new File(args[++i]);
}
//--props
if ("--props".equals(args[i]))
{
File f = new File(args[++i].trim());
props = new Properties();
try (InputStream in = new FileInputStream(f))
{
props.load(in);
}
propsFile = new File(args[++i].trim());
}
//--token
@ -420,25 +197,6 @@ public class Starter
handlers.setHandlers(children);
}
protected Artifact getArtifactForOverlayConfig (OverlayConfig c, List<Artifact> wars)
{
if (wars == null || wars.isEmpty() || c == null)
return null;
Artifact war = null;
Iterator<Artifact> itor = wars.iterator();
while(itor.hasNext() && war == null)
{
Artifact a = itor.next();
if (c.matchesArtifact(a.gid, a.aid, null))
war = a;
}
return war;
}
/**
* @param csv
* @return
@ -456,6 +214,9 @@ public class Starter
return list;
}
/**
* @param args
*/
public static final void main(String[] args)
{
if (args == null)

View File

@ -0,0 +1,276 @@
//
// ========================================================================
// Copyright (c) 1995-2017 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.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.resource.Resource;
import org.eclipse.jetty.util.resource.ResourceCollection;
import org.eclipse.jetty.xml.XmlConfiguration;
/**
* WebAppPropertyConverter
*
* Converts a webapp's configuration to a properties file, and
* vice versa.
*/
public class WebAppPropertyConverter
{
/**
* Convert a webapp to properties stored in a file.
*
* @param webApp the webapp to convert
* @param propsFile the file to put the properties into
* @param contextXml the optional context xml file related to the webApp
* @throws Exception
*/
public static void toProperties(JettyWebAppContext webApp, File propsFile, String contextXml)
throws Exception
{
if (webApp == null)
throw new IllegalArgumentException("No webapp");
if (propsFile == null)
throw new IllegalArgumentException("No properties file");
//work out the configuration based on what is configured in the pom
if (propsFile.exists())
propsFile.delete();
propsFile.createNewFile();
Properties props = new Properties();
//web.xml
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 (webApp.getContextPath() != null)
{
props.put("context.path", webApp.getContextPath());
}
//tmp dir
props.put("tmp.dir", webApp.getTempDirectory().getAbsolutePath());
//props.put("tmp.dir.persist", Boolean.toString(originalPersistTemp));
props.put("tmp.dir.persist", Boolean.toString(webApp.isPersistTempDirectory()));
//send over the calculated resource bases that includes unpacked overlays
Resource baseResource = webApp.getBaseResource();
if (baseResource instanceof ResourceCollection)
props.put("base.dirs", toCSV(((ResourceCollection)webApp.getBaseResource()).getResources()));
else
props.put("base.dirs", webApp.getBaseResource().toString());
//web-inf classes
if (webApp.getClasses() != null)
{
props.put("classes.dir",webApp.getClasses().getAbsolutePath());
}
if (webApp.getTestClasses() != null)
{
props.put("testClasses.dir", webApp.getTestClasses().getAbsolutePath());
}
//web-inf lib
List<File> deps = webApp.getWebInfLib();
StringBuffer strbuff = new StringBuffer();
if (deps != null)
{
for (int i=0; i<deps.size(); i++)
{
File d = deps.get(i);
strbuff.append(d.getAbsolutePath());
if (i < deps.size()-1)
strbuff.append(",");
}
}
props.put("lib.jars", strbuff.toString());
//context xml to apply
if (contextXml != null)
props.put("context.xml", contextXml);
try (OutputStream out = new BufferedOutputStream(new FileOutputStream(propsFile)))
{
props.store(out, "properties for forked webapp");
}
}
/**
* Configure a webapp from a properties file.
*
* @param webApp the webapp to configure
* @param resource the properties file to apply
* @param server the Server instance to use
* @throws Exception
*/
public static void fromProperties (JettyWebAppContext webApp, String resource, Server server)
throws Exception
{
if (resource == null)
throw new IllegalStateException("No resource");
fromProperties(webApp, Resource.newResource(resource).getFile(), server);
}
/**
* Configure a webapp from a properties file
* @param webApp the webapp to configure
* @param propsFile the properties to apply
* @param server the Server instance
* @throws Exception
*/
public static void fromProperties (JettyWebAppContext webApp, File propsFile, Server server)
throws Exception
{
if (webApp == null)
throw new IllegalArgumentException("No webapp");
if (propsFile == null)
throw new IllegalArgumentException("No properties file");
if (!propsFile.exists())
throw new IllegalArgumentException (propsFile.getCanonicalPath()+" does not exist");
Properties props = new Properties();
try (InputStream in = new FileInputStream(propsFile))
{
props.load(in);
}
String str = props.getProperty("context.path");
if (!StringUtil.isBlank(str))
webApp.setContextPath(str);
// - web.xml
str = props.getProperty("web.xml");
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.setConfigurationClasses(JettyWebAppContext.QUICKSTART_CONFIGURATION_CLASSES);
}
// - the tmp directory
str = props.getProperty("tmp.dir");
if (!StringUtil.isBlank(str))
webApp.setTempDirectory(new File(str.trim()));
str = props.getProperty("tmp.dir.persist");
if (!StringUtil.isBlank(str))
webApp.setPersistTempDirectory(Boolean.valueOf(str));
//Get the calculated base dirs which includes the overlays
str = props.getProperty("base.dirs");
if (!StringUtil.isBlank(str))
{
ResourceCollection bases = new ResourceCollection(StringUtil.csvSplit(str));
webApp.setWar(null);
webApp.setBaseResource(bases);
}
// - the equivalent of web-inf classes
str = props.getProperty("classes.dir");
if (!StringUtil.isBlank(str))
{
webApp.setClasses(new File(str));
}
str = props.getProperty("testClasses.dir");
if (!StringUtil.isBlank(str))
{
webApp.setTestClasses(new File(str));
}
// - the equivalent of web-inf lib
str = props.getProperty("lib.jars");
if (!StringUtil.isBlank(str))
{
List<File> jars = new ArrayList<File>();
String[] names = StringUtil.csvSplit(str);
for (int j=0; names != null && j < names.length; j++)
jars.add(new File(names[j].trim()));
webApp.setWebInfLib(jars);
}
//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
//the pom to override the context xml file, but as the other mojos all
//configure a WebAppContext in the pom (the <webApp> element), it is
//already configured by the time the context xml file is applied.
str = (String)props.getProperty("context.xml");
if (!StringUtil.isBlank(str))
{
XmlConfiguration xmlConfiguration = new XmlConfiguration(Resource.newResource(str).getURI().toURL());
xmlConfiguration.getIdMap().put("Server",server);
xmlConfiguration.configure(webApp);
}
}
/**
* Convert an array of Resources to csv file names
*
* @param resources the resources to convert
*
* @return csv string of resource filenames
*/
private static String toCSV (Resource[] resources)
{
StringBuffer rb = new StringBuffer();
for (Resource r:resources)
{
if (rb.length() > 0) rb.append(",");
rb.append(r.toString());
}
return rb.toString();
}
}

View File

@ -0,0 +1,10 @@
[description]
Enables an unassembled maven webapp to run in a jetty distro
[depends]
server
webapp
annotations
[lib]
lib/maven/**.jar

View File

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
<Configure id="wac" class="org.eclipse.jetty.maven.plugin.JettyWebAppContext">
<Call class="org.eclipse.jetty.webapp.Configuration$ClassList" name="setServerDefault">
<Arg><Ref id="Server" /></Arg>
<Call name="replace">
<Arg>org.eclipse.jetty.webapp.WebInfConfiguration</Arg>
<Arg>org.eclipse.jetty.maven.plugin.MavenWebInfConfiguration</Arg>
</Call>
</Call>
<Call class="org.eclipse.jetty.maven.plugin.WebAppPropertyConverter" name="fromProperties">
<Arg><Ref id="wac"/></Arg>
<Arg><Property name="jetty.base" default="."/>/etc/maven.props</Arg>
<Arg><Ref id="Server"/></Arg>
</Call>
</Configure>

View File

@ -197,5 +197,24 @@ public interface Configuration
throw new IllegalArgumentException("beforeClass '"+beforeClass+"' not found in "+this);
}
public void replace(@Name("replaceClass") String replaceClass, @Name("configClass") String configClass)
{
if (replaceClass!=null && configClass!=null)
{
ListIterator<String> iter = listIterator();
while (iter.hasNext())
{
String cc=iter.next();
if (replaceClass.equals(cc))
{
iter.set(configClass);
return;
}
}
}
throw new IllegalArgumentException("replaceClass '"+replaceClass+"' not found in "+this);
}
}
}

View File

@ -1028,8 +1028,9 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL
if (_configurations.size()>0)
return;
if (_configurationClasses.size()==0)
if (_configurationClasses.size()==0) {
_configurationClasses.addAll(Configuration.ClassList.serverDefault(getServer()));
}
for (String configClass : _configurationClasses)
_configurations.add((Configuration)Loader.loadClass(configClass).newInstance());
}