KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > excalibur > source > impl > FileSource


1 /*
2  * Copyright 2002-2004 The Apache Software Foundation
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
12  * implied.
13  *
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */

17 package org.apache.excalibur.source.impl;
18
19 import java.io.File JavaDoc;
20 import java.io.FileInputStream JavaDoc;
21 import java.io.FileNotFoundException JavaDoc;
22 import java.io.FileOutputStream JavaDoc;
23 import java.io.IOException JavaDoc;
24 import java.io.InputStream JavaDoc;
25 import java.io.OutputStream JavaDoc;
26 import java.net.MalformedURLException JavaDoc;
27 import java.net.URLConnection JavaDoc;
28 import java.util.Arrays JavaDoc;
29 import java.util.Collection JavaDoc;
30 import java.util.ConcurrentModificationException JavaDoc;
31
32 import org.apache.excalibur.source.ModifiableSource;
33 import org.apache.excalibur.source.ModifiableTraversableSource;
34 import org.apache.excalibur.source.MoveableSource;
35 import org.apache.excalibur.source.Source;
36 import org.apache.excalibur.source.SourceException;
37 import org.apache.excalibur.source.SourceNotFoundException;
38 import org.apache.excalibur.source.SourceUtil;
39 import org.apache.excalibur.source.SourceValidity;
40 import org.apache.excalibur.source.impl.validity.FileTimeStampValidity;
41
42 /**
43  * A {@link ModifiableTraversableSource} for filesystem objects.
44  *
45  * @author <a HREF="mailto:dev@avalon.apache.org">Avalon Development Team</a>
46  * @version $Id: FileSource.java,v 1.5 2004/02/28 11:47:24 cziegeler Exp $
47  */

