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 */ 017package org.apache.commons.vfs2.provider.ram; 018 019import java.io.BufferedOutputStream; 020import java.io.File; 021import java.io.IOException; 022import java.io.InputStream; 023import java.io.OutputStream; 024import java.io.Serializable; 025import java.util.Collection; 026import java.util.Collections; 027import java.util.HashMap; 028import java.util.Iterator; 029import java.util.Map; 030 031import org.apache.commons.vfs2.Capability; 032import org.apache.commons.vfs2.FileName; 033import org.apache.commons.vfs2.FileObject; 034import org.apache.commons.vfs2.FileSystemException; 035import org.apache.commons.vfs2.FileSystemOptions; 036import org.apache.commons.vfs2.FileType; 037import org.apache.commons.vfs2.provider.AbstractFileName; 038import org.apache.commons.vfs2.provider.AbstractFileSystem; 039 040/** 041 * A RAM File System. 042 */ 043public class RamFileSystem extends AbstractFileSystem implements Serializable { 044 private static final int BUFFER_SIZE = 512; 045 046 /** 047 * serialVersionUID format is YYYYMMDD for the date of the last binary change. 048 */ 049 private static final long serialVersionUID = 20101208L; 050 051 /** 052 * Cache of RAM File Data 053 */ 054 private final Map<FileName, RamFileData> cache; 055 056 /** 057 * @param rootName The root file name. 058 * @param fileSystemOptions The FileSystem options. 059 */ 060 protected RamFileSystem(final FileName rootName, final FileSystemOptions fileSystemOptions) { 061 super(rootName, null, fileSystemOptions); 062 this.cache = Collections.synchronizedMap(new HashMap<FileName, RamFileData>()); 063 // create root 064 final RamFileData rootData = new RamFileData(rootName); 065 rootData.setType(FileType.FOLDER); 066 rootData.setLastModified(System.currentTimeMillis()); 067 this.cache.put(rootName, rootData); 068 } 069 070 /* 071 * (non-Javadoc) 072 * 073 * @see org.apache.commons.vfs2.provider.AbstractFileSystem#createFile(org.apache.commons.vfs2.FileName) 074 */ 075 @Override 076 protected FileObject createFile(final AbstractFileName name) throws Exception { 077 return new RamFileObject(name, this); 078 } 079 080 /* 081 * (non-Javadoc) 082 * 083 * @see org.apache.commons.vfs2.provider.AbstractFileSystem#addCapabilities(java.util.Collection) 084 */ 085 @Override 086 protected void addCapabilities(final Collection<Capability> caps) { 087 caps.addAll(RamFileProvider.capabilities); 088 } 089 090 /** 091 * @param name The name of the file. 092 * @return children The names of the children. 093 */ 094 String[] listChildren(final FileName name) { 095 final RamFileData data = this.cache.get(name); 096 if (data == null || !data.getType().hasChildren()) { 097 return null; 098 } 099 final Collection<RamFileData> children = data.getChildren(); 100 String[] names; 101 102 synchronized (children) { 103 names = new String[children.size()]; 104 105 int pos = 0; 106 final Iterator<RamFileData> iter = children.iterator(); 107 while (iter.hasNext()) { 108 final RamFileData childData = iter.next(); 109 names[pos] = childData.getName().getBaseName(); 110 pos++; 111 } 112 } 113 114 return names; 115 } 116 117 /** 118 * Delete a file 119 * 120 * @param file 121 * @throws FileSystemException 122 */ 123 void delete(final RamFileObject file) throws FileSystemException { 124 // root is read only check 125 if (file.getParent() == null) { 126 throw new FileSystemException("unable to delete root"); 127 } 128 129 // Remove reference from cache 130 this.cache.remove(file.getName()); 131 // Notify the parent 132 final RamFileObject parent = (RamFileObject) this.resolveFile(file.getParent().getName()); 133 parent.getData().removeChild(file.getData()); 134 parent.close(); 135 // Close the file 136 file.getData().clear(); 137 file.close(); 138 } 139 140 /** 141 * Saves a file 142 * 143 * @param file 144 * @throws FileSystemException 145 */ 146 void save(final RamFileObject file) throws FileSystemException { 147 148 // Validate name 149 if (file.getData().getName() == null) { 150 throw new FileSystemException(new IllegalStateException("The data has no name. " + file)); 151 } 152 153 // Add to the parent 154 if (file.getName().getDepth() > 0) { 155 final RamFileData parentData = this.cache.get(file.getParent().getName()); 156 // Only if not already added 157 if (!parentData.hasChildren(file.getData())) { 158 final RamFileObject parent = (RamFileObject) file.getParent(); 159 parent.getData().addChild(file.getData()); 160 parent.close(); 161 } 162 } 163 // Store in cache 164 cache.put(file.getName(), file.getData()); 165 file.getData().updateLastModified(); 166 file.close(); 167 } 168 169 /** 170 * @param from The original file. 171 * @param to The new file. 172 * @throws FileSystemException if an error occurs. 173 */ 174 void rename(final RamFileObject from, final RamFileObject to) throws FileSystemException { 175 if (!this.cache.containsKey(from.getName())) { 176 throw new FileSystemException("File does not exist: " + from.getName()); 177 } 178 // Copy data 179 180 to.getData().setContent(from.getData().getContent()); 181 to.getData().setLastModified(from.getData().getLastModified()); 182 to.getData().setType(from.getData().getType()); 183 184 this.save(to); 185 this.delete(from); 186 } 187 188 public void attach(final RamFileObject fo) { 189 if (fo.getName() == null) { 190 throw new IllegalArgumentException("Null argument"); 191 } 192 RamFileData data = this.cache.get(fo.getName()); 193 if (data == null) { 194 data = new RamFileData(fo.getName()); 195 } 196 fo.setData(data); 197 } 198 199 /** 200 * Import a Tree. 201 * 202 * @param file The File 203 * @throws FileSystemException if an error occurs. 204 */ 205 public void importTree(final File file) throws FileSystemException { 206 final FileObject fileFo = getFileSystemManager().toFileObject(file); 207 this.toRamFileObject(fileFo, fileFo); 208 } 209 210 /** 211 * Import the given file with the name relative to the given root 212 * 213 * @param fo 214 * @param root 215 * @throws FileSystemException 216 */ 217 void toRamFileObject(final FileObject fo, final FileObject root) throws FileSystemException { 218 final RamFileObject memFo = (RamFileObject) this 219 .resolveFile(fo.getName().getPath().substring(root.getName().getPath().length())); 220 if (fo.getType().hasChildren()) { 221 // Create Folder 222 memFo.createFolder(); 223 // Import recursively 224 final FileObject[] fos = fo.getChildren(); 225 for (final FileObject child : fos) { 226 this.toRamFileObject(child, root); 227 } 228 } else if (fo.isFile()) { 229 // Read bytes 230 try { 231 final InputStream is = fo.getContent().getInputStream(); 232 try { 233 final OutputStream os = new BufferedOutputStream(memFo.getOutputStream(), BUFFER_SIZE); 234 int i; 235 while ((i = is.read()) != -1) { 236 os.write(i); 237 } 238 os.close(); 239 } finally { 240 try { 241 is.close(); 242 } catch (final IOException ignored) { 243 /* ignore on close exception. */ 244 } 245 // TODO: close os 246 } 247 } catch (final IOException e) { 248 throw new FileSystemException(e.getClass().getName() + " " + e.getMessage()); 249 } 250 } else { 251 throw new FileSystemException("File is not a folder nor a file " + memFo); 252 } 253 } 254 255 /** 256 * @return Returns the size of the FileSystem 257 */ 258 long size() { 259 long size = 0; 260 synchronized (cache) { 261 final Iterator<RamFileData> iter = cache.values().iterator(); 262 while (iter.hasNext()) { 263 final RamFileData data = iter.next(); 264 size += data.size(); 265 } 266 } 267 return size; 268 } 269 270 /** 271 * Close the RAMFileSystem. 272 */ 273 @Override 274 public void close() { 275 this.cache.clear(); 276 super.close(); 277 } 278}