Coverage Report - org.webslinger.commons.vfs.cow.COWState
 
Classes in this File Line Coverage Branch Coverage Complexity
COWState
85%
125/147
90%
19/21
0
COWState$COWEntryCache
100%
5/5
N/A
0
 
 1  
 package org.webslinger.commons.vfs.cow;
 2  
 
 3  
 import java.io.InputStream;
 4  
 import java.io.IOException;
 5  
 import java.io.OutputStream;
 6  
 import java.util.ArrayList;
 7  
 import java.util.Arrays;
 8  
 import java.util.HashMap;
 9  
 import java.util.HashSet;
 10  
 import java.util.Map;
 11  
 import java.util.Queue;
 12  
 import javax.xml.parsers.DocumentBuilder;
 13  
 import javax.xml.parsers.DocumentBuilderFactory;
 14  
 import javax.xml.parsers.ParserConfigurationException;
 15  
 import javax.xml.transform.OutputKeys;
 16  
 import javax.xml.transform.Transformer;
 17  
 import javax.xml.transform.TransformerFactory;
 18  
 import javax.xml.transform.dom.DOMSource;
 19  
 import javax.xml.transform.stream.StreamResult;
 20  
 
 21  
 import org.w3c.dom.Document;
 22  
 import org.w3c.dom.Element;
 23  
 import org.w3c.dom.Node;
 24  
 import org.xml.sax.SAXException;
 25  
 import org.xml.sax.SAXParseException;
 26  
 
 27  
 import org.apache.commons.collections.CollectionUtils;
 28  
 import org.apache.commons.vfs.FileChangeEvent;
 29  
 import org.apache.commons.vfs.FileName;
 30  
 import org.apache.commons.vfs.FileObject;
 31  
 import org.apache.commons.vfs.FileSystem;
 32  
 import org.apache.commons.vfs.FileSystemException;
 33  
 
 34  
 import org.webslinger.commons.vfs.VFSUtil;
 35  
 import org.webslinger.lang.AtomicMap;
 36  
 import org.webslinger.lang.ConcurrentCache;
 37  
 import org.webslinger.util.GeneratedResult;
 38  
 import org.webslinger.util.TTLCachedObject;
 39  
 import org.webslinger.util.TTLObject;
 40  
 import org.webslinger.xml.XmlUtil;
 41  
 
 42  193
 public class COWState extends TTLCachedObject<COWState.COWEntryCache> {
 43  
     private static final Queue<DocumentBuilder> xmlBuilders;
 44  
 
 45  
     static {
 46  1
         DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
 47  1
         builderFactory.setCoalescing(true);
 48  1
         builderFactory.setExpandEntityReferences(true);
 49  1
         builderFactory.setIgnoringComments(true);
 50  1
         builderFactory.setIgnoringElementContentWhitespace(true);
 51  1
         builderFactory.setNamespaceAware(false);
 52  1
         builderFactory.setValidating(false);
 53  
         //builderFactory.setXIncludeAware(false);
 54  
         try {
 55  1
             xmlBuilders = XmlUtil.createPool(builderFactory, 20);
 56  0
         } catch (RuntimeException e) {
 57  0
             throw e;
 58  0
         } catch (Exception e) {
 59  0
             throw (InternalError) new InternalError("loading xmlBuilders").initCause(e);
 60  1
         }
 61  1
         TTLObject.setDefaultTTLForClass(COWState.class, 1000);
 62  1
     }
 63  
 
 64  
     private final COWFileSystem cfs;
 65  
     private final FileSystem fileSystem;
 66  
     private final FileName fileName;
 67  
     private final COWFileObject dir;
 68  
     private long lastModified;
 69  
 
 70  29
     public COWState(COWFileSystem cfs, COWFileObject cowFile) throws IOException {
 71  29
         this.cfs = cfs;
 72  29
         FileObject file = cowFile.getParentLayerFile();
 73  29
         fileName = file.getName();
 74  29
         fileSystem = file.getFileSystem();
 75  29
         dir = cowFile.getParent();
 76  29
     }
 77  
 
 78  15
     public COWState(FileObject file) throws IOException {
 79  15
         cfs = null;
 80  15
         this.fileName = file.getName();
 81  15
         this.fileSystem = file.getFileSystem();
 82  15
         dir = null;
 83  15
     }
 84  
 
 85  
     protected COWFileObject getDir() throws FileSystemException {
 86  103
         return dir;
 87  
     }
 88  
 
 89  
     protected COWEntryCache getInitial() {
 90  66
         return new COWEntryCache();
 91  
     }
 92  
 
 93  
     COWFileSystem getCOWFileSystem() {
 94  100
         return cfs;
 95  
     }
 96  
 
 97  
     public FileName getFileName() {
 98  0
         return fileName;
 99  
     }
 100  
 
 101  
     public void clear() {
 102  
         try {
 103  0
             getObject().clear();
 104  0
         } catch (IOException e) {
 105  0
         }
 106  0
     }
 107  
 
 108  
     public COWEntry getEntry(String name, boolean create) throws FileSystemException {
 109  
         try {
 110  1383
             COWEntryCache cache = super.getObject();
 111  1383
             return cache != null ? cache.get(name) : null;
 112  0
         } catch (Exception e) {
 113  0
             throw VFSUtil.makeFileSystemException(e);
 114  
         }
 115  
     }
 116  
 
 117  
     private COWEntryCache getCache() throws FileSystemException {
 118  
         try {
 119  65
             return super.getObject();
 120  2
         } catch (Exception e) {
 121  2
             throw VFSUtil.makeFileSystemException(e);
 122  
         }
 123  
     }
 124  
 
 125  
     public String[] getChildNames() throws FileSystemException {
 126  58
         HashSet<String> names = new HashSet<String>();
 127  58
         COWEntryCache cache = getCache();
 128  58
         for (String name: cache.keys()) {
 129  
             try {
 130  152
                 COWEntryImpl entry = cache.get(name);
 131  152
                 if (entry.isEmpty()) continue;
 132  52
                 names.add(name);
 133  0
             } catch (Exception e) {
 134  0
                 throw VFSUtil.makeFileSystemException(e);
 135  52
             }
 136  
         }
 137  58
         return names.toArray(new String[names.size()]);
 138  
     }
 139  
 
 140  
     public String[] getDeletedEntries() throws FileSystemException {
 141  7
         HashSet<String> deleted = new HashSet<String>();
 142  7
         COWEntryCache cache = getCache();
 143  5
         for (String name: cache.keys()) {
 144  
             try {
 145  6
                 COWEntryImpl entry = cache.get(name);
 146  6
                 if (!entry.isDeleted()) continue;
 147  4
                 deleted.add(name);
 148  0
             } catch (Exception e) {
 149  0
                 throw VFSUtil.makeFileSystemException(e);
 150  4
             }
 151  
         }
 152  5
         return deleted.toArray(new String[deleted.size()]);
 153  
     }
 154  
 
 155  
     protected void onChange() {
 156  
         // if local per-entry caching was occuring, this would update such code
 157  0
     }
 158  
 
 159  
     public FileObject getFile() throws FileSystemException {
 160  145
         return fileSystem.resolveFile(fileName);
 161  
     }
 162  
 
 163  
     protected long getTimestamp(COWEntryCache old) throws FileSystemException {
 164  39
         FileObject file = getFile();
 165  39
         return file.exists() ? file.getContent().getLastModifiedTime() : NOT_EXISTANT_TIMESTAMP;
 166  
     }
 167  
 
 168  
     protected GeneratedResult<COWEntryCache> generate(COWEntryCache old) throws FileSystemException {
 169  12
         FileObject file = getFile();
 170  12
         DocumentBuilder documentBuilder = xmlBuilders.poll();
 171  
         try {
 172  12
             InputStream in = file.getContent().getInputStream();
 173  12
             long lastModified = file.getContent().getLastModifiedTime();
 174  12
             Document doc = documentBuilder.parse(in);
 175  10
             in.close();
 176  10
             Node node = doc.getDocumentElement().getFirstChild();
 177  10
             COWEntryCache newEntries = new COWEntryCache();
 178  43
             while (node != null) {
 179  33
                 if (node.getNodeType() == Node.ELEMENT_NODE && "item".equals(node.getNodeName())) {
 180  13
                     Element element = (Element) node;
 181  13
                     String name = element.getAttribute("name");
 182  13
                     boolean deleted = "true".equals(element.getAttribute("deleted"));
 183  13
                     String symlink = element.hasAttribute("symlink") ? element.getAttribute("symlink") : null;
 184  13
                     Node baseNode = element.getFirstChild();
 185  13
                     ArrayList<String> bases = new ArrayList<String>();
 186  39
                     while (baseNode != null) {
 187  26
                         if (baseNode.getNodeType() == Node.ELEMENT_NODE && "base".equals(baseNode.getNodeName())) {
 188  10
                             String base = ((Element) baseNode).getAttribute("target");
 189  10
                             if (base != null && base.length() > 0) bases.add(base);
 190  
                         }
 191  26
                         baseNode = baseNode.getNextSibling();
 192  
                     }
 193  13
                     COWEntryImpl entry = newEntries.get(name);
 194  13
                     entry.absorb(deleted, symlink, bases);
 195  
                 }
 196  33
                 node = node.getNextSibling();
 197  
             }
 198  10
             if (newEntries.isEmpty()) file.delete();
 199  10
             return new GeneratedResult<COWEntryCache>(lastModified, newEntries);
 200  2
         } catch (Exception e) {
 201  2
             throw VFSUtil.makeFileSystemException(e);
 202  
         } finally {
 203  12
             xmlBuilders.offer(documentBuilder);
 204  
         }
 205  
     }
 206  
 
 207  
     public synchronized void save() throws FileSystemException {
 208  53
         DocumentBuilder documentBuilder = xmlBuilders.element();
 209  
         try {
 210  53
             Document doc = documentBuilder.newDocument();
 211  53
             boolean itemsAdded = false;
 212  53
             Element stateElement = doc.createElement("state");
 213  53
             doc.appendChild(stateElement);
 214  53
             COWEntryCache cache = getObject();
 215  53
             ArrayList<String> tmpNames = new ArrayList<String>();
 216  53
             tmpNames.addAll(cache.keys());
 217  53
             String[] names = tmpNames.toArray(new String[tmpNames.size()]);
 218  53
             Arrays.sort(names);
 219  223
             for (String name: names) {
 220  170
                 COWEntry entry = cache.get(name);
 221  170
                 boolean deleted = entry.isDeleted();
 222  170
                 String symlink = entry.getSymlink();
 223  170
                 if (entry.isEmpty()) continue;
 224  69
                 itemsAdded = true;
 225  69
                 Element itemElement = doc.createElement("item");
 226  69
                 itemElement.setAttribute("name", name);
 227  69
                 if (deleted) itemElement.setAttribute("deleted", "true");
 228  69
                 if (symlink != null) itemElement.setAttribute("symlink", symlink);
 229  69
                 ArrayList<String> list = new ArrayList<String>(entry.getBases());
 230  69
                 String[] basePaths = list.toArray(new String[list.size()]);
 231  69
                 Arrays.sort(basePaths);
 232  117
                 for (String basePath: basePaths) {
 233  48
                     Element baseElement = doc.createElement("base");
 234  48
                     baseElement.setAttribute("target", basePath);
 235  48
                     itemElement.appendChild(baseElement);
 236  
                 }
 237  69
                 stateElement.appendChild(itemElement);
 238  
             }
 239  53
             if (itemsAdded) {
 240  41
                 OutputStream out = getFile().getContent().getOutputStream();
 241  41
                 DOMSource source = new DOMSource(doc);
 242  41
                 StreamResult result = new StreamResult(out);
 243  41
                 TransformerFactory factory = TransformerFactory.newInstance();
 244  41
                 Transformer transformer = factory.newTransformer();
 245  41
                 transformer.setOutputProperty(OutputKeys.INDENT, "yes");
 246  41
                 transformer.transform(source, result);
 247  41
                 out.close();
 248  41
                 lastModifiedTime = getFile().getContent().getLastModifiedTime();
 249  41
             } else {
 250  12
                 getFile().delete();
 251  12
                 lastModified = NOT_EXISTANT_TIMESTAMP;
 252  
             }
 253  0
         } catch (Exception e) {
 254  0
             throw VFSUtil.makeFileSystemException(e);
 255  
         } finally {
 256  53
             xmlBuilders.offer(documentBuilder);
 257  53
         }
 258  53
     }
 259  
 
 260  
     public boolean equals(Object o) {
 261  0
         if (!(o instanceof COWState)) return false;
 262  0
         COWState cs = (COWState) o;
 263  0
         return fileSystem.equals(cs.fileSystem) && fileName.equals(cs.fileName);
 264  
     }
 265  
 
 266  
     public int hashCode() {
 267  0
         return fileSystem.hashCode() ^ fileName.hashCode();
 268  
     }
 269  
 
 270  109
     class COWEntryCache extends ConcurrentCache<String, COWEntryImpl> {
 271  76
         COWEntryCache() {
 272  76
             super(COWState.class, "entries", fileName.toString(), ConcurrentCache.HARD);
 273  76
         }
 274  
 
 275  
         protected COWEntryImpl createValue(String name) {
 276  109
             return new COWEntryImpl(COWState.this, name);
 277  
         }
 278  
     }
 279  
 }