1 package org.apache.maven.plugin.clean;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import java.io.File;
23 import java.io.IOException;
24
25 import org.apache.maven.plugin.logging.Log;
26 import org.apache.maven.shared.utils.Os;
27 import org.apache.maven.shared.utils.io.FileUtils;
28
29
30
31
32
33
34 class Cleaner
35 {
36
37 private static final boolean ON_WINDOWS = Os.isFamily( Os.FAMILY_WINDOWS );
38
39 private final Logger logDebug;
40
41 private final Logger logInfo;
42
43 private final Logger logVerbose;
44
45 private final Logger logWarn;
46
47
48
49
50
51
52
53 public Cleaner( final Log log, boolean verbose )
54 {
55 logDebug = ( log == null || !log.isDebugEnabled() ) ? null : new Logger()
56 {
57 public void log( CharSequence message )
58 {
59 log.debug( message );
60 }
61 };
62
63 logInfo = ( log == null || !log.isInfoEnabled() ) ? null : new Logger()
64 {
65 public void log( CharSequence message )
66 {
67 log.info( message );
68 }
69 };
70
71 logWarn = ( log == null || !log.isWarnEnabled() ) ? null : new Logger()
72 {
73 public void log( CharSequence message )
74 {
75 log.warn( message );
76 }
77 };
78
79 logVerbose = verbose ? logInfo : logDebug;
80 }
81
82
83
84
85
86
87
88
89
90
91
92
93
94 public void delete( File basedir, Selector selector, boolean followSymlinks, boolean failOnError,
95 boolean retryOnError )
96 throws IOException
97 {
98 if ( !basedir.isDirectory() )
99 {
100 if ( !basedir.exists() )
101 {
102 if ( logDebug != null )
103 {
104 logDebug.log( "Skipping non-existing directory " + basedir );
105 }
106 return;
107 }
108 throw new IOException( "Invalid base directory " + basedir );
109 }
110
111 if ( logInfo != null )
112 {
113 logInfo.log( "Deleting " + basedir + ( selector != null ? " (" + selector + ")" : "" ) );
114 }
115
116 File file = followSymlinks ? basedir : basedir.getCanonicalFile();
117
118 delete( file, "", selector, followSymlinks, failOnError, retryOnError );
119 }
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136 private Result delete( File file, String pathname, Selector selector, boolean followSymlinks, boolean failOnError,
137 boolean retryOnError )
138 throws IOException
139 {
140 Result result = new Result();
141
142 boolean isDirectory = file.isDirectory();
143
144 if ( isDirectory )
145 {
146 if ( selector == null || selector.couldHoldSelected( pathname ) )
147 {
148 final boolean isSymlink = FileUtils.isSymbolicLink( file );
149 File canonical = followSymlinks ? file : file.getCanonicalFile();
150 if ( followSymlinks || !isSymlink )
151 {
152 String[] filenames = canonical.list();
153 if ( filenames != null )
154 {
155 String prefix = ( pathname.length() > 0 ) ? pathname + File.separatorChar : "";
156 for ( int i = filenames.length - 1; i >= 0; i-- )
157 {
158 String filename = filenames[i];
159 File child = new File( canonical, filename );
160 result.update( delete( child, prefix + filename, selector, followSymlinks, failOnError,
161 retryOnError ) );
162 }
163 }
164 }
165 else if ( logDebug != null )
166 {
167 logDebug.log( "Not recursing into symlink " + file );
168 }
169 }
170 else if ( logDebug != null )
171 {
172 logDebug.log( "Not recursing into directory without included files " + file );
173 }
174 }
175
176 if ( !result.excluded && ( selector == null || selector.isSelected( pathname ) ) )
177 {
178 if ( logVerbose != null )
179 {
180 if ( isDirectory )
181 {
182 logVerbose.log( "Deleting directory " + file );
183 }
184 else if ( file.exists() )
185 {
186 logVerbose.log( "Deleting file " + file );
187 }
188 else
189 {
190 logVerbose.log( "Deleting dangling symlink " + file );
191 }
192 }
193 result.failures += delete( file, failOnError, retryOnError );
194 }
195 else
196 {
197 result.excluded = true;
198 }
199
200 return result;
201 }
202
203
204
205
206
207
208
209
210
211
212
213 private int delete( File file, boolean failOnError, boolean retryOnError )
214 throws IOException
215 {
216 if ( !file.delete() )
217 {
218 boolean deleted = false;
219
220 if ( retryOnError )
221 {
222 if ( ON_WINDOWS )
223 {
224
225 System.gc();
226 }
227
228 final int[] delays = { 50, 250, 750 };
229 for ( int i = 0; !deleted && i < delays.length; i++ )
230 {
231 try
232 {
233 Thread.sleep( delays[i] );
234 }
235 catch ( InterruptedException e )
236 {
237
238 }
239 deleted = file.delete() || !file.exists();
240 }
241 }
242 else
243 {
244 deleted = !file.exists();
245 }
246
247 if ( !deleted )
248 {
249 if ( failOnError )
250 {
251 throw new IOException( "Failed to delete " + file );
252 }
253 else
254 {
255 if ( logWarn != null )
256 {
257 logWarn.log( "Failed to delete " + file );
258 }
259 return 1;
260 }
261 }
262 }
263
264 return 0;
265 }
266
267 private static class Result
268 {
269
270 private int failures;
271
272 private boolean excluded;
273
274 public void update( Result result )
275 {
276 failures += result.failures;
277 excluded |= result.excluded;
278 }
279
280 }
281
282 private interface Logger
283 {
284
285 void log( CharSequence message );
286
287 }
288
289 }