001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: package org.apache.commons.vfs.provider;
018:
019: import org.apache.commons.logging.Log;
020: import org.apache.commons.logging.LogFactory;
021: import org.apache.commons.vfs.CacheStrategy;
022: import org.apache.commons.vfs.Capability;
023: import org.apache.commons.vfs.FileListener;
024: import org.apache.commons.vfs.FileName;
025: import org.apache.commons.vfs.FileObject;
026: import org.apache.commons.vfs.FileSelector;
027: import org.apache.commons.vfs.FileSystem;
028: import org.apache.commons.vfs.FileSystemException;
029: import org.apache.commons.vfs.FileSystemManager;
030: import org.apache.commons.vfs.FileSystemOptions;
031: import org.apache.commons.vfs.FilesCache;
032: import org.apache.commons.vfs.VfsLog;
033: import org.apache.commons.vfs.cache.OnCallRefreshFileObject;
034: import org.apache.commons.vfs.events.AbstractFileChangeEvent;
035: import org.apache.commons.vfs.events.ChangedEvent;
036: import org.apache.commons.vfs.events.CreateEvent;
037: import org.apache.commons.vfs.events.DeleteEvent;
038: import org.apache.commons.vfs.util.Messages;
039:
040: import java.io.File;
041: import java.util.ArrayList;
042: import java.util.Collection;
043: import java.util.HashMap;
044: import java.util.HashSet;
045: import java.util.Map;
046: import java.lang.reflect.InvocationTargetException;
047:
048: /**
049: * A partial {@link org.apache.commons.vfs.FileSystem} implementation.
050: *
051: * @author <a href="mailto:adammurdoch@apache.org">Adam Murdoch</a>
052: * @version $Revision: 520070 $ $Date: 2007-03-19 12:47:13 -0700 (Mon, 19 Mar 2007) $
053: */
054: public abstract class AbstractFileSystem extends AbstractVfsComponent
055: implements FileSystem {
056: private final static Log log = LogFactory
057: .getLog(AbstractFileSystem.class);
058:
059: private final FileName rootName;
060: private FileObject parentLayer;
061: // private FileObject root;
062: private final Collection caps = new HashSet();
063:
064: /**
065: * Map from FileName to FileObject.
066: */
067: // private FilesCache files;
068: /**
069: * Map from FileName to an ArrayList of listeners for that file.
070: */
071: private final Map listenerMap = new HashMap();
072:
073: /**
074: * FileSystemOptions used for configuration
075: */
076: private final FileSystemOptions fileSystemOptions;
077:
078: /**
079: * How many fileObjects are handed out
080: */
081: private long useCount;
082:
083: private FileSystemKey cacheKey;
084:
085: /**
086: * open streams counter for this filesystem
087: */
088: private int openStreams;
089:
090: protected AbstractFileSystem(final FileName rootName,
091: final FileObject parentLayer,
092: final FileSystemOptions fileSystemOptions) {
093: // this.parentLayer = parentLayer;
094: this .parentLayer = parentLayer;
095: this .rootName = rootName;
096: this .fileSystemOptions = fileSystemOptions;
097:
098: // this.files = null;
099: }
100:
101: /**
102: * Initialises this component.
103: */
104: public void init() throws FileSystemException {
105: addCapabilities(caps);
106: }
107:
108: /**
109: * Closes this component.
110: */
111: public void close() {
112: closeCommunicationLink();
113:
114: parentLayer = null;
115: }
116:
117: /**
118: * Close the underlaying link used to access the files
119: */
120: public void closeCommunicationLink() {
121: synchronized (this ) {
122: doCloseCommunicationLink();
123: }
124: }
125:
126: /**
127: * Close the underlaying link used to access the files
128: */
129: protected void doCloseCommunicationLink() {
130: }
131:
132: /**
133: * Creates a file object. This method is called only if the requested
134: * file is not cached.
135: */
136: protected abstract FileObject createFile(final FileName name)
137: throws Exception;
138:
139: /**
140: * Adds the capabilities of this file system.
141: */
142: protected abstract void addCapabilities(Collection caps);
143:
144: /**
145: * Returns the name of the root of this file system.
146: */
147: public FileName getRootName() {
148: return rootName;
149: }
150:
151: /**
152: * Adds a file object to the cache.
153: */
154: protected void putFileToCache(final FileObject file) {
155: getCache().putFile(file);
156: // files.put(file.getName(), file);
157: }
158:
159: private FilesCache getCache() {
160: FilesCache files;
161: //if (this.files == null)
162: {
163: files = getContext().getFileSystemManager().getFilesCache();
164: if (files == null) {
165: throw new RuntimeException(
166: Messages
167: .getString("vfs.provider/files-cache-missing.error"));
168: }
169: }
170:
171: return files;
172: }
173:
174: /**
175: * Returns a cached file.
176: */
177: protected FileObject getFileFromCache(final FileName name) {
178: return getCache().getFile(this , name);
179: // return (FileObject) files.get(name);
180: }
181:
182: /**
183: * remove a cached file.
184: */
185: protected void removeFileFromCache(final FileName name) {
186: getCache().removeFile(this , name);
187: }
188:
189: /**
190: * Determines if this file system has a particular capability.
191: */
192: public boolean hasCapability(final Capability capability) {
193: return caps.contains(capability);
194: }
195:
196: /**
197: * Retrieves the attribute with the specified name. The default
198: * implementation simply throws an exception.
199: */
200: public Object getAttribute(final String attrName)
201: throws FileSystemException {
202: throw new FileSystemException(
203: "vfs.provider/get-attribute-not-supported.error");
204: }
205:
206: /**
207: * Sets the attribute with the specified name. The default
208: * implementation simply throws an exception.
209: */
210: public void setAttribute(final String attrName, final Object value)
211: throws FileSystemException {
212: throw new FileSystemException(
213: "vfs.provider/set-attribute-not-supported.error");
214: }
215:
216: /**
217: * Returns the parent layer if this is a layered file system.
218: */
219: public FileObject getParentLayer() throws FileSystemException {
220: return parentLayer;
221: }
222:
223: /**
224: * Returns the root file of this file system.
225: */
226: public FileObject getRoot() throws FileSystemException {
227: return resolveFile(rootName);
228: /*
229: if (root == null)
230: {
231: root = resolveFile(rootName);
232: }
233: return root;
234: */
235: }
236:
237: /**
238: * Finds a file in this file system.
239: */
240: public FileObject resolveFile(final String nameStr)
241: throws FileSystemException {
242: // Resolve the name, and create the file
243: final FileName name = getFileSystemManager().resolveName(
244: rootName, nameStr);
245: return resolveFile(name);
246: }
247:
248: /**
249: * Finds a file in this file system.
250: */
251: public synchronized FileObject resolveFile(final FileName name)
252: throws FileSystemException {
253: return resolveFile(name, true);
254: }
255:
256: private synchronized FileObject resolveFile(final FileName name,
257: final boolean useCache) throws FileSystemException {
258: if (!rootName.getRootURI().equals(name.getRootURI())) {
259: throw new FileSystemException(
260: "vfs.provider/mismatched-fs-for-name.error",
261: new Object[] { name, rootName, name.getRootURI() });
262: }
263:
264: // imario@apache.org ==> use getFileFromCache
265: FileObject file;
266: if (useCache) {
267: file = getFileFromCache(name);
268: } else {
269: file = null;
270: }
271: // FileObject file = (FileObject) files.get(name);
272: if (file == null) {
273: try {
274: synchronized (this ) {
275: file = createFile(name);
276: }
277: } catch (Exception e) {
278: throw new FileSystemException(
279: "vfs.provider/resolve-file.error", name, e);
280: }
281:
282: file = decorateFileObject(file);
283:
284: // imario@apache.org ==> use putFileToCache
285: if (useCache) {
286: putFileToCache(file);
287: }
288: // files.put(name, file);
289: }
290:
291: /**
292: * resync the file information if requested
293: */
294: if (getFileSystemManager().getCacheStrategy().equals(
295: CacheStrategy.ON_RESOLVE)) {
296: file.refresh();
297: }
298: return file;
299: }
300:
301: protected FileObject decorateFileObject(FileObject file)
302: throws FileSystemException {
303: if (getFileSystemManager().getCacheStrategy().equals(
304: CacheStrategy.ON_CALL)) {
305: file = new OnCallRefreshFileObject(file);
306: }
307:
308: if (getFileSystemManager().getFileObjectDecoratorConst() != null) {
309: try {
310: file = (FileObject) getFileSystemManager()
311: .getFileObjectDecoratorConst().newInstance(
312: new Object[] { file });
313: } catch (InstantiationException e) {
314: throw new FileSystemException(
315: "vfs.impl/invalid-decorator.error",
316: getFileSystemManager().getFileObjectDecorator()
317: .getName(), e);
318: } catch (IllegalAccessException e) {
319: throw new FileSystemException(
320: "vfs.impl/invalid-decorator.error",
321: getFileSystemManager().getFileObjectDecorator()
322: .getName(), e);
323: } catch (InvocationTargetException e) {
324: throw new FileSystemException(
325: "vfs.impl/invalid-decorator.error",
326: getFileSystemManager().getFileObjectDecorator()
327: .getName(), e);
328: }
329: }
330:
331: return file;
332: }
333:
334: /**
335: * Creates a temporary local copy of a file and its descendents.
336: */
337: public File replicateFile(final FileObject file,
338: final FileSelector selector) throws FileSystemException {
339: if (!file.exists()) {
340: throw new FileSystemException(
341: "vfs.provider/replicate-missing-file.error", file
342: .getName());
343: }
344:
345: try {
346: return doReplicateFile(file, selector);
347: } catch (final Exception e) {
348: throw new FileSystemException(
349: "vfs.provider/replicate-file.error",
350: file.getName(), e);
351: }
352: }
353:
354: /**
355: * Return the FileSystemOptions used to instantiate this filesystem
356: */
357: public FileSystemOptions getFileSystemOptions() {
358: return fileSystemOptions;
359: }
360:
361: /**
362: * Return the FileSystemManager used to instantiate this filesystem
363: */
364: public FileSystemManager getFileSystemManager() {
365: return getContext().getFileSystemManager();
366: // return manager;
367: }
368:
369: /**
370: * Returns the accuracy of the last modification time
371: *
372: * @return ms 0 perfectly accurate, >0 might be off by this value e.g. sftp 1000ms
373: */
374: public double getLastModTimeAccuracy() {
375: return 0;
376: }
377:
378: /**
379: * Creates a temporary local copy of a file and its descendents.
380: */
381: protected File doReplicateFile(final FileObject file,
382: final FileSelector selector) throws Exception {
383: return getContext().getReplicator().replicateFile(file,
384: selector);
385: }
386:
387: /**
388: * Adds a junction to this file system.
389: */
390: public void addJunction(final String junctionPoint,
391: final FileObject targetFile) throws FileSystemException {
392: throw new FileSystemException(
393: "vfs.provider/junctions-not-supported.error", rootName);
394: }
395:
396: /**
397: * Removes a junction from this file system.
398: */
399: public void removeJunction(final String junctionPoint)
400: throws FileSystemException {
401: throw new FileSystemException(
402: "vfs.provider/junctions-not-supported.error", rootName);
403: }
404:
405: /**
406: * Adds a listener on a file in this file system.
407: */
408: public void addListener(final FileObject file,
409: final FileListener listener) {
410: synchronized (listenerMap) {
411: ArrayList listeners = (ArrayList) listenerMap.get(file
412: .getName());
413: if (listeners == null) {
414: listeners = new ArrayList();
415: listenerMap.put(file.getName(), listeners);
416: }
417: listeners.add(listener);
418: }
419: }
420:
421: /**
422: * Removes a listener from a file in this file system.
423: */
424: public void removeListener(final FileObject file,
425: final FileListener listener) {
426: synchronized (listenerMap) {
427: final ArrayList listeners = (ArrayList) listenerMap
428: .get(file.getName());
429: if (listeners != null) {
430: listeners.remove(listener);
431: }
432: }
433: }
434:
435: /**
436: * Fires a file create event.
437: */
438: public void fireFileCreated(final FileObject file) {
439: fireEvent(new CreateEvent(file));
440: }
441:
442: /**
443: * Fires a file delete event.
444: */
445: public void fireFileDeleted(final FileObject file) {
446: fireEvent(new DeleteEvent(file));
447: }
448:
449: /**
450: * Fires a file changed event. <br />
451: * This will only happen if you monitor the file using {@link org.apache.commons.vfs.FileMonitor}.
452: */
453: public void fireFileChanged(final FileObject file) {
454: fireEvent(new ChangedEvent(file));
455: }
456:
457: /**
458: * returns true if no file is using this filesystem
459: */
460: public boolean isReleaseable() {
461: return useCount < 1;
462: }
463:
464: void freeResources() {
465: }
466:
467: /**
468: * Fires an event.
469: */
470: private void fireEvent(final AbstractFileChangeEvent event) {
471: FileListener[] fileListeners = null;
472: final FileObject file = event.getFile();
473:
474: synchronized (listenerMap) {
475: final ArrayList listeners = (ArrayList) listenerMap
476: .get(file.getName());
477: if (listeners != null) {
478: fileListeners = (FileListener[]) listeners
479: .toArray(new FileListener[listeners.size()]);
480: }
481: }
482:
483: if (fileListeners != null) {
484: for (int i = 0; i < fileListeners.length; i++) {
485: final FileListener fileListener = fileListeners[i];
486: try {
487: event.notify(fileListener);
488: } catch (final Exception e) {
489: final String message = Messages.getString(
490: "vfs.provider/notify-listener.warn", file);
491: // getLogger().warn(message, e);
492: VfsLog.warn(getLogger(), log, message, e);
493: }
494: }
495: }
496: }
497:
498: /*
499: void fileDetached(FileObject fileObject)
500: {
501: useCount--;
502: }
503:
504: void fileAttached(FileObject fileObject)
505: {
506: useCount++;
507:
508: }
509: */
510:
511: void fileObjectHanded(FileObject fileObject) {
512: useCount++;
513: }
514:
515: void fileObjectDestroyed(FileObject fileObject) {
516: useCount--;
517: }
518:
519: void setCacheKey(FileSystemKey cacheKey) {
520: this .cacheKey = cacheKey;
521: }
522:
523: FileSystemKey getCacheKey() {
524: return this .cacheKey;
525: }
526:
527: void streamOpened() {
528: synchronized (this ) {
529: openStreams++;
530: }
531: }
532:
533: void streamClosed() {
534: synchronized (this ) {
535: if (openStreams > 0) {
536: openStreams--;
537: if (openStreams < 1) {
538: notifyAllStreamsClosed();
539: }
540: }
541: }
542: }
543:
544: /**
545: * will be called after all file-objects closed their streams.
546: */
547: protected void notifyAllStreamsClosed() {
548: }
549:
550: /**
551: * check if this filesystem has open streams
552: */
553: public boolean isOpen() {
554: synchronized (this ) {
555: return openStreams > 0;
556: }
557: }
558: }
|