| 1 | |
package org.webslinger.commons.vfs.cow; |
| 2 | |
|
| 3 | |
import java.io.IOException; |
| 4 | |
import java.net.URLStreamHandler; |
| 5 | |
import java.util.Arrays; |
| 6 | |
import java.util.Collection; |
| 7 | |
import java.util.HashSet; |
| 8 | |
import java.util.Iterator; |
| 9 | |
import java.util.Map; |
| 10 | |
import java.util.concurrent.ConcurrentHashMap; |
| 11 | |
|
| 12 | |
import org.apache.commons.vfs.Capability; |
| 13 | |
import org.apache.commons.vfs.FileName; |
| 14 | |
import org.apache.commons.vfs.FileObject; |
| 15 | |
import org.apache.commons.vfs.FileSystem; |
| 16 | |
import org.apache.commons.vfs.FileSystemException; |
| 17 | |
import org.apache.commons.vfs.FileSystemOptions; |
| 18 | |
import org.apache.commons.vfs.provider.DefaultURLStreamHandler; |
| 19 | |
|
| 20 | |
import org.webslinger.lang.ConcurrentFreezingCache; |
| 21 | |
import org.webslinger.lang.Freezer; |
| 22 | |
import static org.webslinger.lang.ClassUtil.NULL; |
| 23 | |
import org.webslinger.commons.vfs.FileSet; |
| 24 | |
import org.webslinger.commons.vfs.FilteringFileSystem; |
| 25 | |
import org.webslinger.commons.vfs.GenerationalFileObject; |
| 26 | |
import org.webslinger.commons.vfs.GenerationalFileSystem; |
| 27 | |
|
| 28 | 388 | public class COWFileSystem extends GenerationalFileSystem<FileName, COWFileObject, COWFileSystem.COWResolution, COWFileSystem> { |
| 29 | |
protected final COWEntryFactory cowEntryFactory; |
| 30 | |
|
| 31 | 6 | protected HashSet<Capability> caps = new HashSet<Capability>(); |
| 32 | |
|
| 33 | |
public COWFileSystem(FileName name, FileObject root, FileSystemOptions options) throws FileSystemException { |
| 34 | 6 | super(name, root, options); |
| 35 | 6 | FilteringFileSystem.copyCapabilities(root.getFileSystem(), caps); |
| 36 | 6 | COWEntryFactory cowEntryFactory = COWConfigBuilder.getInstance().getCOWEntryFactory(options); |
| 37 | 6 | if (cowEntryFactory == null) cowEntryFactory = new DefaultCOWEntryFactory(); |
| 38 | 6 | this.cowEntryFactory = cowEntryFactory; |
| 39 | 6 | } |
| 40 | |
|
| 41 | 9 | public static final class DefaultCOWEntryFactory implements COWEntryFactory { |
| 42 | 9 | protected final ConcurrentHashMap<FileSystem, ConcurrentFreezingCache<COWFileObject, Object, COWState>> cowStates = new ConcurrentHashMap<FileSystem, ConcurrentFreezingCache<COWFileObject, Object, COWState>>(); |
| 43 | |
|
| 44 | |
public COWEntry getCOWEntry(COWFileSystem cfs, COWFileObject file, boolean create) throws FileSystemException { |
| 45 | |
|
| 46 | |
|
| 47 | 1402 | COWState state = getCOWState(cfs, file.getParent(), create); |
| 48 | 1402 | if (state == null) return null; |
| 49 | 1377 | return state.getEntry(file.getName().getBaseName(), create); |
| 50 | |
} |
| 51 | |
|
| 52 | |
public String[] getChildNames(COWFileSystem cfs, COWFileObject parent, boolean create) throws FileSystemException { |
| 53 | |
|
| 54 | |
|
| 55 | 63 | COWState state = getCOWState(cfs, parent, create); |
| 56 | 63 | if (state == null) return null; |
| 57 | 58 | return state.getChildNames(); |
| 58 | |
} |
| 59 | |
|
| 60 | |
protected COWState getCOWState(final COWFileSystem cfs, COWFileObject parent, boolean create) throws FileSystemException { |
| 61 | |
|
| 62 | 1465 | ConcurrentFreezingCache<COWFileObject, Object, COWState> cowStatesPerFS = cowStates.get(cfs); |
| 63 | 1465 | if (cowStatesPerFS == null && !create) return null; |
| 64 | 1435 | if (cowStatesPerFS == null) { |
| 65 | 6 | cowStatesPerFS = new ConcurrentFreezingCache<COWFileObject, Object, COWState>(COWFileSystem.class, "cowStatsPerFS", cfs.getRoot().getName().toString(), ConcurrentFreezingCache.SOFT, cowStatesPerFSFreezer) { |
| 66 | |
protected COWState createValue(Object id, Object frozen) throws Exception { |
| 67 | 29 | COWFileObject parent = (COWFileObject) id; |
| 68 | 29 | if (parent != null) return new COWState(cfs, parent.resolveFile(".cowstate.xml")); |
| 69 | 5 | return new COWState(cfs, cfs.resolveFile(".cowstate.xml")); |
| 70 | |
} |
| 71 | |
}; |
| 72 | 6 | ConcurrentFreezingCache<COWFileObject, Object, COWState> newCowStatesPerFS = cowStates.putIfAbsent(cfs, cowStatesPerFS); |
| 73 | 6 | if (newCowStatesPerFS != null) cowStatesPerFS = newCowStatesPerFS; |
| 74 | |
} |
| 75 | |
try { |
| 76 | 1435 | return cowStatesPerFS.get(parent); |
| 77 | 0 | } catch (FileSystemException e) { |
| 78 | 0 | throw e; |
| 79 | 0 | } catch (RuntimeException e) { |
| 80 | 0 | throw e; |
| 81 | 0 | } catch (Exception e) { |
| 82 | 0 | throw (InternalError) new InternalError("SNO: " + e.getMessage()).initCause(e); |
| 83 | |
} |
| 84 | |
} |
| 85 | |
} |
| 86 | |
|
| 87 | 1 | private static final Freezer<COWFileObject, Object> cowStatesPerFSFreezer = new Freezer<COWFileObject, Object>() { |
| 88 | |
public Object freeze(COWFileObject parent) { |
| 89 | 1435 | return parent != null ? parent.getName() : NULL; |
| 90 | |
} |
| 91 | |
}; |
| 92 | |
|
| 93 | |
public void addCapabilities(Collection caps) { |
| 94 | 6 | caps.addAll(this.caps); |
| 95 | 6 | } |
| 96 | |
|
| 97 | |
protected COWFileObject createFile(FileName name) { |
| 98 | 114 | return new COWFileObject(name, this); |
| 99 | |
} |
| 100 | |
|
| 101 | |
|
| 102 | |
|
| 103 | |
String[] getChildNames(COWFileObject file, boolean create) throws FileSystemException { |
| 104 | 63 | return cowEntryFactory.getChildNames(this, file, create); |
| 105 | |
} |
| 106 | |
|
| 107 | |
COWEntry getCOWEntry(COWFileObject file, boolean create) throws FileSystemException { |
| 108 | 1239 | return cowEntryFactory.getCOWEntry(this, file, create); |
| 109 | |
} |
| 110 | |
|
| 111 | |
public COWEntry getCOWEntry(String path) throws FileSystemException { |
| 112 | 163 | return cowEntryFactory.getCOWEntry(this, resolveFile(path), true); |
| 113 | |
} |
| 114 | |
|
| 115 | |
protected int newGeneration() { |
| 116 | 47 | return super.newGeneration(); |
| 117 | |
} |
| 118 | |
|
| 119 | |
protected COWResolution newResolution(int generation, COWFileObject genFile) throws FileSystemException { |
| 120 | 197 | return new COWResolution(generation, genFile); |
| 121 | |
} |
| 122 | |
|
| 123 | |
protected COWFileObject[] newArray(int length) { |
| 124 | 71 | return new COWFileObject[length]; |
| 125 | |
} |
| 126 | |
|
| 127 | |
protected FileSet getRealFiles(COWFileObject file) throws FileSystemException { |
| 128 | 0 | return createFileSet(file.getAllFiles(), new FileObject[0]); |
| 129 | |
} |
| 130 | |
|
| 131 | |
protected class COWResolution extends GenerationalFileSystem.Resolution<FileName, COWFileObject, COWResolution, COWFileSystem> { |
| 132 | |
private final COWFileObject[] files; |
| 133 | |
private final COWEntry[] cowEntries; |
| 134 | |
private final int[] cowEntrySerials; |
| 135 | |
|
| 136 | 197 | protected COWResolution(int generation, COWFileObject genFile) throws FileSystemException { |
| 137 | 197 | super(generation, genFile); |
| 138 | 197 | COWResolver resolver = new COWResolver(COWFileSystem.this); |
| 139 | 197 | resolver.resolve(genFile); |
| 140 | 197 | files = resolver.getFiles(); |
| 141 | 197 | for (FileObject file: files) file.refresh(); |
| 142 | 197 | cowEntries = resolver.getCOWEntries(); |
| 143 | 197 | cowEntrySerials = resolver.getCOWEntrySerials(); |
| 144 | 197 | } |
| 145 | |
|
| 146 | |
protected boolean refresh() throws FileSystemException { |
| 147 | 345 | int i = files[0] == file ? 1 : 0; |
| 148 | 588 | for (; i < files.length; i++) files[i].refresh(); |
| 149 | 345 | return super.refresh(); |
| 150 | |
} |
| 151 | |
|
| 152 | |
FileObject[] getFiles() { |
| 153 | 0 | return files; |
| 154 | |
} |
| 155 | |
|
| 156 | |
protected boolean isWriteable() { |
| 157 | 28 | return true; |
| 158 | |
} |
| 159 | |
|
| 160 | |
protected boolean isOutOfDate(boolean create) throws FileSystemException { |
| 161 | 870 | if (create && files.length == 0) return true; |
| 162 | |
|
| 163 | 4307 | for (int i = 0; i < cowEntries.length; i++) { |
| 164 | 3437 | if (cowEntries[i].checkSerial(cowEntrySerials[i])) return true; |
| 165 | |
} |
| 166 | 870 | return false; |
| 167 | |
} |
| 168 | |
|
| 169 | |
protected FileObject doGetFile(boolean create) throws FileSystemException { |
| 170 | 207 | COWEntry entry = getCOWEntry(file, create); |
| 171 | 207 | if (create) { |
| 172 | 48 | if (entry == null) System.err.println("null: " + file); |
| 173 | 48 | entry.setDeleted(false); |
| 174 | 48 | return files[0].getParentLayerFile(); |
| 175 | |
} |
| 176 | |
|
| 177 | 159 | if (entry != null && entry.isDeleted()) return null; |
| 178 | 230 | for (COWFileObject file: files) { |
| 179 | 179 | FileObject parentFile = file.getParentLayerFile(); |
| 180 | 179 | if (parentFile.exists()) return parentFile; |
| 181 | |
} |
| 182 | 51 | return null; |
| 183 | |
} |
| 184 | |
|
| 185 | |
protected void doDelete() throws FileSystemException { |
| 186 | 8 | getCOWEntry(file, true).setDeleted(true); |
| 187 | 8 | files[0].getParentLayerFile().delete(); |
| 188 | 8 | } |
| 189 | |
|
| 190 | |
protected String[] getChildNames() throws FileSystemException { |
| 191 | |
|
| 192 | 63 | HashSet<String> names = new HashSet<String>(); |
| 193 | 167 | for (int i = files.length - 1; i >= 0; i--) { |
| 194 | 104 | COWFileObject cowFile = files[i]; |
| 195 | 104 | FileObject fo = cowFile.getParentLayerFile(); |
| 196 | 104 | if (fo.getType().hasChildren()) { |
| 197 | 95 | fo.refresh(); |
| 198 | 95 | COWFileObject parentDir = cowFile.getParent(); |
| 199 | 95 | FileObject[] children = fo.getChildren(); |
| 200 | 224 | for (FileObject child: children) { |
| 201 | 129 | String name = child.getName().getBaseName(); |
| 202 | 129 | COWEntry childEntry = getCOWEntry(parentDir != null ? parentDir.resolveFile(name) : resolveFile(name), false); |
| 203 | 129 | if (childEntry != null && childEntry.isDeleted()) { |
| 204 | 0 | names.remove(name); |
| 205 | |
} else { |
| 206 | 129 | names.add(name); |
| 207 | |
} |
| 208 | |
} |
| 209 | 95 | } else if (fo.getType().hasContent()) { |
| 210 | 0 | names.add(fo.getName().getBaseName()); |
| 211 | |
} |
| 212 | |
} |
| 213 | 63 | String[] foundNames = COWFileSystem.this.getChildNames(file, false); |
| 214 | 63 | if (foundNames != null) names.addAll(Arrays.asList(foundNames)); |
| 215 | 63 | names.remove(".cowstate.xml"); |
| 216 | 63 | Iterator<String> it = names.iterator(); |
| 217 | 171 | while (it.hasNext()) { |
| 218 | 108 | String name = it.next(); |
| 219 | 108 | COWEntry childEntry = getCOWEntry(file.getName().getPath() + '/' + name); |
| 220 | 108 | if (childEntry.isDeleted()) it.remove(); |
| 221 | 108 | } |
| 222 | 63 | return names.toArray(new String[names.size()]); |
| 223 | |
} |
| 224 | |
} |
| 225 | |
} |
| 226 | |
|