48
49 public class FileSource implements ModifiableTraversableSource, MoveableSource
50 {
51
52     /** The file */
53     private File JavaDoc m_file;
54
55     /** The scheme */
56     private String JavaDoc m_scheme;
57
58     /** The URI of this source */
59     private String JavaDoc m_uri;
60
61     /**
62      * Builds a FileSource given an URI, which doesn't necessarily have to start with "file:"
63      * @param uri
64      * @throws SourceException
65      * @throws MalformedURLException
66      */

67     public FileSource(String JavaDoc uri) throws SourceException, MalformedURLException JavaDoc
68     {
69         int pos = SourceUtil.indexOfSchemeColon(uri);
70         if (pos == -1)
71         {
72             throw new MalformedURLException JavaDoc("Invalid URI : " + uri);
73         }
74
75         String JavaDoc scheme = uri.substring(0, pos);
76         String JavaDoc fileName = uri.substring(pos + 1);
77         fileName = SourceUtil.decodePath(fileName);
78         init(scheme, new File JavaDoc(fileName));
79     }
80
81     /**
82      * Builds a FileSource, given an URI scheme and a File.
83      *
84      * @param scheme
85      * @param file
86      * @throws SourceException
87      */

88     public FileSource(String JavaDoc scheme, File JavaDoc file) throws SourceException
89     {
90         init(scheme, file);
91     }
92
93     private void init(String JavaDoc scheme, File JavaDoc file) throws SourceException
94     {
95         m_scheme = scheme;
96
97         String JavaDoc uri;
98         try
99         {
100             uri = file.toURL().toExternalForm();
101         }
102         catch (MalformedURLException JavaDoc mue)
103         {
104             // Can this really happen ?
105
throw new SourceException("Failed to get URL for file " + file, mue);
106         }
107
108         if (!uri.startsWith(scheme))
109         {
110             // Scheme is not "file:"
111
uri = scheme + ':' + uri.substring(uri.indexOf(':') + 1);
112         }
113
114         m_uri = uri;
115
116         m_file = file;
117     }
118
119     /**
120      * Get the associated file
121      */

122     public File JavaDoc getFile()
123     {
124         return m_file;
125     }
126
127     //----------------------------------------------------------------------------------
128
// Source interface methods
129
//----------------------------------------------------------------------------------
130

131     /**
132      * @see org.apache.excalibur.source.Source#getContentLength()
133      */

134     public long getContentLength()
135     {
136         return m_file.length();
137     }
138
139     /**
140      * @see org.apache.excalibur.source.Source#getInputStream()
141      */

142     public InputStream JavaDoc getInputStream() throws IOException JavaDoc, SourceNotFoundException
143     {
144         try
145         {
146             return new FileInputStream JavaDoc(m_file);
147         }
148         catch (FileNotFoundException JavaDoc fnfe)
149         {
150             throw new SourceNotFoundException(m_uri + " doesn't exist.", fnfe);
151         }
152     }
153
154     /**
155      * @see org.apache.excalibur.source.Source#getLastModified()
156      */

157     public long getLastModified()
158     {
159         return m_file.lastModified();
160     }
161
162     /**
163      * @see org.apache.excalibur.source.Source#getMimeType()
164      */

165     public String JavaDoc getMimeType()
166     {
167         return URLConnection.getFileNameMap().getContentTypeFor(m_file.getName());
168     }
169
170     /* (non-Javadoc)
171      * @see org.apache.excalibur.source.Source#getScheme()
172      */

173     public String JavaDoc getScheme()
174     {
175         return m_scheme;
176
177     }
178
179     /* (non-Javadoc)
180      * @see org.apache.excalibur.source.Source#getURI()
181      */

182     public String JavaDoc getURI()
183     {
184         return m_uri;
185     }
186
187     /**
188      * Return a validity object based on the file's modification date.
189      *
190      * @see org.apache.excalibur.source.Source#getValidity()
191      */

192     public SourceValidity getValidity()
193     {
194         if (m_file.exists())
195         {
196             return new FileTimeStampValidity(m_file);
197         }
198         else
199         {
200             return null;
201         }
202     }
203
204     /**
205      * @see org.apache.excalibur.source.Source#refresh()
206      */

207     public void refresh()
208     {
209         // Nothing to do...
210
}
211
212     /**
213      * Does this source actually exist ?
214      *
215      * @return true if the resource exists.
216      */

217     public boolean exists()
218     {
219         return getFile().exists();
220     }
221
222     //----------------------------------------------------------------------------------
223
// TraversableSource interface methods
224
//----------------------------------------------------------------------------------
225

226     /**
227      * @see org.apache.excalibur.source.TraversableSource#getChild(java.lang.String)
228      */

229     public Source getChild(String JavaDoc name) throws SourceException
230     {
231         if (!m_file.isDirectory())
232         {
233             throw new SourceException(getURI() + " is not a directory");
234         }
235
236         return new FileSource(this.getScheme(), new File JavaDoc(m_file, name));
237
238     }
239
240     /**
241      * @see org.apache.excalibur.source.TraversableSource#getChildren()
242      */

243     public Collection JavaDoc getChildren() throws SourceException
244     {
245
246         if (!m_file.isDirectory())
247         {
248             throw new SourceException(getURI() + " is not a directory");
249         }
250
251         // Build a FileSource object for each of the children
252
File JavaDoc[] files = m_file.listFiles();
253
254         FileSource[] children = new FileSource[files.length];
255         for (int i = 0; i < files.length; i++)
256         {
257             children[i] = new FileSource(this.getScheme(), files[i]);
258         }
259
260         // Return it as a list
261
return Arrays.asList(children);
262     }
263
264     /**
265      * @see org.apache.excalibur.source.TraversableSource#getName()
266      */

267     public String JavaDoc getName()
268     {
269         return m_file.getName();
270     }
271
272     /**
273      * @see org.apache.excalibur.source.TraversableSource#getParent()
274      */

275     public Source getParent() throws SourceException
276     {
277         return new FileSource(getScheme(), m_file.getParentFile());
278     }
279
280     /**
281      * @see org.apache.excalibur.source.TraversableSource#isCollection()
282      */

283     public boolean isCollection()
284     {
285         return m_file.isDirectory();
286     }
287
288     //----------------------------------------------------------------------------------
289
// ModifiableSource interface methods
290
//----------------------------------------------------------------------------------
291

292     /**
293      * Get an <code>InputStream</code> where raw bytes can be written to.
294      * The signification of these bytes is implementation-dependent and
295      * is not restricted to a serialized XML document.
296      *
297      * The output stream returned actually writes to a temp file that replaces
298      * the real one on close. This temp file is used as lock to forbid multiple
299      * simultaneous writes. The real file is updated atomically when the output
300      * stream is closed.
301      *
302      * @return a stream to write to
303      * @throws ConcurrentModificationException if another thread is currently
304      * writing to this file.
305      */

306     public OutputStream JavaDoc getOutputStream() throws IOException JavaDoc
307     {
308         // Create a temp file. It will replace the right one when writing terminates,
309
// and serve as a lock to prevent concurrent writes.
310
File JavaDoc tmpFile = new File JavaDoc(getFile().getPath() + ".tmp");
311
312         // Ensure the directory exists
313
tmpFile.getParentFile().mkdirs();
314
315         // Can we write the file ?
316
if (getFile().exists() && !getFile().canWrite())
317         {
318             throw new IOException JavaDoc("Cannot write to file " + getFile().getPath());
319         }
320
321         // Check if it temp file already exists, meaning someone else currently writing
322
if (!tmpFile.createNewFile())
323         {
324             throw new ConcurrentModificationException JavaDoc(
325                 "File " + getFile().getPath() + " is already being written by another thread");
326         }
327
328         // Return a stream that will rename the temp file on close.
329
return new FileSourceOutputStream(tmpFile, this);
330     }
331
332     /**
333      * Can the data sent to an <code>OutputStream</code> returned by
334      * {@link #getOutputStream()} be cancelled ?
335      *
336      * @return true if the stream can be cancelled
337      */

338     public boolean canCancel(OutputStream JavaDoc stream)
339     {
340         if (stream instanceof FileSourceOutputStream)
341         {
342             FileSourceOutputStream fsos = (FileSourceOutputStream) stream;
343             if (fsos.getSource() == this)
344             {
345                 return fsos.canCancel();
346             }
347         }
348
349         // Not a valid stream for this source
350
throw new IllegalArgumentException JavaDoc("The stream is not associated to this source");
351     }
352
353     /**
354      * Cancel the data sent to an <code>OutputStream</code> returned by
355      * {@link #getOutputStream()}.
356      * <p>
357      * After cancel, the stream should no more be used.
358      */

359     public void cancel(OutputStream JavaDoc stream) throws SourceException
360     {
361         if (stream instanceof FileSourceOutputStream)
362         {
363             FileSourceOutputStream fsos = (FileSourceOutputStream) stream;
364             if (fsos.getSource() == this)
365             {
366                 try
367                 {
368                     fsos.cancel();
369                 }
370                 catch (Exception JavaDoc e)
371                 {
372                     throw new SourceException("Exception during cancel.", e);
373                 }
374                 return;
375             }
376         }
377
378         // Not a valid stream for this source
379
throw new IllegalArgumentException JavaDoc("The stream is not associated to this source");
380     }
381
382     /**
383      * Delete the source.
384      */

385     public void delete() throws SourceException
386     {
387         if (!m_file.exists())
388         {
389             throw new SourceNotFoundException("Cannot delete non-existing file " + m_file.toString());
390         }
391         
392         if (!m_file.delete())
393         {
394             throw new SourceException("Could not delete " + m_file.toString() + " (unknown reason)");
395         }
396     }
397
398     //----------------------------------------------------------------------------------
399
// ModifiableTraversableSource interface methods
400
//----------------------------------------------------------------------------------
401

402     /**
403      * @see org.apache.excalibur.source.ModifiableTraversableSource#makeCollection()
404      */

405     public void makeCollection() throws SourceException
406     {
407         m_file.mkdirs();
408     }
409
410     //----------------------------------------------------------------------------------
411
// MoveableSource interface methods
412
//----------------------------------------------------------------------------------
413

414     /**
415      * @see org.apache.excalibur.source.MoveableSource#copyTo(org.apache.excalibur.source.Source)
416      */

417     public void copyTo(Source destination) throws SourceException
418     {
419         try
420         {
421             SourceUtil.copy(this.getInputStream(), ((ModifiableSource) destination).getOutputStream());
422         }
423         catch (IOException JavaDoc ioe)
424         {
425             throw new SourceException("Couldn't copy " + getURI() + " to " + destination.getURI(), ioe);
426         }
427     }
428
429     /**
430      * @see org.apache.excalibur.source.MoveableSource#moveTo(org.apache.excalibur.source.Source)
431      */

432     public void moveTo(Source destination) throws SourceException
433     {
434         if (destination instanceof FileSource)
435         {
436             final File JavaDoc dest = ((FileSource) destination).getFile();
437             final File JavaDoc parent = dest.getParentFile();
438
439             if (parent != null)
440             {
441                 parent.mkdirs(); // ensure parent directories exist
442
}
443
444             if (!m_file.renameTo(dest))
445             {
446                 throw new SourceException("Couldn't move " + getURI() + " to " + destination.getURI());
447             }
448         }
449         else
450         {
451             SourceUtil.move(this, destination);
452         }
453
454     }
455
456     //----------------------------------------------------------------------------------
457
// Private helper class for ModifiableSource implementation
458
//----------------------------------------------------------------------------------
459

460     /**
461      * A file outputStream that will rename the temp file to the destination file upon close()
462      * and discard the temp file upon cancel().
463      */

464     private static class FileSourceOutputStream extends FileOutputStream JavaDoc
465     {
466
467         private File JavaDoc m_tmpFile;
468         private boolean m_isClosed = false;
469         private FileSource m_source;
470
471         public FileSourceOutputStream(File JavaDoc tmpFile, FileSource source) throws IOException JavaDoc
472         {
473             super(tmpFile);
474             m_tmpFile = tmpFile;
475             m_source = source;
476         }
477
478         public void close() throws IOException JavaDoc
479         {
480             if (!m_isClosed)
481             {
482                 super.close();
483                 try
484                 {
485                     // Delete destination file
486
if (m_source.getFile().exists())
487                     {
488                         m_source.getFile().delete();
489                     }
490                     // Rename temp file to destination file
491
if (!m_tmpFile.renameTo(m_source.getFile()))
492                     {
493                        throw new IOException JavaDoc("Could not rename " +
494                          m_tmpFile.getAbsolutePath() +
495                          " to " + m_source.getFile().getAbsolutePath());
496                     }
497
498                 }
499                 finally
500                 {
501                     // Ensure temp file is deleted, ie lock is released.
502
// If there was a failure above, written data is lost.
503
if (m_tmpFile.exists())
504                     {
505                         m_tmpFile.delete();
506                     }
507                     m_isClosed = true;
508                 }
509             }
510
511         }
512
513         public boolean canCancel()
514         {
515             return !m_isClosed;
516         }
517
518         public void cancel() throws Exception JavaDoc
519         {
520             if (m_isClosed)
521             {
522                 throw new IllegalStateException JavaDoc("Cannot cancel : outputstrem is already closed");
523             }
524
525             m_isClosed = true;
526             super.close();
527             m_tmpFile.delete();
528         }
529
530         public void finalize()
531         {
532             if (!m_isClosed && m_tmpFile.exists())
533             {
534                 // Something wrong happened while writing : delete temp file
535
m_tmpFile.delete();
536             }
537         }
538
539         public FileSource getSource()
540         {
541             return m_source;
542         }
543     }
544 }
545
Popular Tags