Clover coverage report -
Coverage timestamp: Sun Nov 16 2008 23:05:30 GMT
file stats: LOC: 1,903   Methods: 86
NCLOC: 1,057   Classes: 3
 
 Source file Conditionals Statements Methods TOTAL
Collection.java 58.3% 74.5% 73.3% 69.3%
coverage coverage
 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    * $Id: Collection.java 712324 2008-11-08 00:32:01Z vgritsenko $
 18    */
 19   
 20    package org.apache.xindice.core;
 21   
 22    import org.apache.commons.logging.Log;
 23    import org.apache.commons.logging.LogFactory;
 24    import org.apache.xindice.core.cache.DocumentCache;
 25    import org.apache.xindice.core.data.DocumentSet;
 26    import org.apache.xindice.core.data.EmptyDocumentSet;
 27    import org.apache.xindice.core.data.EmptyNodeSet;
 28    import org.apache.xindice.core.data.Entry;
 29    import org.apache.xindice.core.data.Key;
 30    import org.apache.xindice.core.data.NodeSet;
 31    import org.apache.xindice.core.data.Record;
 32    import org.apache.xindice.core.data.RecordSet;
 33    import org.apache.xindice.core.data.Value;
 34    import org.apache.xindice.core.filer.Filer;
 35    import org.apache.xindice.core.indexer.IndexManager;
 36    import org.apache.xindice.core.indexer.Indexer;
 37    import org.apache.xindice.core.meta.MetaData;
 38    import org.apache.xindice.core.meta.inline.InlineMetaMap;
 39    import org.apache.xindice.core.meta.inline.InlineMetaService;
 40    import org.apache.xindice.core.meta.inline.ResourceTypeReader;
 41    import org.apache.xindice.core.query.QueryEngine;
 42    import org.apache.xindice.util.Configurable;
 43    import org.apache.xindice.util.Configuration;
 44    import org.apache.xindice.util.Named;
 45    import org.apache.xindice.util.XindiceException;
 46    import org.apache.xindice.xml.NamespaceMap;
 47    import org.apache.xindice.xml.NodeSource;
 48    import org.apache.xindice.xml.SymbolTable;
 49    import org.apache.xindice.xml.TextWriter;
 50    import org.apache.xindice.xml.XMLSerializable;
 51    import org.apache.xindice.xml.dom.DBDocument;
 52    import org.apache.xindice.xml.dom.DOMCompressor;
 53    import org.apache.xindice.xml.dom.DOMParser;
 54    import org.apache.xindice.xml.dom.DocumentImpl;
 55   
 56    import org.w3c.dom.Document;
 57    import org.w3c.dom.Element;
 58    import org.w3c.dom.Node;
 59    import org.w3c.dom.NodeList;
 60    import org.w3c.dom.ProcessingInstruction;
 61   
 62    import java.io.File;
 63    import java.io.UnsupportedEncodingException;
 64    import java.lang.ref.WeakReference;
 65    import java.net.InetAddress;
 66    import java.util.ArrayList;
 67    import java.util.Map;
 68    import java.util.WeakHashMap;
 69   
 70    /**
 71    * Collection represents a collection of Documents maintains links to
 72    * the Filer storage implementation, and the Indexes associated with
 73    * the Collection.
 74    *
 75    * @version $Revision: 712324 $, $Date: 2008-11-08 00:32:01 +0000 (Sat, 08 Nov 2008) $
 76    */
 77    public class Collection extends CollectionManager
 78    implements Named, DBObject, Configurable {
 79   
 80    private static final Log log = LogFactory.getLog(Collection.class);
 81   
 82    private static final String CACHE = "cache";
 83    private static final String CLASS = "class";
 84    private static final String CLASSNAME = "xindice-class";
 85    private static final String COMPRESSED = "compressed";
 86    private static final String FILER = "filer";
 87    private static final String INDEXES = "indexes";
 88    private static final String INLINE_METADATA = "inline-metadata";
 89    private static final String NAME = "name";
 90    private static final String SYMBOLS = "symbols";
 91   
 92    private static final byte ACTION_INSERT = 1;
 93    private static final byte ACTION_UPDATE = 2;
 94    private static final byte ACTION_STORE = 3;
 95   
 96    private static final DocumentSet EMPTY_DOCUMENTSET = new EmptyDocumentSet();
 97    private static final NodeSet EMPTY_NODESET = new EmptyNodeSet();
 98    private static final String[] EMPTY_STRING_ARRAY = {};
 99   
 100    private static int host_id;
 101    static {
 102  18 try {
 103  18 InetAddress a = InetAddress.getLocalHost();
 104  18 byte[] b = a.getAddress();
 105  18 host_id = 0;
 106  18 host_id += b[0];
 107  18 host_id += (b[1] << 8);
 108  18 host_id += (b[2] << 16);
 109  18 host_id += (b[3] << 24);
 110  18 host_id = Math.abs(host_id);
 111    } catch (Exception e) {
 112  0 if (log.isWarnEnabled()) {
 113  0 log.warn("ignored exception", e);
 114    }
 115    }
 116    }
 117   
 118    /**
 119    * ColContainer
 120    */
 121    private class ColContainer implements Container {
 122    private Document document;
 123    private Key key;
 124   
 125  0 public ColContainer(Key key, Document document) {
 126  0 this.key = key;
 127  0 this.document = document;
 128    }
 129   
 130  0 public void commit() throws DBException {
 131  0 putDocument(this.key, this.document, ACTION_STORE);
 132    }
 133   
 134  0 public void commit(Document doc) throws DBException {
 135  0 this.document = doc;
 136  0 commit();
 137    }
 138   
 139  0 public String getCanonicalName() throws DBException {
 140  0 return Collection.this.getCanonicalDocumentName(key);
 141    }
 142   
 143  0 public Collection getCollection() {
 144  0 return Collection.this;
 145    }
 146   
 147  0 public Document getDocument() {
 148  0 return this.document;
 149    }
 150   
 151  0 public Key getKey() {
 152  0 return this.key;
 153    }
 154   
 155  0 public void remove() throws DBException {
 156  0 Collection.this.remove(key);
 157    }
 158   
 159  0 public Document rollback() throws DBException {
 160  0 this.document = Collection.this.getDocument(key);
 161  0 return this.document;
 162    }
 163    }
 164   
 165    /**
 166    * ColDocumentSet
 167    */
 168    private class ColDocumentSet implements DocumentSet {
 169    private RecordSet set;
 170   
 171  0 public ColDocumentSet(RecordSet set) {
 172  0 this.set = set;
 173    }
 174   
 175  0 public Container getNextContainer() throws DBException {
 176  0 if (set.hasMoreRecords()) {
 177  0 Record rec = set.getNextRecord();
 178  0 Key key = rec.getKey();
 179  0 Value val = rec.getValue();
 180  0 if (val.getLength() > 0) {
 181  0 try {
 182  0 if (compressed) {
 183  0 Document doc = new DocumentImpl(val.getData(), symbols, new NodeSource(Collection.this, key));
 184  0 return new ColContainer(key, doc);
 185    } else {
 186  0 return new ColContainer(key, DOMParser.toDocument(val));
 187    }
 188    } catch (Exception e) {
 189  0 if (log.isWarnEnabled()) {
 190  0 log.warn("ignored exception", e);
 191    }
 192    }
 193    }
 194    }
 195  0 return null;
 196    }
 197   
 198  0 public Document getNextDocument() throws DBException {
 199  0 Container c = getNextContainer();
 200  0 if (c != null) {
 201  0 return c.getDocument();
 202    } else {
 203  0 return null;
 204    }
 205    }
 206   
 207  0 public boolean hasMoreDocuments() throws DBException {
 208  0 return set.hasMoreRecords();
 209    }
 210    }
 211   
 212   
 213    private String name;
 214    private String canonicalName;
 215    private Collection parent;
 216   
 217    // Object ID Stuff
 218    private final Object oidMutex = new Object();
 219    private String oidTemplate;
 220    private long documentId;
 221   
 222    private SymbolTable symbols;
 223   
 224    private File collectionRoot;
 225    private boolean compressed;
 226    private Filer filer;
 227    private InlineMetaService inlineMetaService;
 228    private DocumentCache cache;
 229    private IndexManager indexManager;
 230   
 231    // document keys identity map
 232    private final Map identityMap = new WeakHashMap();
 233   
 234   
 235  122 protected Collection() {
 236  122 documentId = System.currentTimeMillis();
 237    }
 238   
 239    /**
 240    * @param parent parent collection
 241    */
 242  1399 public Collection(Collection parent) {
 243  1399 this.parent = parent;
 244    }
 245   
 246  28147 public boolean isCompressed() {
 247  28147 return compressed;
 248    }
 249   
 250    // -- Internal Implementation Methods -----------------------------------
 251   
 252  53835 private void checkFiler(int faultCode) throws DBException {
 253  53835 if (filer == null) {
 254  0 throw new DBException(faultCode,
 255    "Collection '" + name + "' cannot store resources (no filer)");
 256    }
 257  53835 if (!filer.isOpened()) {
 258  0 throw new DBException(FaultCodes.COL_COLLECTION_CLOSED,
 259    "Collection '" + name + "' is closed.");
 260    }
 261    }
 262   
 263  52621 private Key getIdentityKey(Key key) {
 264  52621 synchronized (identityMap) {
 265  52621 Key id = null;
 266  52621 WeakReference ref = (WeakReference) identityMap.get(key);
 267  52621 if (ref != null) {
 268  48490 id = (Key) ref.get();
 269    }
 270  52621 if (id == null) {
 271  4131 id = key;
 272  4131 identityMap.put(id, new WeakReference(id));
 273    }
 274   
 275  52621 return id;
 276    }
 277    }
 278   
 279  12304 private String debugHeader() {
 280  12304 return "["
 281    + Thread.currentThread().getName()
 282    + "] '"
 283  12304 + (parent != null ? parent.getCanonicalName() : "")
 284    + "/"
 285    + name
 286    + "' ";
 287    }
 288   
 289    /**
 290    * @throws DBException if operation failed
 291    */
 292  16023 private void flushSymbolTable() throws DBException {
 293  16023 if (symbols.isDirty()) {
 294  704 getSystemCollection().saveSymbols(this, symbols);
 295    }
 296    }
 297   
 298    /**
 299    * @param name collection name
 300    */
 301  0 protected void setName(String name) {
 302  0 this.name = name;
 303    }
 304   
 305  1509 protected final void setCanonicalName(String canonicalName) {
 306  1509 this.canonicalName = canonicalName;
 307   
 308    // Calculate The OID Template
 309  1509 StringBuffer sb = new StringBuffer("00000000000000000000000000000000");
 310  1509 String host = Integer.toString(host_id, 16);
 311  1509 sb.insert(8 - host.length(), host);
 312   
 313  1509 String collection = Integer.toString(Math.abs(canonicalName.hashCode()), 16);
 314  1509 sb.insert(16 - collection.length(), collection);
 315   
 316  1509 sb.setLength(32);
 317  1509 oidTemplate = sb.toString();
 318    }
 319   
 320  1509 protected final void setCollectionRoot(File collectionRoot) {
 321  1509 this.collectionRoot = collectionRoot;
 322  1509 if (!collectionRoot.exists()) {
 323  797 if (log.isTraceEnabled()) {
 324  0 log.trace("Creating directories: " + collectionRoot);
 325    }
 326  797 collectionRoot.mkdirs();
 327    }
 328    }
 329   
 330    /**
 331    * createNewKey allocates a new key to be used as a key in the
 332    * collection. Passed in <code>key</code> parameter string value
 333    * used for the key. If passed key parameter is null, new OID is generated.
 334    *
 335    * @param key The Key hint, can be null
 336    * @return The newly generated Key
 337    */
 338  52623 protected final Key createNewKey(Object key) {
 339  52623 if (key == null) {
 340  1 return createNewOID();
 341  52622 } else if (key instanceof Key) {
 342  37766 return (Key) key;
 343    } else {
 344  14856 return new Key(key.toString());
 345    }
 346    }
 347   
 348    /**
 349    * Turns an XML string into a parsed document retrieved
 350    * from the uncompressed collection.
 351    *
 352    * @param key The key to use when caching
 353    * @param xml The string to parse
 354    * @return A parsed DOM document or null if failure
 355    * @throws DBException if operation failed
 356    */
 357  4595 private Document parseDocument(Key key, String xml) throws DBException {
 358  4595 try {
 359  4595 Document doc = DOMParser.toDocument(xml);
 360  4595 ((DBDocument) doc).setSource(new NodeSource(this, key));
 361   
 362    // Have to compress to update collection's SymbolTable,
 363    // which is used even for uncompressed collections
 364  4595 DOMCompressor.compress(doc, symbols);
 365   
 366  4595 return doc;
 367    } catch (Exception e) {
 368  0 throw new DBException(FaultCodes.COL_DOCUMENT_MALFORMED,
 369    "Unable to parse document '" + key + "' in '" + getCanonicalName() + "'", e);
 370    }
 371    }
 372   
 373   
 374    // -- Database Object Methods -------------------------------------------
 375   
 376  0 public boolean isOpened() {
 377    // Collection without filer is always open ... for now.
 378    //noinspection SimplifiableIfStatement
 379  0 if (filer == null) {
 380  0 return true;
 381    }
 382   
 383  0 return filer.isOpened();
 384    }
 385   
 386    /**
 387    * @see org.apache.xindice.core.DBObject#exists()
 388    */
 389  0 public boolean exists() throws DBException {
 390  0 return true;
 391    }
 392   
 393    /**
 394    * @see org.apache.xindice.core.DBObject#create()
 395    */
 396  777 public boolean create() throws DBException {
 397    // update the meta information if necessary
 398  777 updateCollectionMeta();
 399   
 400  777 DBObserver.getInstance().createCollection(this);
 401  777 return true;
 402    }
 403   
 404  0 public final boolean open() throws DBException {
 405  0 return true;
 406    }
 407   
 408    /**
 409    * @see org.apache.xindice.core.DBObject#drop()
 410    */
 411  780 public boolean drop() throws DBException {
 412  780 DBObserver.getInstance().dropCollection(this);
 413   
 414    // Drop the meta if necessary
 415  780 if (isMetaEnabled()) {
 416  779 getMetaSystemCollection().dropCollectionMeta(this);
 417    }
 418   
 419    // Drop Child Collections
 420  780 String[] cols = listCollections();
 421  780 for (int i = 0; i < cols.length; i++) {
 422  40 dropCollection(getCollection(cols[i]));
 423    }
 424   
 425  780 if (filer != null) {
 426    // Drop Indexers and Filer
 427  780 indexManager.drop();
 428  780 filer.drop();
 429    }
 430   
 431  780 getCollectionRoot().delete();
 432   
 433    // Drop symbols
 434  780 if (!symbols.isReadOnly()) {
 435  780 getSystemCollection().dropSymbols(this);
 436    }
 437   
 438  780 getDatabase().flushConfig();
 439  780 return true;
 440    }
 441   
 442    /**
 443    * @see org.apache.xindice.core.DBObject#close()
 444    */
 445  724 public boolean close() throws DBException {
 446    // Close children collections first
 447  730 super.close();
 448   
 449    // Close its own filer
 450  730 if (filer != null) {
 451  244 indexManager.close();
 452  244 filer.close();
 453    }
 454   
 455  730 return true;
 456    }
 457   
 458   
 459    // -- CollectionManager methods -----------------------------------------
 460   
 461  1631 public void setConfig(Configuration config) throws XindiceException {
 462  1631 name = config.getAttribute(NAME);
 463  1631 compressed = config.getBooleanAttribute(COMPRESSED, true);
 464   
 465    /*
 466    * If inline metadata is desired, get an InlineMetaService object.
 467    */
 468  1631 if (config.getBooleanAttribute(INLINE_METADATA, false)) {
 469  385 inlineMetaService = new InlineMetaService();
 470    }
 471   
 472    /*
 473    * Wait to set up the local debug header until everything needed
 474    * by debugHeader() is complete!
 475    */
 476  1631 final String localDebugHeader = debugHeader() + "setConfig: ";
 477