View Javadoc

1   package org.apache.tomcat.maven.common.run;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import org.apache.catalina.loader.WebappLoader;
23  import org.apache.maven.plugin.logging.Log;
24  
25  import java.io.File;
26  import java.net.MalformedURLException;
27  import java.net.URL;
28  import java.util.Date;
29  import java.util.HashMap;
30  import java.util.Map;
31  
32  /**
33   * A {@linkplain WebappLoader} implementation that allows scanning for changes to project classpath in support of
34   * context reloads.
35   *
36   * @author Ryan Connolly
37   * @version $Id: ExternalRepositoriesReloadableWebappLoader.html 1305321 2012-03-26 12:01:12Z olamy $
38   * @since 2.0
39   */
40  public class ExternalRepositoriesReloadableWebappLoader
41      extends WebappLoader
42  {
43  
44      /**
45       * Last modification times of all jar and class files.
46       */
47      private Map<String, Long> modificationTimeMap = new HashMap<String, Long>();
48  
49      private Log log;
50  
51      /**
52       * Default Constructor.
53       */
54      public ExternalRepositoriesReloadableWebappLoader()
55      {
56          super();
57      }
58  
59  
60      /**
61       * Convenience Constructor allows setting of a parent ClassLoader.
62       *
63       * @param parent the ClassLoader instance to set as this Loader's parent ClassLoader.
64       */
65      public ExternalRepositoriesReloadableWebappLoader( ClassLoader parent, Log log )
66      {
67          super( parent );
68          this.log = log;
69      }
70  
71      /**
72       * {@inheritDoc}
73       */
74      @Override
75      public void addRepository( String repository )
76      {
77          super.addRepository( repository );
78          try
79          {
80              File file = new File( new URL( repository ).getPath().replaceAll( "%20", " " ) );
81              if ( file.isDirectory() )
82              {
83                  addClassDirectory( file );
84              }
85              else if ( file.isFile() && file.getName().endsWith( ".jar" ) )
86              {
87                  addFile( file );
88              }
89          }
90          catch ( MalformedURLException muex )
91          {
92              throw new RuntimeException( muex );
93          }
94      }
95  
96      /**
97       * Tracks modification times of files in the given class directory.
98       *
99       * @param directory the File directory to track modification times for.
100      */
101     private void addClassDirectory( File directory )
102     {
103         for ( File file : directory.listFiles() )
104         {
105             if ( file.isDirectory() )
106             {
107                 //remember also directory last modification time
108                 addFile( file );
109                 addClassDirectory( file );
110             }
111             else if ( file.isFile() )
112             {
113                 addFile( file );
114             }
115         }
116     }
117 
118     /**
119      * Tracks last modification time of the given File.
120      *
121      * @param file the File for which to track last modification time.
122      */
123     private void addFile( File file )
124     {
125         modificationTimeMap.put( file.getAbsolutePath(), file.lastModified() );
126     }
127 
128     /**
129      * Check if {@link WebappLoader} says modified(), if not then check files from added repositories.
130      */
131     @Override
132     public boolean modified()
133     {
134         boolean modified = super.modified();
135         if ( !modified )
136         {
137             if ( log != null )
138             {
139                 log.debug( "classPath scanning started at " + new Date().toString() );
140             }
141             for ( Map.Entry<String, Long> entry : modificationTimeMap.entrySet() )
142             {
143                 String key = entry.getKey();
144                 File file = new File( key );
145                 if ( file.exists() )
146                 {
147                     // file could be deleted.
148                     Long savedLastModified = modificationTimeMap.get( key );
149                     if ( file.lastModified() > savedLastModified )
150                     {
151                         modified = true;
152                         modificationTimeMap.put( key, file.lastModified() );
153 
154                         // directory last modification time can change when some class,
155                         // jar or subdirectory was added or deleted.
156                         if ( file.isDirectory() )
157                         {
158                             addClassDirectory( file );
159                         }
160                     }
161                 }
162             }
163         }
164         if ( log != null )
165         {
166             log.debug( "context " + modified + " at " + new Date().toString() );
167         }
168         return modified;
169     }
170 
171 }