View Javadoc

    1   //========================================================================
    2   //$Id: AbstractJettyRunMojo.java 3649 2008-09-18 06:36:58Z 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     public File getWebXml()
    139     {
    140         return this.webXml;
    141     }
    142
    143     public File getJettyEnvXml ()
    144     {
    145         return this.jettyEnvXml;
    146     }
    147
    148     public File getClassesDirectory()
    149     {
    150         return this.classesDirectory;
    151     }
    152
    153     public File getWebAppSourceDirectory()
    154     {
    155         return this.webAppSourceDirectory;
    156     }
    157
    158     public void setWebXmlFile (File f)
    159     {
    160         this.webXmlFile = f;
    161     }
    162
    163     public File getWebXmlFile ()
    164     {
    165         return this.webXmlFile;
    166     }
    167
    168     public File getJettyEnvXmlFile ()
    169     {
    170         return this.jettyEnvXmlFile;
    171     }
    172
    173     public void setJettyEnvXmlFile (File f)
    174     {
    175         this.jettyEnvXmlFile = f;
    176     }
    177
    178     public void setClassPathFiles (List list)
    179     {
    180         this.classPathFiles = new ArrayList(list);
    181     }
    182
    183     public List getClassPathFiles ()
    184     {
    185         return this.classPathFiles;
    186     }
    187
    188
    189     public List getExtraScanTargets ()
    190     {
    191         return this.extraScanTargets;
    192     }
    193
    194     public void setExtraScanTargets(List list)
    195     {
    196         this.extraScanTargets = list;
    197     }
    198
    199     /**
    200      * Run the mojo
    201      * @see org.apache.maven.plugin.Mojo#execute()
    202      */
    203     public void execute() throws MojoExecutionException, MojoFailureException
    204     {
    205        super.execute();
    206     }
    207
    208
    209     /**
    210      * Verify the configuration given in the pom.
    211      *
    212      * @see org.mortbay.jetty.plugin.AbstractJettyMojo#checkPomConfiguration()
    213      */
    214     public void checkPomConfiguration () throws MojoExecutionException
    215     {
    216         // check the location of the static content/jsps etc
    217         try
    218         {
    219             if ((getWebAppSourceDirectory() == null) || !getWebAppSourceDirectory().exists())
    220                 throw new MojoExecutionException("Webapp source directory "
    221                         + (getWebAppSourceDirectory() == null ? "null" : getWebAppSourceDirectory().getCanonicalPath())
    222                         + " does not exist");
    223             else
    224                 getLog().info( "Webapp source directory = "
    225                         + getWebAppSourceDirectory().getCanonicalPath());
    226         }
    227         catch (IOException e)
    228         {
    229             throw new MojoExecutionException("Webapp source directory does not exist", e);
    230         }
    231
    232         // check reload mechanic
    233         if ( !"automatic".equalsIgnoreCase( reload ) && !"manual".equalsIgnoreCase( reload ) )
    234         {
    235             throw new MojoExecutionException( "invalid reload mechanic specified, must be 'automatic' or 'manual'" );
    236         }
    237         else
    238         {
    239             getLog().info("Reload Mechanic: " + reload );
    240         }
    241
    242
    243         // get the web.xml file if one has been provided, otherwise assume it is
    244         // in the webapp src directory
    245         if (getWebXml() == null )
    246             webXml = new File(new File(getWebAppSourceDirectory(),"WEB-INF"), "web.xml");
    247         setWebXmlFile(webXml);
    248
    249         try
    250         {
    251             if (!getWebXmlFile().exists())
    252                 throw new MojoExecutionException( "web.xml does not exist at location "
    253                         + webXmlFile.getCanonicalPath());
    254             else
    255                 getLog().info( "web.xml file = "
    256                         + webXmlFile.getCanonicalPath());
    257         }
    258         catch (IOException e)
    259         {
    260             throw new MojoExecutionException("web.xml does not exist", e);
    261         }
    262
    263         //check if a jetty-env.xml location has been provided, if so, it must exist
    264         if  (getJettyEnvXml() != null)
    265         {
    266             setJettyEnvXmlFile(jettyEnvXml);
    267
    268             try
    269             {
    270                 if (!getJettyEnvXmlFile().exists())
    271                     throw new MojoExecutionException("jetty-env.xml file does not exist at location "+jettyEnvXml);
    272                 else
    273                     getLog().info(" jetty-env.xml = "+getJettyEnvXmlFile().getCanonicalPath());
    274             }
    275             catch (IOException e)
    276             {
    277                 throw new MojoExecutionException("jetty-env.xml does not exist");
    278             }
    279         }
    280
    281
    282         // check the classes to form a classpath with
    283         try
    284         {
    285             //allow a webapp with no classes in it (just jsps/html)
    286             if (getClassesDirectory() != null)
    287             {
    288                 if (!getClassesDirectory().exists())
    289                     getLog().info( "Classes directory "+ getClassesDirectory().getCanonicalPath()+ " does not exist");
    290                 else
    291                     getLog().info("Classes = " + getClassesDirectory().getCanonicalPath());
    292             }
    293             else
    294                 getLog().info("Classes directory not set");
    295         }
    296         catch (IOException e)
    297         {
    298             throw new MojoExecutionException("Location of classesDirectory does not exist");
    299         }
    300
    301
    302         setExtraScanTargets(new ArrayList());
    303         if (scanTargets != null)
    304         {
    305             for (int i=0; i< scanTargets.length; i++)
    306             {
    307                 getLog().info("Added extra scan target:"+ scanTargets[i]);
    308                 getExtraScanTargets().add(scanTargets[i]);
    309             }
    310         }
    311
    312
    313         if (scanTargetPatterns!=null)
    314         {
    315             for (int i=0;i<scanTargetPatterns.length; i++)
    316             {
    317                 Iterator itor = scanTargetPatterns[i].getIncludes().iterator();
    318                 StringBuffer strbuff = new StringBuffer();
    319                 while (itor.hasNext())
    320                 {
    321                     strbuff.append((String)itor.next());
    322                     if (itor.hasNext())
    323                         strbuff.append(",");
    324                 }
    325                 String includes = strbuff.toString();
    326
    327                 itor = scanTargetPatterns[i].getExcludes().iterator();
    328                 strbuff= new StringBuffer();
    329                 while (itor.hasNext())
    330                 {
    331                     strbuff.append((String)itor.next());
    332                     if (itor.hasNext())
    333                         strbuff.append(",");
    334                 }
    335                 String excludes = strbuff.toString();
    336
    337                 try
    338                 {
    339                     List files = FileUtils.getFiles(scanTargetPatterns[i].getDirectory(), includes, excludes);
    340                     itor = files.iterator();
    341                     while (itor.hasNext())
    342                         getLog().info("Adding extra scan target from pattern: "+itor.next());
    343                     List currentTargets = getExtraScanTargets();
    344                     if(currentTargets!=null && !currentTargets.isEmpty())
    345                         currentTargets.addAll(files);
    346                     else
    347                         setExtraScanTargets(files);
    348                 }
    349                 catch (IOException e)
    350                 {
    351                     throw new MojoExecutionException(e.getMessage());
    352                 }
    353             }
    354
    355
    356         }
    357     }
    358
    359
    360
    361
    362
    363     public void configureWebApplication() throws Exception
    364     {
    365        super.configureWebApplication();
    366         setClassPathFiles(setUpClassPath());
    367         if(webAppConfig.getWebXmlFile()==null)
    368             webAppConfig.setWebXmlFile(getWebXmlFile());
    369         if(webAppConfig.getJettyEnvXmlFile()==null)
    370             webAppConfig.setJettyEnvXmlFile(getJettyEnvXmlFile());
    371         if(webAppConfig.getClassPathFiles()==null)
    372             webAppConfig.setClassPathFiles(getClassPathFiles());
    373         if(webAppConfig.getWar()==null)
    374             webAppConfig.setWar(getWebAppSourceDirectory().getCanonicalPath());
    375         getLog().info("Webapp directory = " + getWebAppSourceDirectory().getCanonicalPath());
    376
    377         webAppConfig.configure();
    378     }
    379
    380     public void configureScanner ()
    381     {
    382         // start the scanner thread (if necessary) on the main webapp
    383         final ArrayList scanList = new ArrayList();
    384         scanList.add(getWebXmlFile());
    385         if (getJettyEnvXmlFile() != null)
    386             scanList.add(getJettyEnvXmlFile());
    387         File jettyWebXmlFile = findJettyWebXmlFile(new File(getWebAppSourceDirectory(),"WEB-INF"));
    388         if (jettyWebXmlFile != null)
    389             scanList.add(jettyWebXmlFile);
    390         scanList.addAll(getExtraScanTargets());
    391         scanList.add(getProject().getFile());
    392         scanList.addAll(getClassPathFiles());
    393         setScanList(scanList);
    394         ArrayList listeners = new ArrayList();
    395         listeners.add(new Scanner.BulkListener()
    396         {
    397             public void filesChanged (List changes)
    398             {
    399                 try
    400                 {
    401                     boolean reconfigure = changes.contains(getProject().getFile().getCanonicalPath());
    402                     restartWebApp(reconfigure);
    403                 }
    404                 catch (Exception e)
    405                 {
    406                     getLog().error("Error reconfiguring/restarting webapp after change in watched files",e);
    407                 }
    408             }
    409
    410
    411         });
    412         setScannerListeners(listeners);
    413     }
    414
    415     public void restartWebApp(boolean reconfigureScanner) throws Exception
    416     {
    417         getLog().info("restarting "+webAppConfig);
    418         getLog().debug("Stopping webapp ...");
    419         webAppConfig.stop();
    420         getLog().debug("Reconfiguring webapp ...");
    421
    422         checkPomConfiguration();
    423         configureWebApplication();
    424
    425         // check if we need to reconfigure the scanner,
    426         // which is if the pom changes
    427         if (reconfigureScanner)
    428         {
    429             getLog().info("Reconfiguring scanner after change to pom.xml ...");
    430             scanList.clear();
    431             scanList.add(getWebXmlFile());
    432             if (getJettyEnvXmlFile() != null)
    433                 scanList.add(getJettyEnvXmlFile());
    434             scanList.addAll(getExtraScanTargets());
    435             scanList.add(getProject().getFile());
    436             scanList.addAll(getClassPathFiles());
    437             getScanner().setScanDirs(scanList);
    438         }
    439
    440         getLog().debug("Restarting webapp ...");
    441         webAppConfig.start();
    442         getLog().info("Restart completed at "+new Date().toString());
    443     }
    444
    445     private List getDependencyFiles ()
    446     {
    447         List dependencyFiles = new ArrayList();
    448         List overlays = new ArrayList();
    449         for ( Iterator iter = getProject().getArtifacts().iterator(); iter.hasNext(); )
    450         {
    451             Artifact artifact = (Artifact) iter.next();
    452             // Include runtime and compile time libraries, and possibly test libs too
    453             if(artifact.getType().equals("war"))
    454             {
    455                 try
    456                 {
    457                     Resource r = Resource.newResource("jar:" + artifact.getFile().toURL().toString() + "!/");
    458                     overlays.add(r);
    459                     getExtraScanTargets().add(artifact.getFile());
    460                 }
    461                 catch(Exception e)
    462                 {
    463                     throw new RuntimeException(e);
    464                 }
    465                 continue;
    466             }
    467             if (((!Artifact.SCOPE_PROVIDED.equals(artifact.getScope())) && (!Artifact.SCOPE_TEST.equals( artifact.getScope())))
    468                     ||
    469                 (useTestClasspath && Artifact.SCOPE_TEST.equals( artifact.getScope())))
    470             {
    471                 dependencyFiles.add(artifact.getFile());
    472                 getLog().debug( "Adding artifact " + artifact.getFile().getName() + " for WEB-INF/lib " );
    473             }
    474         }
    475         if(!overlays.isEmpty())
    476         {
    477             try
    478             {
    479                 Resource resource = webAppConfig.getBaseResource();
    480                 ResourceCollection rc = new ResourceCollection();
    481                 if(resource==null)
    482                 {
    483                     // nothing configured, so we automagically enable the overlays
    484                     int size = overlays.size()+1;
    485                     Resource[] resources = new Resource[size];
    486                     resources[0] = Resource.newResource(getWebAppSourceDirectory().toURL());
    487                     for(int i=1; i<size; i++)
    488                     {
    489                         resources[i] = (Resource)overlays.get(i-1);
    490                         getLog().info("Adding overlay: " + resources[i]);
    491                     }
    492                     rc.setResources(resources);
    493                 }
    494                 else
    495                 {
    496                     if(resource instanceof ResourceCollection)
    497                     {
    498                         // there was a preconfigured ResourceCollection ... append the artifact wars
    499                         Resource[] old = ((ResourceCollection)resource).getResources();
    500                         int size = old.length + overlays.size();
    501                         Resource[] resources = new Resource[size];
    502                         System.arraycopy(old, 0, resources, 0, old.length);
    503                         for(int i=old.length,j=0; i<size; i++,j++)
    504                         {
    505                             resources[i] = (Resource)overlays.get(j);
    506                             getLog().info("Adding overlay: " + resources[i]);
    507                         }
    508                         rc.setResources(resources);
    509                     }
    510                     else
    511                     {
    512                         // baseResource was already configured w/c could be src/main/webapp
    513                         if(!resource.isDirectory() && String.valueOf(resource.getFile()).endsWith(".war"))
    514                         {
    515                             // its a war
    516                             resource = Resource.newResource("jar:" + resource.getURL().toString() + "!/");
    517                         }
    518                         int size = overlays.size()+1;
    519                         Resource[] resources = new Resource[size];
    520                         resources[0] = resource;
    521                         for(int i=1; i<size; i++)
    522                         {
    523                             resources[i] = (Resource)overlays.get(i-1);
    524                             getLog().info("Adding overlay: " + resources[i]);
    525                         }
    526                         rc.setResources(resources);
    527                     }
    528                 }
    529                 webAppConfig.setBaseResource(rc);
    530             }
    531             catch(Exception e)
    532             {
    533                 throw new RuntimeException(e);
    534             }
    535         }
    536         return dependencyFiles;
    537     }
    538
    539
    540
    541
    542     private List setUpClassPath()
    543     {
    544         List classPathFiles = new ArrayList();
    545
    546         //if using the test classes, make sure they are first
    547         //on the list
    548         if (useTestClasspath && (testClassesDirectory != null))
    549             classPathFiles.add(testClassesDirectory);
    550
    551         if (getClassesDirectory() != null)
    552             classPathFiles.add(getClassesDirectory());
    553
    554         //now add all of the dependencies
    555         classPathFiles.addAll(getDependencyFiles());
    556
    557         if (getLog().isDebugEnabled())
    558         {
    559             for (int i = 0; i < classPathFiles.size(); i++)
    560             {
    561                 getLog().debug("classpath element: "+ ((File) classPathFiles.get(i)).getName());
    562             }
    563         }
    564         return classPathFiles;
    565     }
    566
    567 }