All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.eclipse.jetty.ee10.maven.plugin.JettyRunMojo Maven / Gradle / Ivy

There is a newer version: 12.0.14
Show newest version
//
// ========================================================================
// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//

package org.eclipse.jetty.ee10.maven.plugin;

import java.io.File;
import java.nio.file.Path;
import java.nio.file.PathMatcher;
import java.util.Date;
import java.util.Set;

import org.apache.maven.artifact.Artifact;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugins.annotations.Execute;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.plugins.annotations.ResolutionScope;
import org.eclipse.jetty.ee10.webapp.WebAppContext;
import org.eclipse.jetty.maven.ConsoleReader;
import org.eclipse.jetty.util.IncludeExcludeSet;
import org.eclipse.jetty.util.Scanner;
import org.eclipse.jetty.util.component.LifeCycle;
import org.eclipse.jetty.util.resource.Resource;
import org.eclipse.jetty.util.thread.Scheduler;

/**
 *  This goal is used in-situ on a Maven project without first requiring that the project 
 *  is assembled into a war, saving time during the development cycle.
 *  

* The plugin runs a parallel lifecycle to ensure that the "test-compile" phase has been completed before invoking Jetty. This means * that you do not need to explicity execute a "mvn compile" first. It also means that a "mvn clean jetty:run" will ensure that * a full fresh compile is done before invoking Jetty. *

* Once invoked, the plugin can be configured to run continuously, scanning for changes in the project and automatically performing a * hot redeploy when necessary. This allows the developer to concentrate on coding changes to the project using their IDE of choice and have those changes * immediately and transparently reflected in the running web container, eliminating development time that is wasted on rebuilding, reassembling and redeploying. * Alternatively, you can configure the plugin to wait for an <enter> at the command line to manually control redeployment. *

* You can configure this goal to run your unassembled webapp either in-process with maven, or forked into a new process, or deployed into a * jetty distribution. */ @Mojo (name = "run", requiresDependencyResolution = ResolutionScope.TEST) @Execute (phase = LifecyclePhase.TEST_COMPILE) public class JettyRunMojo extends AbstractUnassembledWebAppMojo { //Start of parameters only valid for deploymentType=EMBED /** * Controls redeployment of the webapp. *

    *
  1. -1 : means no redeployment will be done
  2. *
  3. 0 : means redeployment only occurs if you hit the ENTER key
  4. *
  5. otherwise, the interval in seconds to pause before checking and redeploying if necessary
  6. *
*/ @Parameter(defaultValue = "-1", property = "jetty.scan", required = true) protected int scan; /** * Scanner to check for files changes to cause redeploy */ protected Scanner scanner; /** * Only one of the following will be used, depending the mode * the mojo is started in: EMBED, FORK, EXTERNAL */ protected JettyEmbedder embedder; protected JettyForker forker; protected JettyHomeForker homeForker; @Override public void execute() throws MojoExecutionException, MojoFailureException { super.execute(); } @Override public void startJettyEmbedded() throws MojoExecutionException { try { //start jetty embedder = newJettyEmbedder(); embedder.setExitVm(true); embedder.setStopAtShutdown(true); embedder.start(); startScanner(); embedder.join(); } catch (Exception e) { throw new MojoExecutionException("Error starting jetty", e); } } @Override public void startJettyForked() throws MojoExecutionException { try { forker = newJettyForker(); forker.setWaitForChild(true); //we run at the command line, echo child output and wait for it //if we can do hot redeploy, tell the forked process to watch for changes to the generated webapp file if (scan >= 0) { forker.setScan(true); forker.setScanInterval((scan == 0 ? 1 : scan)); //if redeploying on ENTER key, the forked process still needs to watch the generated webapp file } //TODO is it ok to start the scanner before we start jetty? startScanner(); forker.start(); //forks jetty instance } catch (Exception e) { throw new MojoExecutionException("Error starting jetty", e); } } @Override public void startJettyHome() throws MojoExecutionException { try { homeForker = newJettyHomeForker(); homeForker.setWaitForChild(true); //we always run at the command line, echo child output and wait for it //TODO is it ok to start the scanner before we start jetty? startScanner(); homeForker.start(); //forks a jetty distro } catch (Exception e) { throw new MojoExecutionException("Error starting jetty", e); } } private void startScanner() throws Exception { if (scan < 0) { getLog().info("Automatic redeployment disabled, see 'mvn jetty:help' for more redeployment options"); return; //no automatic or manual redeployment } // start scanning for changes, or wait for linefeed on stdin if (scan > 0) { scanner = new Scanner(); scanner.setScanInterval(scan); scanner.setScanDepth(Scanner.MAX_SCAN_DEPTH); //always fully walk directory hierarchies scanner.setReportExistingFilesOnStartup(false); scanner.addListener(new Scanner.BulkListener() { public void filesChanged(Set changes) { try { restartWebApp(changes.contains(project.getFile().getCanonicalPath())); } catch (Exception e) { getLog().error("Error reconfiguring/restarting webapp after change in watched files", e); } } }); configureScanner(); getLog().info("Scan interval sec = " + scan); //unmanage scheduler so it is not stopped with the scanner Scheduler scheduler = scanner.getBean(Scheduler.class); scanner.unmanage(scheduler); LifeCycle.start(scheduler); scanner.start(); } else { ConsoleReader creader = new ConsoleReader(); creader.addListener(new ConsoleReader.Listener() { @Override public void consoleEvent(String line) { try { restartWebApp(false); } catch (Exception e) { getLog().debug(e); } } }); Thread cthread = new Thread(creader, "ConsoleReader"); cthread.setDaemon(true); cthread.start(); } } protected void configureScanner() throws MojoExecutionException { try { gatherScannables(); } catch (Exception e) { throw new MojoExecutionException("Error forming scan list", e); } } public void gatherScannables() throws Exception { if (webApp.getDescriptor() != null) { Resource r = webApp.getResourceFactory().newResource(webApp.getDescriptor()); scanner.addFile(r.getPath()); } if (webApp.getJettyEnvXml() != null) scanner.addFile(new File(webApp.getJettyEnvXml()).toPath()); if (webApp.getDefaultsDescriptor() != null) { if (!WebAppContext.WEB_DEFAULTS_XML.equals(webApp.getDefaultsDescriptor())) scanner.addFile(new File(webApp.getDefaultsDescriptor()).toPath()); } if (webApp.getOverrideDescriptor() != null) { scanner.addFile(new File(webApp.getOverrideDescriptor()).toPath()); } File jettyWebXmlFile = findJettyWebXmlFile(new File(webAppSourceDirectory, "WEB-INF")); if (jettyWebXmlFile != null) { scanner.addFile(jettyWebXmlFile.toPath()); } //make sure each of the war artifacts is added to the scanner for (Artifact a:mavenProjectHelper.getWarPluginInfo().getWarArtifacts()) { File f = a.getFile(); if (a.getFile().isDirectory()) scanner.addDirectory(f.toPath()); else scanner.addFile(f.toPath()); } //set up any extra files or dirs to watch configureScanTargetPatterns(scanner); scanner.addFile(project.getFile().toPath()); if (webApp.getTestClasses() != null && webApp.getTestClasses().exists()) { Path p = webApp.getTestClasses().toPath(); IncludeExcludeSet includeExcludeSet = scanner.addDirectory(p); if (scanTestClassesPattern != null) { for (String s : scanTestClassesPattern.getExcludes()) { if (!s.startsWith("glob:")) s = "glob:" + s; includeExcludeSet.exclude(p.getFileSystem().getPathMatcher(s)); } for (String s : scanTestClassesPattern.getIncludes()) { if (!s.startsWith("glob:")) s = "glob:" + s; includeExcludeSet.include(p.getFileSystem().getPathMatcher(s)); } } } if (webApp.getClasses() != null && webApp.getClasses().exists()) { Path p = webApp.getClasses().toPath(); IncludeExcludeSet includeExcludes = scanner.addDirectory(p); if (scanClassesPattern != null) { for (String s : scanClassesPattern.getExcludes()) { if (!s.startsWith("glob:")) s = "glob:" + s; includeExcludes.exclude(p.getFileSystem().getPathMatcher(s)); } for (String s : scanClassesPattern.getIncludes()) { if (!s.startsWith("glob:")) s = "glob:" + s; includeExcludes.include(p.getFileSystem().getPathMatcher(s)); } } } if (webApp.getWebInfLib() != null) { for (File f : webApp.getWebInfLib()) { if (f.isDirectory()) scanner.addDirectory(f.toPath()); else scanner.addFile(f.toPath()); } } } /** * Stop an executing webapp and restart it after optionally * reconfiguring it. * * @param reconfigure if true, the scanner will * be reconfigured after changes to the pom. If false, only * the webapp will be reconfigured. * * @throws Exception if there is an unspecified problem */ public void restartWebApp(boolean reconfigure) throws Exception { getLog().info("Restarting " + webApp); getLog().debug("Stopping webapp ..."); if (scanner != null) scanner.stop(); switch (deployMode) { case EMBED: { getLog().debug("Reconfiguring webapp ..."); verifyPomConfiguration(); // check if we need to reconfigure the scanner, // which is if the pom changes if (reconfigure) { getLog().info("Reconfiguring scanner after change to pom.xml ..."); warArtifacts = null; //will be regenerated by configureWebApp if (scanner != null) { scanner.reset(); configureScanner(); } } embedder.stopWebApp(); configureWebApp(); embedder.redeployWebApp(); if (scanner != null) scanner.start(); getLog().info("Restart completed at " + new Date().toString()); break; } case FORK: { verifyPomConfiguration(); if (reconfigure) { getLog().info("Reconfiguring scanner after change to pom.xml ..."); warArtifacts = null; ///TODO if the pom changes for the forked case, how would we get the forked process to stop and restart? if (scanner != null) { scanner.reset(); configureScanner(); } } configureWebApp(); //regenerate with new config and restart the webapp forker.redeployWebApp(); //restart scanner if (scanner != null) scanner.start(); break; } case DISTRO: case HOME: case EXTERNAL: { if (deployMode != DeploymentMode.EXTERNAL) getLog().warn(deployMode + " mode is deprecated, use mode EXTERNAL"); verifyPomConfiguration(); if (reconfigure) { getLog().info("Reconfiguring scanner after change to pom.xml ..."); warArtifacts = null; //TODO if there are any changes to the pom, then we would have to tell the //existing forked home process to stop, then rerun the configuration and then refork - too complicated??! if (scanner != null) { scanner.reset(); configureScanner(); } } configureWebApp(); //regenerate the webapp and redeploy it homeForker.redeployWebApp(); //restart scanner if (scanner != null) scanner.start(); break; } default: { throw new IllegalStateException("Unrecognized run type " + deployMode); } } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy