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

View File

@ -18,8 +18,11 @@
package org.eclipse.jetty.maven.plugin; package org.eclipse.jetty.maven.plugin;
import java.io.BufferedOutputStream;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.net.MalformedURLException; 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.PathWatcher;
import org.eclipse.jetty.util.StringUtil; import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.resource.Resource; import org.eclipse.jetty.util.resource.Resource;
import org.eclipse.jetty.util.resource.ResourceCollection;
import org.eclipse.jetty.xml.XmlConfiguration; import org.eclipse.jetty.xml.XmlConfiguration;
/** /**
@ -538,8 +542,15 @@ public abstract class AbstractJettyMojo extends AbstractMojo
//context xml file can OVERRIDE those settings //context xml file can OVERRIDE those settings
if (contextXml != null) if (contextXml != null)
{ {
File file = FileUtils.getFile(contextXml); Path path = Paths.get(contextXml);
XmlConfiguration xmlConfiguration = new XmlConfiguration(Resource.toURL(file)); 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); getLog().info("Applying context xml file "+contextXml);
xmlConfiguration.configure(webApp); 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

@ -143,9 +143,7 @@ public class JettyRunForkedMojo extends JettyRunMojo
*/ */
private Random random; private Random random;
private Resource originalBaseResource;
private boolean originalPersistTemp; private boolean originalPersistTemp;
@ -256,9 +254,6 @@ public class JettyRunForkedMojo extends JettyRunMojo
//ensure config of the webapp based on settings in plugin //ensure config of the webapp based on settings in plugin
configureWebApplication(); configureWebApplication();
//copy the base resource as configured by the plugin
originalBaseResource = webApp.getBaseResource();
//get the original persistance setting //get the original persistance setting
originalPersistTemp = webApp.isPersistTempDirectory(); originalPersistTemp = webApp.isPersistTempDirectory();
@ -295,13 +290,15 @@ public class JettyRunForkedMojo extends JettyRunMojo
//leave everything unpacked for the forked process to use //leave everything unpacked for the forked process to use
webApp.setPersistTempDirectory(true); webApp.setPersistTempDirectory(true);
webApp.start(); //just enough to generate the quickstart webApp.start(); //just enough to generate the quickstart
//save config of the webapp BEFORE we stop //save config of the webapp BEFORE we stop
File props = prepareConfiguration(); File props = prepareConfiguration();
webApp.stop(); webApp.stop();
if (tpool != null) if (tpool != null)
tpool.stop(); tpool.stop();
@ -461,106 +458,7 @@ public class JettyRunForkedMojo extends JettyRunMojo
{ {
//work out the configuration based on what is configured in the pom //work out the configuration based on what is configured in the pom
File propsFile = new File (target, "fork.props"); File propsFile = new File (target, "fork.props");
if (propsFile.exists()) WebAppPropertyConverter.toProperties(webApp, propsFile, contextXml);
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");
}
return propsFile; return propsFile;
} }
catch (Exception e) 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) public boolean isPluginArtifact(Artifact artifact)
{ {
if (pluginArtifacts == null || pluginArtifacts.isEmpty()) if (pluginArtifacts == null || pluginArtifacts.isEmpty())
@ -747,32 +622,4 @@ public class JettyRunForkedMojo extends JettyRunMojo
thread.setDaemon(true); thread.setDaemon(true);
thread.start(); 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; 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.File;
import java.io.IOException; import java.io.IOException;
import java.net.URL; import java.net.URL;
import java.nio.file.Path;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
@ -41,8 +29,20 @@ import java.util.HashSet;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Set; 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 * This goal is used in-situ on a Maven project without first requiring that the project
@ -162,6 +162,10 @@ public class JettyRunMojo extends AbstractJettyMojo
* List of deps that are wars * List of deps that are wars
*/ */
protected List<Artifact> warArtifacts; protected List<Artifact> warArtifacts;
protected Resource originalBaseResource;
@Parameter(defaultValue = "${reactorProjects}", readonly = true, required = true) @Parameter(defaultValue = "${reactorProjects}", readonly = true, required = true)
private List<MavenProject> reactorProjects; private List<MavenProject> reactorProjects;
@ -275,9 +279,19 @@ public class JettyRunMojo extends AbstractJettyMojo
Resource webAppSourceDirectoryResource = Resource.newResource(webAppSourceDirectory.getCanonicalPath()); Resource webAppSourceDirectoryResource = Resource.newResource(webAppSourceDirectory.getCanonicalPath());
if (webApp.getWar() == null) if (webApp.getWar() == null)
webApp.setWar(webAppSourceDirectoryResource.toString()); webApp.setWar(webAppSourceDirectoryResource.toString());
if (webApp.getBaseResource() == null) //The first time we run, remember the original base dir
webApp.setBaseResource(webAppSourceDirectoryResource); if (originalBaseResource == null)
{
if (webApp.getBaseResource() == null)
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) if (classesDirectory != null)
webApp.setClasses (classesDirectory); webApp.setClasses (classesDirectory);
@ -286,55 +300,12 @@ public class JettyRunMojo extends AbstractJettyMojo
webApp.setWebInfLib(getDependencyFiles()); 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 //if we have not already set web.xml location, need to set one up
List<Overlay> overlays = new ArrayList<>(); if (webApp.getDescriptor() == null)
for (OverlayConfig config:warPluginInfo.getMavenWarOverlayConfigs())
{ {
//overlays can be individually skipped //Has an explicit web.xml file been configured to use?
if (config.isSkip()) if (webXml != null)
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)
{
//Has an explicit web.xml file been configured to use?
if (webXml != null)
{ {
Resource r = Resource.newResource(webXml); Resource r = Resource.newResource(webXml);
if (r.exists() && !r.isDirectory()) if (r.exists() && !r.isDirectory())
@ -362,9 +333,14 @@ public class JettyRunMojo extends AbstractJettyMojo
webApp.setDescriptor(f.getCanonicalPath()); webApp.setDescriptor(f.getCanonicalPath());
} }
} }
} }
getLog().info( "web.xml file = "+webApp.getDescriptor());
getLog().info("Webapp directory = " + webAppSourceDirectory.getCanonicalPath()); //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());
} }
private static File toFile(Resource resource) private static File toFile(Resource resource)
@ -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 * @return

