View Javadoc

    1   //========================================================================
    2   //$Id: AbstractJettyRunMojo.java 5224 2009-05-29 07:56:29Z dyu $
    3   //Copyright 2000-2004 Mort Bay Consulting Pty. Ltd.
    4   //------------------------------------------------------------------------
    5   //Licensed under the Apache License, Version 2.0 (the "License");
    6   //you may not use this file except in compliance with the License.
    7   //You may obtain a copy of the License at
    8   //http://www.apache.org/licenses/LICENSE-2.0
    9   //Unless required by applicable law or agreed to in writing, software
    10  //distributed under the License is distributed on an "AS IS" BASIS,
    11  //WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  //See the License for the specific language governing permissions and
    13  //limitations under the License.
    14  //========================================================================
    15
    16  package org.mortbay.jetty.plugin;
    17
    18  import java.io.File;
    19  import java.io.IOException;
    20  import java.util.ArrayList;
    21  import java.util.Collections;
    22  import java.util.Date;
    23  import java.util.Iterator;
    24  import java.util.List;
    25
    26  import org.apache.maven.artifact.Artifact;
    27  import org.apache.maven.plugin.MojoExecutionException;
    28  import org.apache.maven.plugin.MojoFailureException;
    29  import org.codehaus.plexus.util.FileUtils;
    30  import org.mortbay.jetty.plugin.util.ScanTargetPattern;
    31  import org.mortbay.resource.Resource;
    32  import org.mortbay.resource.ResourceCollection;
    33  import org.mortbay.util.Scanner;
    34
    35  /**
    36   * AbstractJettyRunMojo
    37   *
    38   *
    39   * Base class for all jetty versions for the "run" mojo.
    40   *
    41   */
    42  public abstract class AbstractJettyRunMojo extends AbstractJettyMojo
    43  {
    44
    45      /**
    46       * If true, the &lt;testOutputDirectory&gt;
    47       * and the dependencies of &lt;scope&gt;test&lt;scope&gt;
    48       * will be put first on the runtime classpath.
    49       * @parameter default-value="false"
    50       */
    51      private boolean useTestClasspath;
    52
    53
    54      /**
    55       * The location of a jetty-env.xml file. Optional.
    56       * @parameter
    57       */
    58      private File jettyEnvXml;
    59
    60      /**
    61       * The location of the web.xml file. If not
    62       * set then it is assumed it is in ${basedir}/src/main/webapp/WEB-INF
    63       *
    64       * @parameter expression="${maven.war.webxml}"
    65       */
    66      private File webXml;
    67
    68      /**
    69       * The directory containing generated classes.
    70       *
    71       * @parameter expression="${project.build.outputDirectory}"
    72       * @required
    73       *
    74       */
    75      private File classesDirectory;
    76
    77
    78
    79      /**
    80       * The directory containing generated test classes.
    81       *
    82       * @parameter expression="${project.build.testOutputDirectory}"
    83       * @required
    84       */
    85      private File testClassesDirectory;
    86
    87      /**
    88       * Root directory for all html/jsp etc files
    89       *
    90       * @parameter expression="${basedir}/src/main/webapp"
    91       * @required
    92       */
    93      private File webAppSourceDirectory;
    94
    95      /**
    96       * @parameter expression="${plugin.artifacts}"
    97       * @readonly
    98       */
    99      private List pluginArtifacts;
    100
    101     /**
    102      * List of files or directories to additionally periodically scan for changes. Optional.
    103      * @parameter
    104      */
    105     private File[] scanTargets;
    106
    107
    108     /**
    109      * List of directories with ant-style &lt;include&gt; and &lt;exclude&gt; patterns
    110      * for extra targets to periodically scan for changes. Can be used instead of,
    111      * or in conjunction with &lt;scanTargets&gt;.Optional.
    112      * @parameter
    113      */
    114     private ScanTargetPattern[] scanTargetPatterns;
    115
    116     /**
    117      * web.xml as a File
    118      */
    119     private File webXmlFile;
    120
    121
    122     /**
    123      * jetty-env.xml as a File
    124      */
    125     private File jettyEnvXmlFile;
    126
    127     /**
    128      * List of files on the classpath for the webapp
    129      */
    130     private List classPathFiles;
    131
    132
    133     /**
    134      * Extra scan targets as a list
    135      */
    136     private List extraScanTargets;
    137
    138     /**
    139      * overlays (resources)
    140      */
    141     private List _overlays;
    142
    143     public File getWebXml()
    144     {
    145         return this.webXml;
    146     }
    147
    148     public File getJettyEnvXml ()
    149     {
    150         return this.jettyEnvXml;
    151     }
    152
    153     public File getClassesDirectory()
    154     {
    155         return this.classesDirectory;
    156     }
    157
    158     public File getWebAppSourceDirectory()
    159     {
    160         return this.webAppSourceDirectory;
    161     }
    162
    163     public void setWebXmlFile (File f)
    164     {
    165         this.webXmlFile = f;
    166     }
    167
    168     public File getWebXmlFile ()
    169     {
    170         return this.webXmlFile;
    171     }
    172
    173     public File getJettyEnvXmlFile ()
    174     {
    175         return this.jettyEnvXmlFile;
    176     }
    177
    178     public void setJettyEnvXmlFile (File f)
    179     {
    180         this.jettyEnvXmlFile = f;
    181     }
    182
    183     public void setClassPathFiles (List list)
    184     {
    185         this.classPathFiles = new ArrayList(list);
    186     }
    187
    188     public List getClassPathFiles ()
    189     {
    190         return this.classPathFiles;
    191     }
    192
    193
    194     public List getExtraScanTargets ()
    195     {
    196         return this.extraScanTargets;
    197     }
    198
    199     public void setExtraScanTargets(List list)
    200     {
    201         this.extraScanTargets = list;
    202     }
    203
    204     /**
    205      * Run the mojo
    206      * @see org.apache.maven.plugin.Mojo#execute()
    207      */
    208     public void execute() throws MojoExecutionException, MojoFailureException
    209     {
    210        super.execute();
    211     }
    212
    213
    214     /**
    215      * Verify the configuration given in the pom.
    216      *
    217      * @see org.mortbay.jetty.plugin.AbstractJettyMojo#checkPomConfiguration()
    218      */
    219     public void checkPomConfiguration () throws MojoExecutionException
    220     {
    221         File buildDir = new File(getProject().getBuild().getDirectory());
    222         if(!buildDir.exists())
    223             buildDir.mkdir();
    224         // check the location of the static content/jsps etc
    225         try
    226         {
    227             if ((getWebAppSourceDirectory() == null) || !getWebAppSourceDirectory().exists())
    228                 throw new MojoExecutionException("Webapp source directory "
    229                         + (getWebAppSourceDirectory() == null ? "null" : getWebAppSourceDirectory().getCanonicalPath())
    230                         + " does not exist");
    231             else
    232                 getLog().info( "Webapp source directory = "
    233                         + getWebAppSourceDirectory().getCanonicalPath());
    234         }
    235         catch (IOException e)
    236         {
    237             throw new MojoExecutionException("Webapp source directory does not exist", e);
    238         }
    239
    240         // check reload mechanic
    241         if ( !"automatic".equalsIgnoreCase( reload ) && !"manual".equalsIgnoreCase( reload ) )
    242         {
    243             throw new MojoExecutionException( "invalid reload mechanic specified, must be 'automatic' or 'manual'" );
    244         }
    245         else
    246         {
    247             getLog().info("Reload Mechanic: " + reload );
    248         }
    249
    250         //check if a jetty-env.xml location has been provided, if so, it must exist
    251         if  (getJettyEnvXml() != null)
    252         {
    253             setJettyEnvXmlFile(jettyEnvXml);
    254
    255             try
    256             {
    257                 if (!getJettyEnvXmlFile().exists())
    258                     throw new MojoExecutionException("jetty-env.xml file does not exist at location "+jettyEnvXml);
    259                 else
    260                     getLog().info(" jetty-env.xml = "+getJettyEnvXmlFile().getCanonicalPath());
    261             }
    262             catch (IOException e)
    263             {
    264                 throw new MojoExecutionException("jetty-env.xml does not exist");
    265             }
    266         }
    267
    268
    269         // check the classes to form a classpath with
    270         try
    271         {
    272             //allow a webapp with no classes in it (just jsps/html)
    273             if (getClassesDirectory() != null)
    274             {
    275                 if (!getClassesDirectory().exists())
    276                     getLog().info( "Classes directory "+ getClassesDirectory().getCanonicalPath()+ " does not exist");
    277                 else
    278                     getLog().info("Classes = " + getClassesDirectory().getCanonicalPath());
    279             }
    280             else
    281                 getLog().info("Classes directory not set");
    282         }
    283         catch (IOException e)
    284         {
    285             throw new MojoExecutionException("Location of classesDirectory does not exist");
    286         }
    287
    288
    289         setExtraScanTargets(new ArrayList());
    290         if (scanTargets != null)
    291         {
    292             for (int i=0; i< scanTargets.length; i++)
    293             {
    294                 getLog().info("Added extra scan target:"+ scanTargets[i]);
    295                 getExtraScanTargets().add(scanTargets[i]);
    296             }
    297         }
    298
    299
    300         if (scanTargetPatterns!=null)
    301         {
    302             for (int i=0;i<scanTargetPatterns.length; i++)
    303             {
    304                 Iterator itor = scanTargetPatterns[i].getIncludes().iterator();
    305                 StringBuffer strbuff = new StringBuffer();
    306                 while (itor.hasNext())
    307                 {
    308                     strbuff.append((String)itor.next());
    309                     if (itor.hasNext())
    310                         strbuff.append(",");
    311                 }
    312                 String includes = strbuff.toString();
    313
    314                 itor = scanTargetPatterns[i].getExcludes().iterator();
    315                 strbuff= new StringBuffer();
    316                 while (itor.hasNext())
    317                 {
    318                     strbuff.append((String)itor.next());
    319                     if (itor.hasNext())
    320                         strbuff.append(",");
    321                 }
    322                 String excludes = strbuff.toString();
    323
    324                 try
    325                 {
    326                     List files = FileUtils.getFiles(scanTargetPatterns[i].getDirectory(), includes, excludes);
    327                     itor = files.iterator();
    328                     while (itor.hasNext())
    329                         getLog().info("Adding extra scan target from pattern: "+itor.next());
    330                     List currentTargets = getExtraScanTargets();
    331                     if(currentTargets!=null && !currentTargets.isEmpty())
    332                         currentTargets.addAll(files);
    333                     else
    334                         setExtraScanTargets(files);
    335                 }
    336                 catch (IOException e)
    337                 {
    338                     throw new MojoExecutionException(e.getMessage());
    339                 }
    340             }
    341
    342
    343         }
    344     }
    345
    346     private void checkWebXml() throws MojoExecutionException
    347     {
    348         // get the web.xml file if one has been provided, otherwise assume it is
    349         // in the webapp src directory
    350         if (getWebXml() == null )
    351             webXml = new File(new File(getWebAppSourceDirectory(),"WEB-INF"), "web.xml");
    352         setWebXmlFile(webXml);
    353
    354         try
    355         {
    356             if (!getWebXmlFile().exists())
    357             {
    358                 Resource resource = webAppConfig.getBaseResource().addPath("WEB-INF/web.xml");
    359                 if(!resource.exists())
    360                 {
    361
    362                     throw new MojoExecutionException( "web.xml does not exist at location "
    363                             + webXmlFile.getCanonicalPath());
    364                 }
    365                 getLog().info( "web.xml file = " + resource);
    366             }
    367             else
    368                 getLog().info( "web.xml file = "
    369                         + webXmlFile.getCanonicalPath());
    370         }
    371         catch (IOException e)
    372         {
    373             throw new MojoExecutionException("web.xml does not exist", e);
    374         }
    375     }
    376
    377
    378
    379     public void configureWebApplication() throws Exception
    380     {
    381        super.configureWebApplication();
    382         setClassPathFiles(setUpClassPath());
    383         checkWebXml();
    384         if(webAppConfig.getWebXmlFile()==null)
    385             webAppConfig.setWebXmlFile(getWebXmlFile());
    386         if(webAppConfig.getJettyEnvXmlFile()==null)
    387             webAppConfig.setJettyEnvXmlFile(getJettyEnvXmlFile());
    388         if(webAppConfig.getClassPathFiles()==null)
    389             webAppConfig.setClassPathFiles(getClassPathFiles());
    390         if(webAppConfig.getWar()==null)
    391             webAppConfig.setWar(getWebAppSourceDirectory().getCanonicalPath());
    392         getLog().info("Webapp directory = " + getWebAppSourceDirectory().getCanonicalPath());
    393
    394         webAppConfig.configure();
    395     }
    396
    397     public void configureScanner ()
    398     {
    399         // start the scanner thread (if necessary) on the main webapp
    400         final ArrayList scanList = new ArrayList();
    401         scanList.add(getWebXmlFile());
    402         if (getJettyEnvXmlFile() != null)
    403             scanList.add(getJettyEnvXmlFile());
    404         File jettyWebXmlFile = findJettyWebXmlFile(new File(getWebAppSourceDirectory(),"WEB-INF"));
    405         if (jettyWebXmlFile != null)
    406             scanList.add(jettyWebXmlFile);
    407         scanList.addAll(getExtraScanTargets());
    408         scanList.add(getProject().getFile());
    409         scanList.addAll(getClassPathFiles());
    410         setScanList(scanList);
    411         ArrayList listeners = new ArrayList();
    412         listeners.add(new Scanner.BulkListener()
    413         {
    414             public void filesChanged (List changes)
    415             {
    416                 try
    417                 {
    418                     boolean reconfigure = changes.contains(getProject().getFile().getCanonicalPath());
    419                     restartWebApp(reconfigure);
    420                 }
    421                 catch (Exception e)
    422                 {
    423                     getLog().error("Error reconfiguring/restarting webapp after change in watched files",e);
    424                 }
    425             }
    426
    427
    428         });
    429         setScannerListeners(listeners);
    430     }
    431
    432     public void restartWebApp(boolean reconfigureScanner) throws Exception
    433     {
    434         getLog().info("restarting "+webAppConfig);
    435         getLog().debug("Stopping webapp ...");
    436         webAppConfig.stop();
    437         getLog().debug("Reconfiguring webapp ...");
    438
    439         checkPomConfiguration();
    440         configureWebApplication();
    441
    442         // check if we need to reconfigure the scanner,
    443         // which is if the pom changes
    444         if (reconfigureScanner)
    445         {
    446             getLog().info("Reconfiguring scanner after change to pom.xml ...");
    447             scanList.clear();
    448             scanList.add(getWebXmlFile());
    449             if (getJettyEnvXmlFile() != null)
    450                 scanList.add(getJettyEnvXmlFile());
    451             scanList.addAll(getExtraScanTargets());
    452             scanList.add(getProject().getFile());
    453             scanList.addAll(getClassPathFiles());
    454             getScanner().setScanDirs(scanList);
    455         }
    456
    457         getLog().debug("Restarting webapp ...");
    458         webAppConfig.start();
    459         getLog().info("Restart completed at "+new Date().toString());
    460     }
    461
    462     private List getDependencyFiles ()
    463     {
    464         List dependencyFiles = new ArrayList();
    465         List overlays = new ArrayList();
    466         for ( Iterator iter = getProject().getArtifacts().iterator(); iter.hasNext(); )
    467         {
    468             Artifact artifact = (Artifact) iter.next();
    469             // Include runtime and compile time libraries, and possibly test libs too
    470             if(artifact.getType().equals("war"))
    471             {
    472                 try
    473                 {
    474                     Resource r = Resource.newResource("jar:" + artifact.getFile().toURL().toString() + "!/");
    475                     overlays.add(r);
    476                     getExtraScanTargets().add(artifact.getFile());
    477                 }
    478                 catch(Exception e)
    479                 {
    480                     throw new RuntimeException(e);
    481                 }
    482                 continue;
    483             }
    484             if (((!Artifact.SCOPE_PROVIDED.equals(artifact.getScope())) && (!Artifact.SCOPE_TEST.equals( artifact.getScope())))
    485                     ||
    486                 (useTestClasspath && Artifact.SCOPE_TEST.equals( artifact.getScope())))
    487             {
    488                 dependencyFiles.add(artifact.getFile());
    489                 getLog().debug( "Adding artifact " + artifact.getFile().getName() + " for WEB-INF/lib " );
    490             }
    491         }
    492         if(!overlays.isEmpty() && !isEqual(overlays, _overlays))
    493         {
    494             try
    495             {
    496                 Resource resource = _overlays==null ? webAppConfig.getBaseResource() : null;
    497                 ResourceCollection rc = new ResourceCollection();
    498                 if(resource==null)
    499                 {
    500                     // nothing configured, so we automagically enable the overlays
    501                     int size = overlays.size()+1;
    502                     Resource[] resources = new Resource[size];
    503                     resources[0] = Resource.newResource(getWebAppSourceDirectory().toURL());
    504                     for(int i=1; i<size; i++)
    505                     {
    506                         resources[i] = (Resource)overlays.get(i-1);
    507                         getLog().info("Adding overlay: " + resources[i]);
    508                     }
    509                     rc.setResources(resources);
    510                 }
    511                 else
    512                 {
    513                     if(resource instanceof ResourceCollection)
    514                     {
    515                         // there was a preconfigured ResourceCollection ... append the artifact wars
    516                         Resource[] old = ((ResourceCollection)resource).getResources();
    517                         int size = old.length + overlays.size();
    518                         Resource[] resources = new Resource[size];
    519                         System.arraycopy(old, 0, resources, 0, old.length);
    520                         for(int i=old.length,j=0; i<size; i++,j++)
    521                         {
    522                             resources[i] = (Resource)overlays.get(j);
    523                             getLog().info("Adding overlay: " + resources[i]);
    524                         }
    525                         rc.setResources(resources);
    526                     }
    527                     else
    528                     {
    529                         // baseResource was already configured w/c could be src/main/webapp
    530                         if(!resource.isDirectory() && String.valueOf(resource.getFile()).endsWith(".war"))
    531                         {
    532                             // its a war
    533                             resource = Resource.newResource("jar:" + resource.getURL().toString() + "!/");
    534                         }
    535                         int size = overlays.size()+1;
    536                         Resource[] resources = new Resource[size];
    537                         resources[0] = resource;
    538                         for(int i=1; i<size; i++)
    539                         {
    540                             resources[i] = (Resource)overlays.get(i-1);
    541                             getLog().info("Adding overlay: " + resources[i]);
    542                         }
    543                         rc.setResources(resources);
    544                     }
    545                 }
    546                 webAppConfig.setBaseResource(rc);
    547                 _overlays = overlays;
    548             }
    549             catch(Exception e)
    550             {
    551                 throw new RuntimeException(e);
    552             }
    553         }
    554         return dependencyFiles;
    555     }
    556
    557
    558
    559
    560     private List setUpClassPath()
    561     {
    562         List classPathFiles = new ArrayList();
    563
    564         //if using the test classes, make sure they are first
    565         //on the list
    566         if (useTestClasspath && (testClassesDirectory != null))
    567             classPathFiles.add(testClassesDirectory);
    568
    569         if (getClassesDirectory() != null)
    570             classPathFiles.add(getClassesDirectory());
    571
    572         //now add all of the dependencies
    573         classPathFiles.addAll(getDependencyFiles());
    574
    575         if (getLog().isDebugEnabled())
    576         {
    577             for (int i = 0; i < classPathFiles.size(); i++)
    578             {
    579                 getLog().debug("classpath element: "+ ((File) classPathFiles.get(i)).getName());
    580             }
    581         }
    582         return classPathFiles;
    583     }
    584
    585     static boolean isEqual(List overlays1, List overlays2)
    586     {
    587         if(overlays2==null || overlays1.size()!=overlays2.size())
    588             return false;
    589
    590         for(int i=0; i<overlays1.size(); i++)
    591         {
    592             if(!overlays1.get(i).equals(overlays2.get(i)))
    593                 return false;
    594         }
    595         return true;
    596     }
    597
    598 }