1 package org.apache.tomcat.maven.plugin.tomcat6;
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 }