View File

@ -31,9 +31,7 @@ import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.TreeSet; import java.util.TreeSet;
import org.eclipse.jetty.annotations.AnnotationConfiguration;
import org.eclipse.jetty.plus.webapp.EnvConfiguration; 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.PreconfigureDescriptorProcessor;
import org.eclipse.jetty.quickstart.QuickStartDescriptorGenerator; import org.eclipse.jetty.quickstart.QuickStartDescriptorGenerator;
import org.eclipse.jetty.servlet.FilterHolder; 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.Resource;
import org.eclipse.jetty.util.resource.ResourceCollection; import org.eclipse.jetty.util.resource.ResourceCollection;
import org.eclipse.jetty.webapp.Configuration; 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.WebAppContext;
import org.eclipse.jetty.webapp.WebInfConfiguration; import org.eclipse.jetty.webapp.WebInfConfiguration;
import org.eclipse.jetty.webapp.WebXmlConfiguration;
/** /**
* JettyWebAppContext * JettyWebAppContext
@ -73,7 +67,14 @@ public class JettyWebAppContext extends WebAppContext
private static final String WEB_INF_CLASSES_PREFIX = "/WEB-INF/classes"; private static final String WEB_INF_CLASSES_PREFIX = "/WEB-INF/classes";
private static final String WEB_INF_LIB_PREFIX = "/WEB-INF/lib"; 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 = { public static final String[] DEFAULT_CONFIGURATION_CLASSES = {
"org.eclipse.jetty.maven.plugin.MavenWebInfConfiguration", "org.eclipse.jetty.maven.plugin.MavenWebInfConfiguration",
"org.eclipse.jetty.webapp.WebXmlConfiguration", "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.maven.plugin.MavenQuickStartConfiguration",
"org.eclipse.jetty.plus.webapp.EnvConfiguration", "org.eclipse.jetty.plus.webapp.EnvConfiguration",
"org.eclipse.jetty.plus.webapp.PlusConfiguration", "org.eclipse.jetty.plus.webapp.PlusConfiguration",
@ -375,18 +376,11 @@ public class JettyWebAppContext extends WebAppContext
@Override @Override
public void doStart () throws Exception public void doStart () throws Exception
{ {
//choose if this will be a quickstart or normal start
if (!isGenerateQuickStart() && getQuickStartWebDescriptor() != null) if (isGenerateQuickStart())
{ {
setConfigurationClasses(QUICKSTART_CONFIGURATION_CLASSES); _preconfigProcessor = new PreconfigureDescriptorProcessor();
} getMetaData().addDescriptorProcessor(_preconfigProcessor);
else
{
if (isGenerateQuickStart())
{
_preconfigProcessor = new PreconfigureDescriptorProcessor();
getMetaData().addDescriptorProcessor(_preconfigProcessor);
}
} }
//Set up the pattern that tells us where the jars are that need scanning //Set up the pattern that tells us where the jars are that need scanning
@ -444,9 +438,7 @@ public class JettyWebAppContext extends WebAppContext
for (Configuration c:getConfigurations()) for (Configuration c:getConfigurations())
{ {
if (c instanceof EnvConfiguration && getJettyEnvXml() != null) if (c instanceof EnvConfiguration && getJettyEnvXml() != null)
((EnvConfiguration)c).setJettyEnvXml(Resource.toURL(new File(getJettyEnvXml()))); ((EnvConfiguration)c).setJettyEnvXml(Resource.toURL(new File(getJettyEnvXml())));
else if (c instanceof MavenQuickStartConfiguration && getQuickStartWebDescriptor() != null)
((MavenQuickStartConfiguration)c).setQuickStartWebXml(getQuickStartWebDescriptor());
} }
} }

View File

@ -39,22 +39,8 @@ import org.eclipse.jetty.webapp.WebAppContext;
public class MavenQuickStartConfiguration extends QuickStartConfiguration public class MavenQuickStartConfiguration extends QuickStartConfiguration
{ {
private static final Logger LOG = Log.getLogger(QuickStartConfiguration.class); 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 @Override
public void preConfigure(WebAppContext context) throws Exception 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 //look for quickstart-web.xml in WEB-INF of webapp
Resource quickStartWebXml = getQuickStartWebXml(context); Resource quickStartWebXml = ((JettyWebAppContext)context).getQuickStartWebDescriptor();
LOG.debug("quickStartWebXml={}",quickStartWebXml); LOG.debug("quickStartWebXml={}",quickStartWebXml);
context.getMetaData().setWebXml(quickStartWebXml); context.getMetaData().setWebXml(quickStartWebXml);

View File

@ -19,17 +19,14 @@
package org.eclipse.jetty.maven.plugin; package org.eclipse.jetty.maven.plugin;
import java.io.File; import java.io.File;
import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.resource.Resource; 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.WebAppClassLoader;
import org.eclipse.jetty.webapp.WebAppContext; import org.eclipse.jetty.webapp.WebAppContext;
import org.eclipse.jetty.webapp.WebInfConfiguration; import org.eclipse.jetty.webapp.WebInfConfiguration;
@ -45,11 +42,6 @@ import org.eclipse.jetty.webapp.WebInfConfiguration;
public class MavenWebInfConfiguration extends WebInfConfiguration public class MavenWebInfConfiguration extends WebInfConfiguration
{ {
private static final Logger LOG = Log.getLogger(MavenWebInfConfiguration.class); 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) * @see org.eclipse.jetty.webapp.WebInfConfiguration#configure(org.eclipse.jetty.webapp.WebAppContext)
@ -84,91 +76,6 @@ public class MavenWebInfConfiguration extends WebInfConfiguration
context.setServerClasses( newServerClasses ); 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 * Get the jars to examine from the files from which we have
* synthesized the classpath. Note that the classpath is not * 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; package org.eclipse.jetty.maven.plugin;
import java.io.File; 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.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List; 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.Handler;
import org.eclipse.jetty.server.Server; 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.Log;
import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.resource.Resource; 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 static final Logger LOG = Log.getLogger(Starter.class);
private List<File> jettyXmls; // list of jetty.xml config files to apply - Mandatory private List<File> jettyXmls; // list of jetty.xml config files to apply
private File contextXml; //name of context xml file to configure the webapp - Mandatory
private Server server; private Server server;
private JettyWebAppContext webApp; private JettyWebAppContext webApp;
@ -64,57 +48,10 @@ public class Starter
private int stopPort=0; private int stopPort=0;
private String stopKey=null; private String stopKey=null;
private Properties props; private File propsFile;
private String token; 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 public void configureJetty () throws Exception
{ {
@ -146,19 +83,6 @@ public class Starter
webApp.setQuickStartWebDescriptor(Resource.newResource(qs)); 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); ServerSupport.addWebApplication(server, webApp);
if(stopPort>0 && stopKey!=null) if(stopPort>0 && stopKey!=null)
@ -173,147 +97,11 @@ public class Starter
public void configureWebApp () public void configureWebApp ()
throws Exception throws Exception
{ {
if (props == null) if (propsFile == null)
return; return;
//apply a properties file that defines the things that we configure in the jetty:run plugin: //apply a properties file that defines the things that we configure in the jetty:run plugin
// - the context path WebAppPropertyConverter.fromProperties(webApp, propsFile, server);
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);
}
} }
public void getConfiguration (String[] args) 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 //--props
if ("--props".equals(args[i])) if ("--props".equals(args[i]))
{ {
File f = new File(args[++i].trim()); propsFile = new File(args[++i].trim());
props = new Properties();
try (InputStream in = new FileInputStream(f))
{
props.load(in);
}
} }
//--token //--token
@ -419,25 +196,6 @@ public class Starter
System.arraycopy(existing, 0, children, 1, existing.length); System.arraycopy(existing, 0, children, 1, existing.length);
handlers.setHandlers(children); 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 * @param csv
@ -456,6 +214,9 @@ public class Starter
return list; return list;
} }
/**
* @param args
*/
public static final void main(String[] args) public static final void main(String[] args)
{ {
if (args == null) 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); 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) if (_configurations.size()>0)
return; return;
if (_configurationClasses.size()==0) if (_configurationClasses.size()==0) {
_configurationClasses.addAll(Configuration.ClassList.serverDefault(getServer())); _configurationClasses.addAll(Configuration.ClassList.serverDefault(getServer()));
}
for (String configClass : _configurationClasses) for (String configClass : _configurationClasses)
_configurations.add((Configuration)Loader.loadClass(configClass).newInstance()); _configurations.add((Configuration)Loader.loadClass(configClass).newInstance());
} }