View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.vfs2.provider.ram;
18  
19  import java.io.BufferedOutputStream;
20  import java.io.File;
21  import java.io.IOException;
22  import java.io.InputStream;
23  import java.io.OutputStream;
24  import java.io.Serializable;
25  import java.util.Collection;
26  import java.util.Collections;
27  import java.util.HashMap;
28  import java.util.Iterator;
29  import java.util.Map;
30  
31  import org.apache.commons.vfs2.Capability;
32  import org.apache.commons.vfs2.FileName;
33  import org.apache.commons.vfs2.FileObject;
34  import org.apache.commons.vfs2.FileSystemException;
35  import org.apache.commons.vfs2.FileSystemOptions;
36  import org.apache.commons.vfs2.FileType;
37  import org.apache.commons.vfs2.provider.AbstractFileName;
38  import org.apache.commons.vfs2.provider.AbstractFileSystem;
39  
40  /**
41   * A RAM File System.
42   */
43  public class RamFileSystem extends AbstractFileSystem implements Serializable {
44  
45      private static final int BUFFER_SIZE = 512;
46  
47      /**
48       * serialVersionUID format is YYYYMMDD for the date of the last binary change.
49       */
50      private static final long serialVersionUID = 20101208L;
51  
52      /**
53       * Cache of RAM File Data
54       */
55      private final Map<FileName, RamFileData> cache;
56  
57      /**
58       * @param rootName The root file name.
59       * @param fileSystemOptions The FileSystem options.
60       */
61      protected RamFileSystem(final FileName rootName, final FileSystemOptions fileSystemOptions) {
62          super(rootName, null, fileSystemOptions);
63          this.cache = Collections.synchronizedMap(new HashMap<FileName, RamFileData>());
64          // create root
65          final RamFileDatader/ram/RamFileData.html#RamFileData">RamFileData rootData = new RamFileData(rootName);
66          rootData.setType(FileType.FOLDER);
67          rootData.setLastModified(System.currentTimeMillis());
68          this.cache.put(rootName, rootData);
69      }
70  
71      /*
72       * (non-Javadoc)
73       *
74       * @see org.apache.commons.vfs2.provider.AbstractFileSystem#createFile(org.apache.commons.vfs2.FileName)
75       */
76      @Override
77      protected FileObject createFile(final AbstractFileName name) throws Exception {
78          return new RamFileObject(name, this);
79      }
80  
81      /*
82       * (non-Javadoc)
83       *
84       * @see org.apache.commons.vfs2.provider.AbstractFileSystem#addCapabilities(java.util.Collection)
85       */
86      @Override
87      protected void addCapabilities(final Collection<Capability> caps) {
88          caps.addAll(RamFileProvider.capabilities);
89      }
90  
91      /**
92       * @param name The name of the file.
93       * @return children The names of the children.
94       */
95      String[] listChildren(final FileName name) {
96          final RamFileData data = this.cache.get(name);
97          if (data == null || !data.getType().hasChildren()) {
98              return null;
99          }
100         final Collection<RamFileData> children = data.getChildren();
101         String[] names;
102 
103         synchronized (children) {
104             names = new String[children.size()];
105 
106             int pos = 0;
107             final Iterator<RamFileData> iter = children.iterator();
108             while (iter.hasNext()) {
109                 final RamFileData childData = iter.next();
110                 names[pos] = childData.getName().getBaseName();
111                 pos++;
112             }
113         }
114 
115         return names;
116     }
117 
118     /**
119      * Delete a file
120      *
121      * @param file
122      * @throws FileSystemException
123      */
124     void delete(final RamFileObject file) throws FileSystemException {
125         // root is read only check
126         FileSystemException.requireNonNull(file.getParent(), "unable to delete root");
127 
128         // Remove reference from cache
129         this.cache.remove(file.getName());
130         // Notify the parent
131         final RamFileObject./../../../org/apache/commons/vfs2/provider/ram/RamFileObject.html#RamFileObject">RamFileObject parent = (RamFileObject) this.resolveFile(file.getParent().getName());
132         parent.getData().removeChild(file.getData());
133         parent.close();
134         // Close the file
135         file.getData().clear();
136         file.close();
137     }
138 
139     /**
140      * Saves a file
141      *
142      * @param file
143      * @throws FileSystemException
144      */
145     void save(final RamFileObject file) throws FileSystemException {
146 
147         // Validate name
148         if (file.getData().getName() == null) {
149             throw new FileSystemException(new IllegalStateException("The data has no name. " + file));
150         }
151 
152         // Add to the parent
153         if (file.getName().getDepth() > 0) {
154             final RamFileData parentData = this.cache.get(file.getParent().getName());
155             // Only if not already added
156             if (!parentData.hasChildren(file.getData())) {
157                 final RamFileObject./../../../org/apache/commons/vfs2/provider/ram/RamFileObject.html#RamFileObject">RamFileObject parent = (RamFileObject) file.getParent();
158                 parent.getData().addChild(file.getData());
159                 parent.close();
160             }
161         }
162         // Store in cache
163         cache.put(file.getName(), file.getData());
164         file.getData().updateLastModified();
165         file.close();
166     }
167 
168     /**
169      * @param from The original file.
170      * @param to The new file.
171      * @throws FileSystemException if an error occurs.
172      */
173     void rename(final RamFileObjectider/ram/RamFileObject.html#RamFileObject">RamFileObject from, final RamFileObject to) throws FileSystemException {
174         if (!this.cache.containsKey(from.getName())) {
175             throw new FileSystemException("File does not exist: " + from.getName());
176         }
177         // Copy data
178 
179         to.getData().setContent(from.getData().getContent());
180         to.getData().setLastModified(from.getData().getLastModified());
181         to.getData().setType(from.getData().getType());
182 
183         this.save(to);
184         this.delete(from);
185     }
186 
187     public void attach(final RamFileObject fo) {
188         if (fo.getName() == null) {
189             throw new IllegalArgumentException("Null argument");
190         }
191         RamFileData data = this.cache.get(fo.getName());
192         if (data == null) {
193             data = new RamFileData(fo.getName());
194         }
195         fo.setData(data);
196     }
197 
198     /**
199      * Import a Tree.
200      *
201      * @param file The File
202      * @throws FileSystemException if an error occurs.
203      */
204     public void importTree(final File file) throws FileSystemException {
205         final FileObject fileFo = getFileSystemManager().toFileObject(file);
206         this.toRamFileObject(fileFo, fileFo);
207     }
208 
209     /**
210      * Import the given file with the name relative to the given root
211      *
212      * @param fo
213      * @param root
214      * @throws FileSystemException
215      */
216     void toRamFileObject(final FileObject/FileObject.html#FileObject">FileObject fo, final FileObject root) throws FileSystemException {
217         final RamFileObject../../../../org/apache/commons/vfs2/provider/ram/RamFileObject.html#RamFileObject">RamFileObject memFo = (RamFileObject) this
218                 .resolveFile(fo.getName().getPath().substring(root.getName().getPath().length()));
219         if (fo.getType().hasChildren()) {
220             // Create Folder
221             memFo.createFolder();
222             // Import recursively
223             final FileObject[] fos = fo.getChildren();
224             for (final FileObject child : fos) {
225                 this.toRamFileObject(child, root);
226             }
227         } else if (fo.isFile()) {
228             // Read bytes
229             try {
230                 final InputStream is = fo.getContent().getInputStream();
231                 try {
232                     final OutputStream os = new BufferedOutputStream(memFo.getOutputStream(), BUFFER_SIZE);
233                     int i;
234                     while ((i = is.read()) != -1) {
235                         os.write(i);
236                     }
237                     os.close();
238                 } finally {
239                     try {
240                         is.close();
241                     } catch (final IOException ignored) {
242                         /* ignore on close exception. */
243                     }
244                     // TODO: close os
245                 }
246             } catch (final IOException e) {
247                 throw new FileSystemException(e.getClass().getName() + " " + e.getMessage());
248             }
249         } else {
250             throw new FileSystemException("File is not a folder nor a file " + memFo);
251         }
252     }
253 
254     /**
255      * @return Returns the size of the FileSystem
256      */
257     long size() {
258         long size = 0;
259         synchronized (cache) {
260             final Iterator<RamFileData> iter = cache.values().iterator();
261             while (iter.hasNext()) {
262                 final RamFileData data = iter.next();
263                 size += data.size();
264             }
265         }
266         return size;
267     }
268 
269     /**
270      * Close the RAMFileSystem.
271      */
272     @Override
273     public void close() {
274         this.cache.clear();
275         super.close();
276     }
277 }