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 * @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 }