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   * @since 2.0
38   */
39  public class ExternalRepositoriesReloadableWebappLoader
40      extends WebappLoader
41  {
42  
43      /**
44       * Last modification times of all jar and class files.
45       */
46      private Map<String, Long> modificationTimeMap = new HashMap<String, Long>();
47  
48      private Log log;
49  
50      /**
51       * Default Constructor.
52       */
53      public ExternalRepositoriesReloadableWebappLoader()
54      {
55          super();
56      }
57  
58  
59      /**
60       * Convenience Constructor allows setting of a parent ClassLoader.
61       *
62       * @param parent the ClassLoader instance to set as this Loader's parent ClassLoader.
63       */
64      public ExternalRepositoriesReloadableWebappLoader( ClassLoader parent, Log log )
65      {
66          super( parent );
67          this.log = log;
68      }
69  
70      /**
71       * {@inheritDoc}
72       */
73      @Override
74      public void addRepository( String repository )
75      {
76          super.addRepository( repository );
77          try
78          {
79              File file = new File( new URL( repository ).getPath().replaceAll( "%20", " " ) );
80              if ( file.isDirectory() )
81              {
82                  addClassDirectory( file );
83              }
84              else if ( file.isFile() && file.getName().endsWith( ".jar" ) )
85              {
86                  addFile( file );
87              }
88          }
89          catch ( MalformedURLException muex )
90          {
91              throw new RuntimeException( muex );
92          }
93      }
94  
95      /**
96       * Tracks modification times of files in the given class directory.
97       *
98       * @param directory the File directory to track modification times for.
99       */
100     private void addClassDirectory( File directory )
101     {
102         for ( File file : directory.listFiles() )
103         {
104             if ( file.isDirectory() )
105             {
106                 //remember also directory last modification time
107                 addFile( file );
108                 addClassDirectory( file );
109             }
110             else if ( file.isFile() )
111             {
112                 addFile( file );
113             }
114         }
115     }
116 
117     /**
118      * Tracks last modification time of the given File.
119      *
120      * @param file the File for which to track last modification time.
121      */
122     private void addFile( File file )
123     {
124         modificationTimeMap.put( file.getAbsolutePath(), file.lastModified() );
125     }
126 
127     /**
128      * Check if {@link WebappLoader} says modified(), if not then check files from added repositories.
129      */
130     @Override
131     public boolean modified()
132     {
133         boolean modified = super.modified();
134         if ( !modified )
135         {
136             if ( log != null )
137             {
138                 log.debug( "classPath scanning started at " + new Date().toString() );
139             }
140             for ( Map.Entry<String, Long> entry : modificationTimeMap.entrySet() )
141             {
142                 String key = entry.getKey();
143                 File file = new File( key );
144                 if ( file.exists() )
145                 {
146                     // file could be deleted.
147                     Long savedLastModified = modificationTimeMap.get( key );
148                     if ( file.lastModified() > savedLastModified )
149                     {
150                         modified = true;
151                         modificationTimeMap.put( key, file.lastModified() );
152 
153                         // directory last modification time can change when some class,
154                         // jar or subdirectory was added or deleted.
155                         if ( file.isDirectory() )
156                         {
157                             addClassDirectory( file );
158                         }
159                     }
160                 }
161             }
162         }
163         if ( log != null )
164         {
165             log.debug( "context " + modified + " at " + new Date().toString() );
166         }
167         return modified;
168     }
169 
170 }