You are viewing a plain text version of this content. The canonical link for it is here.
Posted to xindice-dev@xml.apache.org by vg...@apache.org on 2007/11/12 19:06:01 UTC
svn commit: r594237 -
/xml/xindice/trunk/java/src/org/apache/xindice/core/Collection.java
Author: vgritsenko
Date: Mon Nov 12 10:06:00 2007
New Revision: 594237
URL: http://svn.apache.org/viewvc?rev=594237&view=rev
Log:
rearranging methods - use logical groupings
Modified:
xml/xindice/trunk/java/src/org/apache/xindice/core/Collection.java
Modified: xml/xindice/trunk/java/src/org/apache/xindice/core/Collection.java
URL: http://svn.apache.org/viewvc/xml/xindice/trunk/java/src/org/apache/xindice/core/Collection.java?rev=594237&r1=594236&r2=594237&view=diff
==============================================================================
--- xml/xindice/trunk/java/src/org/apache/xindice/core/Collection.java (original)
+++ xml/xindice/trunk/java/src/org/apache/xindice/core/Collection.java Mon Nov 12 10:06:00 2007
@@ -324,6 +324,49 @@
}
}
+ /**
+ * createNewKey allocates a new key to be used as a key in the
+ * collection. Passed in <code>key</code> parameter string value
+ * used for the key. If passed key parameter is null, new OID is generated.
+ *
+ * @param key The Key hint, can be null
+ * @return The newly generated Key
+ */
+ protected final Key createNewKey(Object key) {
+ if (key == null) {
+ return createNewOID();
+ } else if (key instanceof Key) {
+ return (Key) key;
+ } else {
+ return new Key(key.toString());
+ }
+ }
+
+ /**
+ * Turns an XML string into a parsed document retrieved
+ * from the uncompressed collection.
+ *
+ * @param key The key to use when caching
+ * @param xml The string to parse
+ * @return A parsed DOM document or null if failure
+ * @throws DBException if operation failed
+ */
+ private Document parseDocument(Key key, String xml) throws DBException {
+ try {
+ Document doc = DOMParser.toDocument(xml);
+ ((DBDocument) doc).setSource(new NodeSource(this, key));
+
+ // Have to compress to update collection's SymbolTable,
+ // which is used even for uncompressed collections
+ DOMCompressor.compress(doc, symbols);
+
+ return doc;
+ } catch (Exception e) {
+ throw new DBException(FaultCodes.COL_DOCUMENT_MALFORMED,
+ "Unable to parse document '" + key + "' in '" + getCanonicalName() + "'", e);
+ }
+ }
+
// -- Database Object Methods -------------------------------------------
@@ -664,7 +707,42 @@
}
- // ----------------------------------------------------------------------
+ // -- Core Collection API Public Methods: Index Management --------------
+
+ /**
+ * return the IndexManager being used by this Collection.
+ *
+ * @return The IndexManager
+ * @throws DBException if operation failed
+ */
+ public final IndexManager getIndexManager() throws DBException {
+ checkFiler(FaultCodes.COL_NO_INDEXMANAGER);
+ return indexManager;
+ }
+
+ /**
+ * getIndexer retrieves an Indexer by name.
+ *
+ * @param name The Indexer name
+ * @return The Indexer (or null)
+ * @throws DBException if operation failed
+ */
+ public final Indexer getIndexer(String name) throws DBException {
+ checkFiler(FaultCodes.COL_NO_INDEXMANAGER);
+ return indexManager.get(name);
+ }
+
+ /**
+ * listIndexers returns a list of the currently registered Indexers
+ * as an array of String.
+ *
+ * @return The Indexer list
+ * @throws DBException if operation failed
+ */
+ public final String[] listIndexers() throws DBException {
+ checkFiler(FaultCodes.COL_NO_INDEXMANAGER);
+ return indexManager.list();
+ }
/**
* createIndexer creates a new Indexer object and any associated
@@ -682,23 +760,29 @@
}
/**
- * createNewKey allocates a new key to be used as a key in the
- * collection. Passed in <code>key</code> parameter string value
- * used for the key. If passed key parameter is null, new OID is generated.
+ * dropIndexer physically removes the specified Indexer and any
+ * associated system resources that the Indexer uses.
*
- * @param key The Key hint, can be null
- * @return The newly generated Key
+ * @param index The Indexer to drop
+ * @return Whether or not the Indexer was dropped
+ * @throws DBException if operation failed
*/
- protected final Key createNewKey(Object key) {
- if (key == null) {
- return createNewOID();
- } else if (key instanceof Key) {
- return (Key) key;
- } else {
- return new Key(key.toString());
+ public final boolean dropIndexer(Indexer index) throws DBException {
+ checkFiler(FaultCodes.COL_NO_INDEXMANAGER);
+
+ if (index == null) {
+ throw new DBException(FaultCodes.IDX_INDEX_NOT_FOUND,
+ "Index value is null");
}
+
+ boolean success = indexManager.drop(index.getName());
+ getDatabase().flushConfig();
+ return success;
}
+
+ // -- Core Collection API Public Methods: Data Management ---------------
+
/**
* createNewOID allocates a new Object ID to be used as a Key in the
* Collection.
@@ -722,47 +806,130 @@
}
/**
- * dropIndexer physically removes the specified Indexer and any
- * associated system resources that the Indexer uses.
+ * Retrieve a database entry by key.
*
- * @param index The Indexer to drop
- * @return Whether or not the Indexer was dropped
- * @throws DBException if operation failed
+ * If no matching entry is found, null is returned, otherwise this
+ * method returns Entry that identifies resource type and holds its
+ * value and metadata.
+ *
+ * @param docKey identifying the desired database entry
+ * @return Entry containing the database entry and its metadata, or null
+ * if no matching entry is found
+ * @throws DBException in case of backing store error,
+ * and in case of header corruption
*/
- public final boolean dropIndexer(Indexer index) throws DBException {
- checkFiler(FaultCodes.COL_NO_INDEXMANAGER);
+ public final Entry getEntry(Object docKey) throws DBException {
- if (index == null) {
- throw new DBException(FaultCodes.IDX_INDEX_NOT_FOUND,
- "Index value is null");
+ // I would prefer to throw an exception (NPE) in this case,
+ // but we have a test indicating a null return...
+ if (docKey == null) {
+ return null;
}
- boolean success = indexManager.drop(index.getName());
- getDatabase().flushConfig();
- return success;
+ String localDebugHeader = null;
+ if (log.isTraceEnabled()) {
+ localDebugHeader = debugHeader() + "getEntry: docKey=<" + docKey + ">: ";
+ log.trace(localDebugHeader);
+ }
+
+ checkFiler(FaultCodes.COL_NO_FILER);
+
+ Key key = getIdentityKey(createNewKey(docKey));
+ synchronized (key) {
+
+ /*
+ * If the key has a corresponding value in the cache, return it
+ * and save a disk access.
+ *
+ * At some point the current document-centric cache implementation
+ * needs to be converted to an entry cache which can hold both
+ * Document and byte[].
+ */
+ if (documentCache != null) {
+ Entry entry = documentCache.getEntry(this, key);
+ if (entry != null) {
+ if (log.isTraceEnabled()) {
+ log.trace(localDebugHeader + "Returning cached: " + entry.getValue());
+ }
+
+ return entry;
+ }
+ }
+
+ Record record = filer.readRecord(key);
+ if (record == null) {
+ return null;
+ }
+
+ Value value;
+ InlineMetaMap metaMap = null;
+ if (inlineMetaService == null) {
+ value = record.getValue();
+
+ if (log.isTraceEnabled()) {
+ log.trace(localDebugHeader + "Type is not available, Length=" + value.getLength());
+ }
+ } else {
+ InlineMetaService.DatabaseEntry databaseEntry = inlineMetaService.readDatabaseEntry(record.getValue());
+ metaMap = databaseEntry.map;
+ value = databaseEntry.value;
+
+ if (log.isTraceEnabled()) {
+ log.trace(localDebugHeader + "Type=" + metaMap.get("type") + ", Length=" + value.getLength());
+ }
+ }
+
+ Map entryMeta = Entry.createMetaMap(record);
+ if (inlineMetaService == null || metaMap.get("type").equals(ResourceTypeReader.XML)) {
+ Document document;
+ if (compressed) {
+ document = new DocumentImpl(value.getData(), symbols, new NodeSource(this, key));
+ flushSymbolTable();
+ if (log.isTraceEnabled()) {
+ log.trace(localDebugHeader +
+ "Compressed XML document=<" + TextWriter.toString(document) + ">");
+ }
+
+ if (documentCache != null) {
+ documentCache.putDocumentEntry(this, key, value.getData(), entryMeta);
+ }
+ } else {
+ String documentChars = value.toString();
+ if (log.isTraceEnabled()) {
+ log.trace(localDebugHeader + "Pre parseDocument(): value=<" + documentChars + ">");
+ }
+
+ // FIXME There should be no reason here to re-compress the document & flush symbols table?
+ document = parseDocument(key, documentChars);
+ flushSymbolTable();
+
+ if (documentCache != null) {
+ documentCache.putDocumentEntry(this, key, documentChars, entryMeta);
+ }
+ }
+
+ DBObserver.getInstance().loadDocument(this, record, document);
+ return new Entry(key, document, entryMeta);
+ } else {
+ if (log.isTraceEnabled()) {
+ log.trace(localDebugHeader + "Binary document");
+ }
+
+ return new Entry(key, value.getData(), entryMeta);
+ }
+ }
}
/**
- * Retrieve a binary database entry by key.
- * This low-level method will not update non-inline metadata.
+ * getDocument retrieves a Document by Key.
*
- * @param key identifying the desired database entry
- * @return byte[] containing the binary database entry
- * @throws DBException if inline-metadata is not enabled
- * (binary resource cannot be stored in a collection
- * which does not have inline-metadata enabled),
- * in case of backing store error, and in case of
- * header corruption
+ * @param key The Document Key
+ * @return The Document
+ * @throws DBException if operation failed
*/
- public final byte[] getBinary(Object key) throws DBException {
- if (log.isTraceEnabled()) {
- log.trace(debugHeader() + "Get binary: " + key);
- }
-
- if (inlineMetaService == null) {
- throw new DBException(FaultCodes.COL_DOCUMENT_NOT_FOUND,
- "Collection '" + getCanonicalName() +
- "' has no binary resources (inline metadata is not enabled)");
+ public final Document getDocument(Object key) throws DBException {
+ if (log.isDebugEnabled()) {
+ log.debug(debugHeader() + "Get document: " + key);
}
Entry entry = getEntry(key);
@@ -770,76 +937,7 @@
return null;
}
- if (entry.getEntryType() != Entry.BINARY) {
- throw new DBException(FaultCodes.COL_INVALID_RESULT,
- "Resource '" + key + "' in collection '" +
- getCanonicalName() + "' is not a binary resource");
- }
-
- return (byte[]) entry.getValue();
- }
-
- /**
- * Return the MetaData for this collection.
- *
- * If metadata is not enabled in the configuration, the MetaData object
- * returned will be null.
- *
- * @return MetaData this collection's metadata.
- * @throws DBException if operation failed
- */
- public MetaData getCollectionMeta() throws DBException {
- if (!isMetaEnabled()) {
- if (log.isWarnEnabled()) {
- log.warn("Meta information requested but not enabled in config!");
- }
- return null;
- }
-
- MetaSystemCollection metacol = getMetaSystemCollection();
- MetaData meta = metacol.getCollectionMeta(this);
- if (null == meta) {
- long now = System.currentTimeMillis();
- meta = new MetaData(MetaData.COLLECTION, getCanonicalName(), now, now);
- metacol.setCollectionMeta(this, meta);
- }
-
- return meta;
- }
-
- /**
- * getContainer retrieves a Container from the Collection. The Container
- * encapsulates all information needed in dealing with a Document outside
- * of the context of a Collection (ex: DocumentContext).
- *
- * @param docKey The Document Key
- * @return The Container
- * @throws DBException if operation failed
- */
- public final Container getContainer(Object docKey) throws DBException {
- Key key = createNewKey(docKey);
- Document doc = getDocument(key);
- return doc != null ? new ColContainer(key, doc) : null;
- }
-
- /**
- * getDocument retrieves a Document by Key.
- *
- * @param key The Document Key
- * @return The Document
- * @throws DBException if operation failed
- */
- public final Document getDocument(Object key) throws DBException {
- if (log.isDebugEnabled()) {
- log.debug(debugHeader() + "Get document: " + key);
- }
-
- Entry entry = getEntry(key);
- if (entry == null) {
- return null;
- }
-
- if (entry.getEntryType() != Entry.DOCUMENT) {
+ if (entry.getEntryType() != Entry.DOCUMENT) {
throw new DBException(FaultCodes.COL_INVALID_RESULT,
"Resource '" + key + "' in collection '" +
getCanonicalName() + "' is not a document");
@@ -849,268 +947,6 @@
}
/**
- * getDocumentCount returns the count of Documents being maintained
- * by this Collection.
- *
- * @return The Document count
- * @throws DBException if operation failed
- */
- public final long getDocumentCount() throws DBException {
- // a collection in which you are unable to file documents will have no filer
- // (for example the root collection). Rather than throwing an exception return
- // a constant result (nothing)
- return null == filer ? 0 : filer.getRecordCount();
- }
-
- /**
- * Return the MetaData object for a document within this collection.
- * If metadata is not enabled, the MetaData object returned will be null.
- *
- * @param id the document whose metadata you want
- * @return meta data for requested resource
- * @throws DBException if operation failed
- */
- public MetaData getDocumentMeta(String id) throws DBException {
- if (!isMetaEnabled()) {
- if (log.isWarnEnabled()) {
- log.warn("Meta information requested but not enabled in config!");
- }
- return null;
- }
-
- Key key = getIdentityKey(createNewKey(id));
- synchronized (key) {
- if (getEntry(id) == null) {
- throw new DBException(FaultCodes.COL_DOCUMENT_NOT_FOUND,
- "Resource '" + id + "' does not exist in '" + getCanonicalName() + "'");
- }
-
- MetaSystemCollection metacol = getMetaSystemCollection();
- MetaData meta = metacol.getDocumentMeta(this, id);
-
- /*
- FIXME It is more efficient to store (and retrieve) created/modified timestamps
- from the Record itself instead of storing them in the separate MetaData
- object. Storing in the Record avoids writing two documents on each update
- (Document itself and its MetaData).
- Retrieval of the timestamps from Record can be implemented via TimeRecord.
-
- TimeRecord rec = null;
- if( null == meta || !meta.hasContext() )
- rec = getDatabase().getTime(path);
-
- long created = (null != rec) ? rec.getCreatedTime() : System.currentTimeMillis();
- long modified = (null != rec) ? rec.getModifiedTime() : System.currentTimeMillis();
- */
-
- // this is wrong.. but it should work for now...
- long now = System.currentTimeMillis();
- if (meta == null) {
- meta = new MetaData(MetaData.DOCUMENT, getCanonicalDocumentName(id), now, now);
- metacol.setDocumentMeta(this, id, meta);
- } else if (!meta.hasContext()) {
- meta.setContext(now, now);
- }
-
- return meta;
- }
- }
-
- /**
- * getDocumentSet returns the set of Documents being maintained
- * by this Collection.
- *
- * @return The DocumentSet
- * @throws DBException if operation failed
- */
- public final DocumentSet getDocumentSet() throws DBException {
- // a collection in which you are unable to file documents will have no filer
- // (for example the root collection). Rather than throwing an exception return
- // a constant result (nothing)
- return null == filer ? EMPTY_DOCUMENTSET : new ColDocumentSet(filer.getRecordSet());
- }
-
- /**
- * Retrieve a database entry by key.
- *
- * If no matching entry is found, null is returned, otherwise this
- * method returns Entry that identifies resource type and holds its
- * value and metadata.
- *
- * @param docKey identifying the desired database entry
- * @return Entry containing the database entry and its metadata, or null
- * if no matching entry is found
- * @throws DBException in case of backing store error,
- * and in case of header corruption
- */
- public final Entry getEntry(Object docKey) throws DBException {
-
- // I would prefer to throw an exception (NPE) in this case,
- // but we have a test indicating a null return...
- if (docKey == null) {
- return null;
- }
-
- String localDebugHeader = null;
- if (log.isTraceEnabled()) {
- localDebugHeader = debugHeader() + "getEntry: docKey=<" + docKey + ">: ";
- log.trace(localDebugHeader);
- }
-
- checkFiler(FaultCodes.COL_NO_FILER);
-
- Key key = getIdentityKey(createNewKey(docKey));
- synchronized (key) {
-
- /*
- * If the key has a corresponding value in the cache, return it
- * and save a disk access.
- *
- * At some point the current document-centric cache implementation
- * needs to be converted to an entry cache which can hold both
- * Document and byte[].
- */
- if (documentCache != null) {
- Entry entry = documentCache.getEntry(this, key);
- if (entry != null) {
- if (log.isTraceEnabled()) {
- log.trace(localDebugHeader + "Returning cached: " + entry.getValue());
- }
-
- return entry;
- }
- }
-
- Record record = filer.readRecord(key);
- if (record == null) {
- return null;
- }
-
- Value value;
- InlineMetaMap metaMap = null;
- if (inlineMetaService == null) {
- value = record.getValue();
-
- if (log.isTraceEnabled()) {
- log.trace(localDebugHeader + "Type is not available, Length=" + value.getLength());
- }
- } else {
- InlineMetaService.DatabaseEntry databaseEntry = inlineMetaService.readDatabaseEntry(record.getValue());
- metaMap = databaseEntry.map;
- value = databaseEntry.value;
-
- if (log.isTraceEnabled()) {
- log.trace(localDebugHeader + "Type=" + metaMap.get("type") + ", Length=" + value.getLength());
- }
- }
-
- Map entryMeta = Entry.createMetaMap(record);
- if (inlineMetaService == null || metaMap.get("type").equals(ResourceTypeReader.XML)) {
- Document document;
- if (compressed) {
- document = new DocumentImpl(value.getData(), symbols, new NodeSource(this, key));
- flushSymbolTable();
- if (log.isTraceEnabled()) {
- log.trace(localDebugHeader +
- "Compressed XML document=<" + TextWriter.toString(document) + ">");
- }
-
- if (documentCache != null) {
- documentCache.putDocumentEntry(this, key, value.getData(), entryMeta);
- }
- } else {
- String documentChars = value.toString();
- if (log.isTraceEnabled()) {
- log.trace(localDebugHeader + "Pre parseDocument(): value=<" + documentChars + ">");
- }
-
- // FIXME There should be no reason here to re-compress the document & flush symbols table?
- document = parseDocument(key, documentChars);
- flushSymbolTable();
-
- if (documentCache != null) {
- documentCache.putDocumentEntry(this, key, documentChars, entryMeta);
- }
- }
-
- DBObserver.getInstance().loadDocument(this, record, document);
- return new Entry(key, document, entryMeta);
- } else {
- if (log.isTraceEnabled()) {
- log.trace(localDebugHeader + "Binary document");
- }
-
- return new Entry(key, value.getData(), entryMeta);
- }
- }
- }
-
- /**
- * Retrieve a database entry metadata by key.
- *
- * If no matching entry is found, null is returned, otherwise this method
- * return Entry that holds metadata only.
- *
- * @param docKey identifying the desired database entry
- * @return Entry containing the metadata of the database entry, or null if no
- * matching entry is found
- * @throws DBException in case of backing store error,
- * and in case of header corruption
- */
- public final Entry getEntryMeta(Object docKey) throws DBException {
- if (docKey == null) {
- return null;
- }
-
- checkFiler(FaultCodes.COL_NO_FILER);
-
- Key key = getIdentityKey(createNewKey(docKey));
- synchronized (key) {
- /*
- * If the key has a corresponding value in the cache, return it
- * and save a disk access.
- */
- if (documentCache != null) {
- Entry entry = documentCache.getEntryMeta(this, key);
- if (entry != null) {
- return entry;
- }
- }
-
- Record record = filer.readRecord(key, true);
- if (record == null) {
- return null;
- }
-
- Map entryMeta = Entry.createMetaMap(record);
- return new Entry(key, entryMeta);
- }
- }
-
- /**
- * getIndexer retrieves an Indexer by name.
- *
- * @param name The Indexer name
- * @return The Indexer (or null)
- * @throws DBException if operation failed
- */
- public final Indexer getIndexer(String name) throws DBException {
- checkFiler(FaultCodes.COL_NO_INDEXMANAGER);
- return indexManager.get(name);
- }
-
- /**
- * return the IndexManager being used by this Collection.
- *
- * @return The IndexManager
- * @throws DBException if operation failed
- */
- public final IndexManager getIndexManager() throws DBException {
- checkFiler(FaultCodes.COL_NO_INDEXMANAGER);
- return indexManager;
- }
-
- /**
* getObject instantiates and returns an XMLSerializable object based on the
* provided Key. Xindice takes care of instantiating the correct class, but
* only if a class was registered with the Document in the first place.
@@ -1154,46 +990,40 @@
}
/**
- * Insert a binary object into a Xindice Collection. A unique key
- * is automatically generated. by which the binary object can be
- * retrieved in the future. Note: because the key is automatically
- * unique, this insert method will never cause a collision with an
- * object already in the database.
+ * Retrieve a binary database entry by key.
+ * This low-level method will not update non-inline metadata.
*
- * @param bytes The bytes making up the binary object to insert
- * @return Key automatically generated for the binary object
- * @throws DBException if inline-metadata is not enabled, or an
- * error occurs while saving.
+ * @param key identifying the desired database entry
+ * @return byte[] containing the binary database entry
+ * @throws DBException if inline-metadata is not enabled
+ * (binary resource cannot be stored in a collection
+ * which does not have inline-metadata enabled),
+ * in case of backing store error, and in case of
+ * header corruption
*/
- public Key insertBinary(byte[] bytes) throws DBException {
- return insertBinary(null, bytes);
- }
+ public final byte[] getBinary(Object key) throws DBException {
+ if (log.isTraceEnabled()) {
+ log.trace(debugHeader() + "Get binary: " + key);
+ }
- /**
- * insertBinary inserts a new binary object into a Xindice Collection.
- *
- * @param docKey The document Key
- * @param bytes The document to insert
- * @return key for the inserted binary
- * @throws DBException if inline-metadata is not enabled, the key is
- * already in the database, or an error occurs while saving.
- */
- public Key insertBinary(Object docKey, byte[] bytes) throws DBException {
if (inlineMetaService == null) {
- throw new DBException(FaultCodes.COL_CANNOT_STORE,
- "Cannot insert a binary resource in '" + getCanonicalName() +
- "' (inline-metadata is not enabled)");
+ throw new DBException(FaultCodes.COL_DOCUMENT_NOT_FOUND,
+ "Collection '" + getCanonicalName() +
+ "' has no binary resources (inline metadata is not enabled)");
}
- Key key = createNewKey(docKey);
- if (log.isInfoEnabled()) {
- log.info(debugHeader() + "Insert binary: " + key);
+ Entry entry = getEntry(key);
+ if (entry == null) {
+ return null;
}
- putBinary(key, bytes, ACTION_INSERT);
- // update the meta information if necessary
- updateCollectionMeta();
- return key;
+ if (entry.getEntryType() != Entry.BINARY) {
+ throw new DBException(FaultCodes.COL_INVALID_RESULT,
+ "Resource '" + key + "' in collection '" +
+ getCanonicalName() + "' is not a binary resource");
+ }
+
+ return (byte[]) entry.getValue();
}
/**
@@ -1263,129 +1093,112 @@
}
/**
- * Returns whether or not meta data is enabled.
+ * Insert a binary object into a Xindice Collection. A unique key
+ * is automatically generated. by which the binary object can be
+ * retrieved in the future. Note: because the key is automatically
+ * unique, this insert method will never cause a collision with an
+ * object already in the database.
*
- * @return boolean whether or not meta data is enabled.
+ * @param bytes The bytes making up the binary object to insert
+ * @return Key automatically generated for the binary object
+ * @throws DBException if inline-metadata is not enabled, or an
+ * error occurs while saving.
*/
- public boolean isMetaEnabled() {
- return getDatabase().isMetaEnabled();
+ public Key insertBinary(byte[] bytes) throws DBException {
+ return insertBinary(null, bytes);
+ }
+
+ /**
+ * insertBinary inserts a new binary object into a Xindice Collection.
+ *
+ * @param docKey The document Key
+ * @param bytes The document to insert
+ * @return key for the inserted binary
+ * @throws DBException if inline-metadata is not enabled, the key is
+ * already in the database, or an error occurs while saving.
+ */
+ public Key insertBinary(Object docKey, byte[] bytes) throws DBException {
+ if (inlineMetaService == null) {
+ throw new DBException(FaultCodes.COL_CANNOT_STORE,
+ "Cannot insert a binary resource in '" + getCanonicalName() +
+ "' (inline-metadata is not enabled)");
+ }
+
+ Key key = createNewKey(docKey);
+ if (log.isInfoEnabled()) {
+ log.info(debugHeader() + "Insert binary: " + key);
+ }
+ putBinary(key, bytes, ACTION_INSERT);
+
+ // update the meta information if necessary
+ updateCollectionMeta();
+ return key;
}
/**
- * listDocuments returns a list of all entry keys stored by this
- * collection.
+ * setDocument inserts or updates an existing Document in a
+ * Xindice Collection.
*
- * @return the list of entry keys
+ * @param docKey The Document Key
+ * @param document The Document
+ * @return True if new document entry was created, false otherwise
* @throws DBException if operation failed
*/
- public final String[] listDocuments() throws DBException {
- // a collection in which you are unable to file documents will have no filer
- // (for example the root collection). Rather than throwing an exception return
- // a constant result (nothing)
- if (null == filer) {
- return EMPTY_STRING_ARRAY;
- } else {
- // TODO: ArrayList length is limited to the int, while filer record count is long
-
- // give a hint to the size of the record set, saves on arraylist array copies.
- ArrayList temp = new ArrayList((int) filer.getRecordCount());
-
- RecordSet set = filer.getRecordSet();
- while (set.hasMoreRecords()) {
- Key key = set.getNextKey();
- temp.add(key.toString());
- }
+ public final boolean setDocument(Object docKey, Document document) throws DBException {
+ if (log.isInfoEnabled()) {
+ log.info(debugHeader() + "Set document " + docKey);
+ }
- return (String[]) temp.toArray(new String[temp.size()]);
+ boolean res = putDocument(createNewKey(docKey), document, ACTION_STORE);
+ if (res) {
+ updateCollectionMeta();
}
- }
- /**
- * listIndexers returns a list of the currently registered Indexers
- * as an array of String.
- *
- * @return The Indexer list
- * @throws DBException if operation failed
- */
- public final String[] listIndexers() throws DBException {
- checkFiler(FaultCodes.COL_NO_INDEXMANAGER);
- return indexManager.list();
+ return res;
}
/**
- * Turns an XML string into a parsed document retrieved
- * from the uncompressed collection.
+ * setObject sets an XMLSerializable object in the Collection based on the
+ * provided Key. Xindice takes care of associating the implementation class
+ * with the XMLSerializable object.
*
- * @param key The key to use when caching
- * @param xml The string to parse
- * @return A parsed DOM document or null if failure
+ * @param key The Key to use
+ * @param obj The Object to set
* @throws DBException if operation failed
*/
- private Document parseDocument(Key key, String xml) throws DBException {
- try {
- Document doc = DOMParser.toDocument(xml);
- ((DBDocument) doc).setSource(new NodeSource(this, key));
-
- // Have to compress to update collection's SymbolTable,
- // which is used even for uncompressed collections
- DOMCompressor.compress(doc, symbols);
-
- return doc;
- } catch (Exception e) {
- throw new DBException(FaultCodes.COL_DOCUMENT_MALFORMED,
- "Unable to parse document '" + key + "' in '" + getCanonicalName() + "'", e);
+ public final void setObject(Object key, XMLSerializable obj) throws DBException {
+ if (log.isInfoEnabled()) {
+ log.info(debugHeader() + "Set object " + key);
}
+ putObject(createNewKey(key), obj, ACTION_STORE);
}
/**
- * Lowest-level method for saving a binary entry into the database. At this moment,
- * presence of inline metadata is known.
- * It now does update non-inline metadata if the user has configured it.
- * <br/><br/>
- * putBinary attempts to perform requested action, and success depends on action
- * and presense of the key in the collection.
- * <br/><br/>
- * @param key Entry key
- * @param bytes Value
- * @param action It can be either ACTION_INSERT, ACTION_UPDATE or ACTION_STORE
- * @return True if new binary entry was created, false otherwise
- * @throws DBException<ul>
- * <li>FaultCodes.COL_DUPLICATE_RESOURCE If entry with that key already present in
- * collection and action is ACTION_INSERT</li>
- * <li>FaultCodes.COL_DOCUMENT_NOT_FOUND If entry with that key is not present in
- * collection and action is ACTION_UPDATE
- * </ul>
+ * setBinary inserts or updates binary object into a Xindice Collection.
+ *
+ * @param docKey The document Key
+ * @param bytes The document to insert
+ * @return true if new binary was created, false otherwise
+ * @throws DBException if inline-metadata is not enabled, the key is
+ * already in the database, or an error occurs while saving.
*/
- private boolean putBinary(Key key, byte[] bytes, byte action) throws DBException {
- synchronized (getIdentityKey(key)) {
- Entry entry = getEntry(key);
- if (action == ACTION_INSERT && entry != null) {
- throw new DBException(FaultCodes.COL_DUPLICATE_RESOURCE,
- "Error inserting binary resource '" + key + "' in '" + getCanonicalName() +
- "': key is already in database");
- } else if (action == ACTION_UPDATE && entry == null) {
- throw new DBException(FaultCodes.COL_DOCUMENT_NOT_FOUND,
- "Error updating binary resource '" + key + "' in '" + getCanonicalName() +
- "': key does not exist in database");
- }
-
- if (entry != null && entry.getEntryType() == Entry.DOCUMENT) {
- // binary resources aren't stored in cache or indexes
- if (documentCache != null) {
- documentCache.removeEntry(this, key);
- }
- indexManager.removeDocument(key, (Document) entry.getValue());
- }
+ public boolean setBinary(Object docKey, byte[] bytes) throws DBException {
+ if (inlineMetaService == null) {
+ throw new DBException(FaultCodes.COL_CANNOT_STORE,
+ "Cannot insert a binary resource in '" + getCanonicalName() +
+ "' (inline-metadata is not enabled)");
+ }
- InlineMetaMap map = inlineMetaService.getEmptyMap();
- map.put("type", ResourceTypeReader.BINARY);
- Value value = inlineMetaService.createValue(map, bytes, 0, bytes.length);
- Record record = filer.writeRecord(key, value);
+ if (log.isInfoEnabled()) {
+ log.info(debugHeader() + "Set binary " + docKey);
+ }
- // update the meta for this document
- updateDocumentMeta(record);
- return entry == null;
+ boolean res = putBinary(createNewKey(docKey), bytes, ACTION_STORE);
+ if (res) {
+ updateCollectionMeta();
}
+
+ return res;
}
/**
@@ -1541,50 +1354,54 @@
}
/**
- * queryCollection performs a query against the current collection
- * using the specified style and query String.
- *
- * @param style The query style to use (ex: XPath)
- * @param query The query to execute
- * @param nsMap The namespace Map (if any)
- * @return The resulting NodeSet
- * @throws DBException if operation failed
+ * Lowest-level method for saving a binary entry into the database. At this moment,
+ * presence of inline metadata is known.
+ * It now does update non-inline metadata if the user has configured it.
+ * <br/><br/>
+ * putBinary attempts to perform requested action, and success depends on action
+ * and presense of the key in the collection.
+ * <br/><br/>
+ * @param key Entry key
+ * @param bytes Value
+ * @param action It can be either ACTION_INSERT, ACTION_UPDATE or ACTION_STORE
+ * @return True if new binary entry was created, false otherwise
+ * @throws DBException<ul>
+ * <li>FaultCodes.COL_DUPLICATE_RESOURCE If entry with that key already present in
+ * collection and action is ACTION_INSERT</li>
+ * <li>FaultCodes.COL_DOCUMENT_NOT_FOUND If entry with that key is not present in
+ * collection and action is ACTION_UPDATE
+ * </ul>
*/
- public final NodeSet queryCollection(String style, String query, NamespaceMap nsMap) throws DBException {
- if (log.isDebugEnabled()) {
- log.debug(debugHeader() + "Query collection, query " + query);
- }
+ private boolean putBinary(Key key, byte[] bytes, byte action) throws DBException {
+ synchronized (getIdentityKey(key)) {
+ Entry entry = getEntry(key);
+ if (action == ACTION_INSERT && entry != null) {
+ throw new DBException(FaultCodes.COL_DUPLICATE_RESOURCE,
+ "Error inserting binary resource '" + key + "' in '" + getCanonicalName() +
+ "': key is already in database");
+ } else if (action == ACTION_UPDATE && entry == null) {
+ throw new DBException(FaultCodes.COL_DOCUMENT_NOT_FOUND,
+ "Error updating binary resource '" + key + "' in '" + getCanonicalName() +
+ "': key does not exist in database");
+ }
- // A collection in which you are unable to file documents will have no filer
- // (for example the root collection). Rather than throwing an exception return
- // a constant result (nothing)
- return null == filer ? EMPTY_NODESET : getQueryEngine().query(this, style, query, nsMap, null);
- }
+ if (entry != null && entry.getEntryType() == Entry.DOCUMENT) {
+ // binary resources aren't stored in cache or indexes
+ if (documentCache != null) {
+ documentCache.removeEntry(this, key);
+ }
+ indexManager.removeDocument(key, (Document) entry.getValue());
+ }
- /**
- * queryDocument performs a query against a single Document using
- * the specified style, query string, and Document ID.
- *
- * @param style The query style to use (ex: XPath)
- * @param query The query to execute
- * @param nsMap The namespace Map (if any)
- * @param key The Document to query
- * @return The resulting NodeSet
- * @throws DBException if operation failed
- */
- public final NodeSet queryDocument(String style, String query, NamespaceMap nsMap, Object key) throws DBException {
- if (log.isInfoEnabled()) {
- log.info(debugHeader() + "Query document " + key + ", query: " + query);
- }
+ InlineMetaMap map = inlineMetaService.getEmptyMap();
+ map.put("type", ResourceTypeReader.BINARY);
+ Value value = inlineMetaService.createValue(map, bytes, 0, bytes.length);
+ Record record = filer.writeRecord(key, value);
- checkFiler(FaultCodes.QRY_STYLE_NOT_FOUND);
- Key[] k;
- if (key instanceof Key[]) {
- k = (Key[]) key;
- } else {
- k = new Key[]{createNewKey(key)};
+ // update the meta for this document
+ updateDocumentMeta(record);
+ return entry == null;
}
- return getQueryEngine().query(this, style, query, nsMap, k);
}
/**
@@ -1626,7 +1443,47 @@
getMetaSystemCollection().dropDocumentMeta(this, objKey.toString());
}
}
- DBObserver.getInstance().dropDocument(this, objKey);
+ DBObserver.getInstance().dropDocument(this, objKey);
+ }
+
+
+ // -- Core Collection API Public Methods: Meta Data Management ----------
+
+ /**
+ * Returns whether or not meta data is enabled.
+ *
+ * @return boolean whether or not meta data is enabled.
+ */
+ public boolean isMetaEnabled() {
+ return getDatabase().isMetaEnabled();
+ }
+
+ /**
+ * Return the MetaData for this collection.
+ *
+ * If metadata is not enabled in the configuration, the MetaData object
+ * returned will be null.
+ *
+ * @return MetaData this collection's metadata.
+ * @throws DBException if operation failed
+ */
+ public MetaData getCollectionMeta() throws DBException {
+ if (!isMetaEnabled()) {
+ if (log.isWarnEnabled()) {
+ log.warn("Meta information requested but not enabled in config!");
+ }
+ return null;
+ }
+
+ MetaSystemCollection metacol = getMetaSystemCollection();
+ MetaData meta = metacol.getCollectionMeta(this);
+ if (null == meta) {
+ long now = System.currentTimeMillis();
+ meta = new MetaData(MetaData.COLLECTION, getCanonicalName(), now, now);
+ metacol.setCollectionMeta(this, meta);
+ }
+
+ return meta;
}
/**
@@ -1657,53 +1514,99 @@
}
/**
- * setDocument inserts or updates an existing Document in a
- * Xindice Collection.
+ * Retrieve a database entry metadata by key.
*
- * @param docKey The Document Key
- * @param document The Document
- * @return True if new document entry was created, false otherwise
- * @throws DBException if operation failed
+ * If no matching entry is found, null is returned, otherwise this method
+ * return Entry that holds metadata only.
+ *
+ * @param docKey identifying the desired database entry
+ * @return Entry containing the metadata of the database entry, or null if no
+ * matching entry is found
+ * @throws DBException in case of backing store error,
+ * and in case of header corruption
*/
- public final boolean setDocument(Object docKey, Document document) throws DBException {
- if (log.isInfoEnabled()) {
- log.info(debugHeader() + "Set document " + docKey);
+ public final Entry getEntryMeta(Object docKey) throws DBException {
+ if (docKey == null) {
+ return null;
}
- boolean res = putDocument(createNewKey(docKey), document, ACTION_STORE);
- if (res) {
- updateCollectionMeta();
- }
+ checkFiler(FaultCodes.COL_NO_FILER);
- return res;
+ Key key = getIdentityKey(createNewKey(docKey));
+ synchronized (key) {
+ /*
+ * If the key has a corresponding value in the cache, return it
+ * and save a disk access.
+ */
+ if (documentCache != null) {
+ Entry entry = documentCache.getEntryMeta(this, key);
+ if (entry != null) {
+ return entry;
+ }
+ }
+
+ Record record = filer.readRecord(key, true);
+ if (record == null) {
+ return null;
+ }
+
+ Map entryMeta = Entry.createMetaMap(record);
+ return new Entry(key, entryMeta);
+ }
}
/**
- * setBinary inserts or updates binary object into a Xindice Collection.
+ * Return the MetaData object for a document within this collection.
+ * If metadata is not enabled, the MetaData object returned will be null.
*
- * @param docKey The document Key
- * @param bytes The document to insert
- * @return true if new binary was created, false otherwise
- * @throws DBException if inline-metadata is not enabled, the key is
- * already in the database, or an error occurs while saving.
+ * @param id the document whose metadata you want
+ * @return meta data for requested resource
+ * @throws DBException if operation failed
*/
- public boolean setBinary(Object docKey, byte[] bytes) throws DBException {
- if (inlineMetaService == null) {
- throw new DBException(FaultCodes.COL_CANNOT_STORE,
- "Cannot insert a binary resource in '" + getCanonicalName() +
- "' (inline-metadata is not enabled)");
+ public MetaData getDocumentMeta(String id) throws DBException {
+ if (!isMetaEnabled()) {
+ if (log.isWarnEnabled()) {
+ log.warn("Meta information requested but not enabled in config!");
+ }
+ return null;
}
- if (log.isInfoEnabled()) {
- log.info(debugHeader() + "Set binary " + docKey);
- }
+ Key key = getIdentityKey(createNewKey(id));
+ synchronized (key) {
+ if (getEntry(id) == null) {
+ throw new DBException(FaultCodes.COL_DOCUMENT_NOT_FOUND,
+ "Resource '" + id + "' does not exist in '" + getCanonicalName() + "'");
+ }
- boolean res = putBinary(createNewKey(docKey), bytes, ACTION_STORE);
- if (res) {
- updateCollectionMeta();
- }
+ MetaSystemCollection metacol = getMetaSystemCollection();
+ MetaData meta = metacol.getDocumentMeta(this, id);
- return res;
+ /*
+ FIXME It is more efficient to store (and retrieve) created/modified timestamps
+ from the Record itself instead of storing them in the separate MetaData
+ object. Storing in the Record avoids writing two documents on each update
+ (Document itself and its MetaData).
+ Retrieval of the timestamps from Record can be implemented via TimeRecord.
+
+ TimeRecord rec = null;
+ if( null == meta || !meta.hasContext() )
+ rec = getDatabase().getTime(path);
+
+ long created = (null != rec) ? rec.getCreatedTime() : System.currentTimeMillis();
+ long modified = (null != rec) ? rec.getModifiedTime() : System.currentTimeMillis();
+ */
+
+ // this is wrong.. but it should work for now...
+ long now = System.currentTimeMillis();
+ if (meta == null) {
+ meta = new MetaData(MetaData.DOCUMENT, getCanonicalDocumentName(id), now, now);
+ metacol.setDocumentMeta(this, id, meta);
+ } else if (!meta.hasContext()) {
+ meta.setContext(now, now);
+ }
+
+ return meta;
+ }
}
/**
@@ -1746,22 +1649,6 @@
}
/**
- * setObject sets an XMLSerializable object in the Collection based on the
- * provided Key. Xindice takes care of associating the implementation class
- * with the XMLSerializable object.
- *
- * @param key The Key to use
- * @param obj The Object to set
- * @throws DBException if operation failed
- */
- public final void setObject(Object key, XMLSerializable obj) throws DBException {
- if (log.isInfoEnabled()) {
- log.info(debugHeader() + "Set object " + key);
- }
- putObject(createNewKey(key), obj, ACTION_STORE);
- }
-
- /**
* update the modified time of this collection when appropriate
*/
protected void updateCollectionMeta() {
@@ -1843,5 +1730,127 @@
}
metacol.setDocumentMeta(this, id, meta);
+ }
+
+
+ // ----------------------------------------------------------------------
+
+ /**
+ * getContainer retrieves a Container from the Collection. The Container
+ * encapsulates all information needed in dealing with a Document outside
+ * of the context of a Collection (ex: DocumentContext).
+ *
+ * @param docKey The Document Key
+ * @return The Container
+ * @throws DBException if operation failed
+ */
+ public final Container getContainer(Object docKey) throws DBException {
+ Key key = createNewKey(docKey);
+ Document doc = getDocument(key);
+ return doc != null ? new ColContainer(key, doc) : null;
+ }
+
+ /**
+ * getDocumentCount returns the count of Documents being maintained
+ * by this Collection.
+ *
+ * @return The Document count
+ * @throws DBException if operation failed
+ */
+ public final long getDocumentCount() throws DBException {
+ // a collection in which you are unable to file documents will have no filer
+ // (for example the root collection). Rather than throwing an exception return
+ // a constant result (nothing)
+ return null == filer ? 0 : filer.getRecordCount();
+ }
+
+ /**
+ * getDocumentSet returns the set of Documents being maintained
+ * by this Collection.
+ *
+ * @return The DocumentSet
+ * @throws DBException if operation failed
+ */
+ public final DocumentSet getDocumentSet() throws DBException {
+ // a collection in which you are unable to file documents will have no filer
+ // (for example the root collection). Rather than throwing an exception return
+ // a constant result (nothing)
+ return null == filer ? EMPTY_DOCUMENTSET : new ColDocumentSet(filer.getRecordSet());
+ }
+
+ /**
+ * listDocuments returns a list of all entry keys stored by this
+ * collection.
+ *
+ * @return the list of entry keys
+ * @throws DBException if operation failed
+ */
+ public final String[] listDocuments() throws DBException {
+ // a collection in which you are unable to file documents will have no filer
+ // (for example the root collection). Rather than throwing an exception return
+ // a constant result (nothing)
+ if (null == filer) {
+ return EMPTY_STRING_ARRAY;
+ } else {
+ // TODO: ArrayList length is limited to the int, while filer record count is long
+
+ // give a hint to the size of the record set, saves on arraylist array copies.
+ ArrayList temp = new ArrayList((int) filer.getRecordCount());
+
+ RecordSet set = filer.getRecordSet();
+ while (set.hasMoreRecords()) {
+ Key key = set.getNextKey();
+ temp.add(key.toString());
+ }
+
+ return (String[]) temp.toArray(new String[temp.size()]);
+ }
+ }
+
+ /**
+ * queryCollection performs a query against the current collection
+ * using the specified style and query String.
+ *
+ * @param style The query style to use (ex: XPath)
+ * @param query The query to execute
+ * @param nsMap The namespace Map (if any)
+ * @return The resulting NodeSet
+ * @throws DBException if operation failed
+ */
+ public final NodeSet queryCollection(String style, String query, NamespaceMap nsMap) throws DBException {
+ if (log.isDebugEnabled()) {
+ log.debug(debugHeader() + "Query collection, query " + query);
+ }
+
+ // A collection in which you are unable to file documents will have no filer
+ // (for example the root collection). Rather than throwing an exception return
+ // a constant result (nothing)
+ return null == filer ? EMPTY_NODESET : getQueryEngine().query(this, style, query, nsMap, null);
+ }
+
+ /**
+ * queryDocument performs a query against a single Document using
+ * the specified style, query string, and Document ID.
+ *
+ * @param style The query style to use (ex: XPath)
+ * @param query The query to execute
+ * @param nsMap The namespace Map (if any)
+ * @param key The Document to query
+ * @return The resulting NodeSet
+ * @throws DBException if operation failed
+ */
+ public final NodeSet queryDocument(String style, String query, NamespaceMap nsMap, Object key) throws DBException {
+ if (log.isInfoEnabled()) {
+ log.info(debugHeader() + "Query document " + key + ", query: " + query);
+ }
+
+ checkFiler(FaultCodes.QRY_STYLE_NOT_FOUND);
+ Key[] k;
+ if (key instanceof Key[]) {
+ k = (Key[]) key;
+ } else {
+ k = new Key[]{createNewKey(key)};
+ }
+ return getQueryEngine().query(this, style, query, nsMap, k);
}
}