| // |
| // ======================================================================== |
| // 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.InputStream; |
| import java.net.URL; |
| import java.util.ArrayList; |
| 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 org.eclipse.jetty.server.Handler; |
| import org.eclipse.jetty.server.Server; |
| import org.eclipse.jetty.server.ShutdownMonitor; |
| import org.eclipse.jetty.server.handler.HandlerCollection; |
| 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; |
| |
| |
| |
| /** |
| * Starter Class which is exec'ed to create a new jetty process. Used by the JettyRunForked mojo. |
| */ |
| 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 Server server; |
| private JettyWebAppContext webApp; |
| |
| |
| private int stopPort=0; |
| private String stopKey=null; |
| private Properties props; |
| 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 |
| { |
| LOG.debug("Starting Jetty Server ..."); |
| Resource.setDefaultUseCaches(false); |
| |
| //apply any configs from jetty.xml files first |
| applyJettyXml (); |
| |
| //ensure there's a connector |
| ServerSupport.configureConnectors(server, null); |
| |
| //check if contexts already configured, create if not |
| ServerSupport.configureHandlers(server, null); |
| |
| //Set up list of default Configurations to apply to a webapp |
| ServerSupport.configureDefaultConfigurationClasses(server); |
| |
| webApp = new JettyWebAppContext(); |
| |
| //configure webapp from properties file describing unassembled webapp |
| configureWebApp(); |
| |
| //make it a quickstart if the quickstart-web.xml file exists |
| if (webApp.getTempDirectory() != null) |
| { |
| File qs = new File (webApp.getTempDirectory(), "quickstart-web.xml"); |
| if (qs.exists() && qs.isFile()) |
| webApp.setQuickStartWebDescriptor(Resource.newResource(qs)); |
| } |
| |
| //set up the webapp from the context xml file provided |
| //NOTE: just like jetty:run mojo this means that the context file can |
| //potentially override settings made in the pom. Ideally, we'd like |
| //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) |
| { |
| ShutdownMonitor monitor = ShutdownMonitor.getInstance(); |
| monitor.setPort(stopPort); |
| monitor.setKey(stopKey); |
| monitor.setExitVm(true); |
| } |
| } |
| |
| public void configureWebApp () |
| throws Exception |
| { |
| if (props == null) |
| return; |
| |
| //apply a properties file that defines the things that we configure in the jetty:run plugin: |
| // - the context path |
| String str = (String)props.get("context.path"); |
| if (str != null) |
| webApp.setContextPath(str); |
| |
| |
| // - web.xml |
| str = (String)props.get("web.xml"); |
| if (str != null) |
| webApp.setDescriptor(str); |
| |
| str = (String)props.get("quickstart.web.xml"); |
| if (str != null) |
| webApp.setQuickStartWebDescriptor(Resource.newResource(new File(str))); |
| |
| // - the tmp directory |
| str = (String)props.getProperty("tmp.dir"); |
| if (str != null) |
| webApp.setTempDirectory(new File(str.trim())); |
| |
| str = (String)props.getProperty("tmp.dir.persist"); |
| if (str != null) |
| webApp.setPersistTempDirectory(Boolean.valueOf(str)); |
| |
| //Get the calculated base dirs which includes the overlays |
| str = (String)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 = (String)props.get("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 = (String)props.getProperty("maven.war.includes"); |
| List<String> defaultWarIncludes = fromCSV(str); |
| str = (String)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<Overlay>(); |
| 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 = (String)props.getProperty("classes.dir"); |
| if (str != null && !"".equals(str.trim())) |
| { |
| webApp.setClasses(new File(str)); |
| } |
| |
| str = (String)props.getProperty("testClasses.dir"); |
| if (str != null && !"".equals(str.trim())) |
| { |
| webApp.setTestClasses(new File(str)); |
| } |
| |
| |
| // - the equivalent of web-inf lib |
| str = (String)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) |
| throws Exception |
| { |
| for (int i=0; i<args.length; i++) |
| { |
| //--stop-port |
| if ("--stop-port".equals(args[i])) |
| stopPort = Integer.parseInt(args[++i]); |
| |
| //--stop-key |
| if ("--stop-key".equals(args[i])) |
| stopKey = args[++i]; |
| |
| //--jettyXml |
| if ("--jetty-xml".equals(args[i])) |
| { |
| jettyXmls = new ArrayList<File>(); |
| String[] names = StringUtil.csvSplit(args[++i]); |
| for (int j=0; names!= null && j < names.length; j++) |
| { |
| jettyXmls.add(new File(names[j].trim())); |
| } |
| } |
| |
| //--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); |
| } |
| } |
| |
| //--token |
| if ("--token".equals(args[i])) |
| { |
| token = args[++i].trim(); |
| } |
| } |
| } |
| |
| |
| public void run() throws Exception |
| { |
| LOG.info("Started Jetty Server"); |
| server.start(); |
| } |
| |
| |
| public void join () throws Exception |
| { |
| server.join(); |
| } |
| |
| |
| public void communicateStartupResult (Exception e) |
| { |
| if (token != null) |
| { |
| if (e==null) |
| System.out.println(token); |
| else |
| System.out.println(token+"\t"+e.getMessage()); |
| } |
| } |
| |
| |
| /** |
| * Apply any jetty xml files given |
| * @throws Exception if unable to apply the xml |
| */ |
| public void applyJettyXml() throws Exception |
| { |
| Server tmp = ServerSupport.applyXmlConfigurations(server, jettyXmls); |
| if (server == null) |
| server = tmp; |
| |
| if (server == null) |
| server = new Server(); |
| } |
| |
| |
| |
| |
| protected void prependHandler (Handler handler, HandlerCollection handlers) |
| { |
| if (handler == null || handlers == null) |
| return; |
| |
| Handler[] existing = handlers.getChildHandlers(); |
| Handler[] children = new Handler[existing.length + 1]; |
| children[0] = handler; |
| System.arraycopy(existing, 0, children, 1, existing.length); |
| 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 |
| */ |
| private List<String> fromCSV (String csv) |
| { |
| if (csv == null || "".equals(csv.trim())) |
| return null; |
| String[] atoms = StringUtil.csvSplit(csv); |
| List<String> list = new ArrayList<String>(); |
| for (String a:atoms) |
| { |
| list.add(a.trim()); |
| } |
| return list; |
| } |
| |
| public static final void main(String[] args) |
| { |
| if (args == null) |
| System.exit(1); |
| |
| Starter starter = null; |
| try |
| { |
| starter = new Starter(); |
| starter.getConfiguration(args); |
| starter.configureJetty(); |
| starter.run(); |
| starter.communicateStartupResult(null); |
| starter.join(); |
| } |
| catch (Exception e) |
| { |
| starter.communicateStartupResult(e); |
| e.printStackTrace(); |
| System.exit(1); |
| } |
| |
| } |
| } |