package com.dbxml.db.server.labrador;
/*
* dbXML - Native XML Database
* Copyright (c) 1999-2006 The dbXML Group, L.L.C.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* $Id: CollectionProxy.java,v 1.9 2006/02/02 19:04:15 bradford Exp $
*/
import com.dbxml.db.core.Collection;
import com.dbxml.db.core.DBException;
import com.dbxml.db.core.FaultCodes;
import com.dbxml.db.core.data.Key;
import com.dbxml.db.core.data.Record;
import com.dbxml.db.core.data.Value;
import com.dbxml.db.core.query.helpers.ResultSetWrapper;
import com.dbxml.db.core.security.Access;
import com.dbxml.db.core.security.AccessUtils;
import com.dbxml.db.core.security.Role;
import com.dbxml.db.core.transaction.Transaction;
import com.dbxml.labrador.Handler;
import com.dbxml.labrador.Headers;
import com.dbxml.labrador.broker.Broker;
import com.dbxml.labrador.broker.BrokerContext;
import com.dbxml.labrador.rest.RESTHandler;
import com.dbxml.util.Configuration;
import com.dbxml.xml.NamespaceMap;
import com.dbxml.xml.dom.DOMHelper;
import com.dbxml.xml.dtsm.DTSMException;
import com.dbxml.xml.dtsm.DTSMHelper;
import com.dbxml.xml.dtsm.DocumentTable;
import java.util.HashMap;
import java.util.Map;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.ProcessingInstruction;
/**
* CollectionProxy is a proxy facade that is used to expose Collection
* functionality via Labrador.
*/
public final class CollectionProxy {
private static final int[] INTS = {Access.READ, Access.WRITE, Access.EXECUTE, Access.CREATE};
private static final String[] STRS = {"r", "w", "e", "c"};
private static final String PI_TARGET = "xml-stylesheet";
private static final String COLLECTION = "collection";
private static final String PATH = "path";
private static final String TYPE = "type";
private static final String TYPE_DOCUMENTS = "documents";
private static final String TYPE_RECORDS = "records";
private static final String CHILDREN = "children";
private static final String CHILD = "child";
private static final String INDEXES = "indexes";
private static final String INDEX = "index";
private static final String TRIGGERS = "triggers";
private static final String TRIGGER = "trigger";
private static final String EXTENSIONS = "extensions";
private static final String EXTENSION = "extension";
private static final String ROLES = "roles";
private static final String ROLE = "role";
private static final String PERMISSIONS = "permissions";
private static final String DOCUMENTS = "documents";
private static final String DOCUMENT = "document";
public static final String REST_DEFAULT_METHOD = "get";
public static final String NAME = "name";
public static final String PARENT_COLLECTION = "parentCollection";
public static final String CANONICAL_NAME = "canonicalName";
public static final String COLLECTION_TYPE = "collectionType";
private Collection col;
public CollectionProxy(Collection col) {
this.col = col;
}
public Map getInformation() throws DBException {
Map result = new HashMap(4);
result.put(NAME, col.getName());
result.put(CANONICAL_NAME, col.getCanonicalName());
result.put(COLLECTION_TYPE, new Integer(col.getCollectionType()));
try {
Collection parent = col.getParentCollection();
result.put(PARENT_COLLECTION, parent.getCanonicalName());
}
catch ( Exception e ) {
// NOOP
}
return result;
}
public Document get() throws DBException {
Document doc = DOMHelper.newDocument();
// Let's see if we can interact with Labrador's HTTP Server for REST calls
BrokerContext context = Broker.getInstance().getBrokerContext();
Handler handler = context.getHandler();
boolean restHandler = handler instanceof RESTHandler;
if ( restHandler ) {
StringBuffer sb = new StringBuffer();
sb.append("type=\"text/xsl\" href=\"");
sb.append(RESTHandler.PREFIX);
sb.append("stylesheets/collection.xsl");
sb.append('\"');
ProcessingInstruction pi = doc.createProcessingInstruction(PI_TARGET, sb.toString());
doc.appendChild(pi);
}
Element rootElem = doc.createElement(COLLECTION);
rootElem.setAttribute(PATH, col.getCanonicalName());
switch ( col.getCollectionType() ) {
case Collection.TYPE_DOCUMENTS:
rootElem.setAttribute(TYPE, TYPE_DOCUMENTS);
break;
case Collection.TYPE_RECORDS:
rootElem.setAttribute(TYPE, TYPE_RECORDS);
break;
}
doc.appendChild(rootElem);
try {
Element childrenElem = doc.createElement(CHILDREN);
rootElem.appendChild(childrenElem);
String[] cols = col.listCollections();
for ( int i = 0; i < cols.length; i++ ) {
Element childElem = doc.createElement(CHILD);
childElem.appendChild(doc.createTextNode(cols[i]));
childrenElem.appendChild(childElem);
}
}
catch ( DBException e ) {
}
try {
Element indexesElem = doc.createElement(INDEXES);
rootElem.appendChild(indexesElem);
String[] indexes = col.getIndexManager().list();
for ( int i = 0; i < indexes.length; i++ ) {
Element indexElem = doc.createElement(INDEX);
indexElem.appendChild(doc.createTextNode(indexes[i]));
indexesElem.appendChild(indexElem);
}
}
catch ( DBException e ) {
}
try {
Element triggersElem = doc.createElement(TRIGGERS);
rootElem.appendChild(triggersElem);
String[] triggers = col.getTriggerManager().list();
for ( int i = 0; i < triggers.length; i++ ) {
Element triggerElem = doc.createElement(TRIGGER);
triggerElem.appendChild(doc.createTextNode(triggers[i]));
triggersElem.appendChild(triggerElem);
}
}
catch ( DBException e ) {
}
try {
Element extensionsElem = doc.createElement(EXTENSIONS);
rootElem.appendChild(extensionsElem);
String[] extensions = col.getExtensionManager().list();
for ( int i = 0; i < extensions.length; i++ ) {
Element extensionElem = doc.createElement(EXTENSION);
extensionElem.appendChild(doc.createTextNode(extensions[i]));
extensionsElem.appendChild(extensionElem);
}
}
catch ( DBException e ) {
}
try {
Element rolesElem = doc.createElement(ROLES);
rootElem.appendChild(rolesElem);
AccessUtils utils = new AccessUtils(col.getDatabase());
Access access = utils.readAccess(col.getCanonicalName());
String[] roles = access.listRoles();
for ( int i = 0; i < roles.length; i++ ) {
Element roleElem = doc.createElement(ROLE);
roleElem.appendChild(doc.createTextNode(roles[i]));
rolesElem.appendChild(roleElem);
Role role = utils.readRole(roles[i]);
int permissions = access.getPermissions(role);
if ( permissions != 0 ) {
StringBuffer sb = new StringBuffer();
for ( int j = 0; j < STRS.length; j++ )
if ( (permissions & INTS[j]) != 0 )
sb.append(STRS[j]);
roleElem.setAttribute(PERMISSIONS, sb.toString());
}
}
}
catch ( DBException e ) {
}
Transaction tx = new Transaction();
try {
Element docsElem = doc.createElement(DOCUMENTS);
rootElem.appendChild(docsElem);
Key[] keys = col.listKeys(tx);
for ( int i = 0; i < keys.length; i++ ) {
Element docElem = doc.createElement(DOCUMENT);
docElem.appendChild(doc.createTextNode(keys[i].toString()));
docsElem.appendChild(docElem);
}
}
catch ( DBException e ) {
try {
tx.cancel();
}
catch ( DBException ex ) {
}
}
finally {
if ( tx.getStatus() == Transaction.ACTIVE )
tx.commit();
}
return doc;
}
public String getDatabase() {
return col.getDatabase().getCanonicalName();
}
public String getSystemCollection() throws DBException {
return col.getDatabase().getSystemCollection().getCanonicalName();
}
public static final String[] PARAMS_getCollection = {"name"};
public String getCollection(String name) throws DBException {
return col.getCollection(name).getCanonicalName();
}
public static final String[] PARAMS_createCollection = {"path", "configuration"};
public String createCollection(String path, Document configuration) throws DBException {
Configuration cfg = new Configuration(configuration);
return col.createCollection(path, cfg).getCanonicalName();
}
public String[] listCollections() throws DBException {
return col.listCollections();
}
public static final String[] PARAMS_dropCollection = {"name"};
public boolean dropCollection(String name) throws DBException {
Collection c = col.getCollection(name);
if ( c != null )
return col.dropCollection(c);
else
return false;
}
public static final String[] PARAMS_createTrigger = {"configuration"};
public String createTrigger(Document configuration) throws DBException {
Configuration cfg = new Configuration(configuration);
return col.getTriggerManager().create(cfg).getName();
}
public static final String[] PARAMS_dropTrigger = {"name"};
public boolean dropTrigger(String name) throws DBException {
return col.getTriggerManager().drop(name);
}
public String[] listTriggers() throws DBException {
return col.getTriggerManager().list();
}
public static final String[] PARAMS_createIndexer = {"configuration"};
public String createIndexer(Document configuration) throws DBException {
Configuration cfg = new Configuration(configuration);
return col.getIndexManager().create(cfg).getName();
}
public static final String[] PARAMS_dropIndexer = {"name"};
public boolean dropIndexer(String name) throws DBException {
return col.getIndexManager().drop(name);
}
public String[] listIndexers() throws DBException {
return col.getIndexManager().list();
}
public static final String[] PARAMS_getExtension = {"name"};
public String getExtension(String name) throws DBException {
return col.getExtensionManager().getCanonicalName(name);
}
public static final String[] PARAMS_createExtension = {"configuration"};
public String createExtension(Document configuration) throws DBException {
Configuration cfg = new Configuration(configuration);
String name = col.getExtensionManager().create(cfg).getName();
return col.getExtensionManager().getCanonicalName(name);
}
public String[] listExtensions() throws DBException {
return col.getExtensionManager().list();
}
public static final String[] PARAMS_dropExtension = {"name"};
public boolean dropExtension(String name) throws DBException {
return col.getExtensionManager().drop(name);
}
public String createKey() throws DBException {
return col.createNewOID().toString();
}
public static final String[] PARAMS_getDocument = {"docKey"};
public static final String REST_CONTENT_TYPE_getDocument = Headers.TYPE_TEXT_XML;
public String getDocument(String docKey) throws DBException {
Transaction tx = new Transaction();
try {
DocumentTable dt = col.getDocument(tx, docKey);
if ( dt == null )
throw new DBException(FaultCodes.COL_CANNOT_RETRIEVE, "Document '"+docKey+"' not found");
return DTSMHelper.tableToText(dt);
}
catch ( DBException e ) {
tx.cancel();
throw e;
}
catch ( DTSMException e ) {
tx.cancel();
throw new DBException(FaultCodes.COL_CANNOT_RETRIEVE, "Can't convert Document '" + docKey + "' to text", e);
}
finally {
if ( tx.getStatus() == Transaction.ACTIVE )
tx.commit();
}
}
public static final String[] PARAMS_insertDocument = {"document"};
public String insertDocument(String document) throws DBException {
Transaction tx = new Transaction();
try {
DocumentTable doc = DTSMHelper.textToTable(document, col.getSymbols());
return col.insertDocument(tx, doc).toString();
}
catch ( DBException e ) {
tx.cancel();
throw e;
}
catch ( DTSMException e ) {
tx.cancel();
throw new DBException(FaultCodes.COL_CANNOT_STORE, "Can't parse Document", e);
}
finally {
if ( tx.getStatus() == Transaction.ACTIVE )
tx.commit();
}
}
public static final String[] PARAMS_setDocument = {"docKey", "document"};
public void setDocument(String docKey, String document) throws DBException {
Transaction tx = new Transaction();
try {
DocumentTable dt = DTSMHelper.textToTable(document, col.getSymbols());
col.setDocument(tx, docKey, dt);
tx.commit();
}
catch ( DBException e ) {
tx.cancel();
throw e;
}
catch ( DTSMException e ) {
tx.cancel();
throw new DBException(FaultCodes.COL_CANNOT_STORE, "Can't parse Document '" + docKey + "'", e);
}
finally {
if ( tx.getStatus() == Transaction.ACTIVE )
tx.commit();
}
}
public static final String[] PARAMS_remove = {"docKey"};
public void remove(String docKey) throws DBException {
Transaction tx = new Transaction();
try {
col.remove(tx, docKey);
}
catch ( DBException e ) {
tx.cancel();
throw e;
}
finally {
if ( tx.getStatus() == Transaction.ACTIVE )
tx.commit();
}
}
public String[] listKeys() throws DBException {
Transaction tx = new Transaction();
try {
Key[] keys = col.listKeys(tx);
String[] result = new String[keys.length];
for ( int i = 0; i < keys.length; i++ )
result[i] = keys[i].toString();
return result;
}
catch ( DBException e ) {
tx.cancel();
throw e;
}
finally {
if ( tx.getStatus() == Transaction.ACTIVE )
tx.commit();
}
}
public long getKeyCount() throws DBException {
Transaction tx = new Transaction();
try {
return col.getKeyCount(tx);
}
catch ( DBException e ) {
tx.cancel();
throw e;
}
finally {
if ( tx.getStatus() == Transaction.ACTIVE )
tx.commit();
}
}
public static final String[] PARAMS_insertValue = {"value"};
public String insertValue(byte[] value) throws DBException {
Transaction tx = new Transaction();
try {
return col.insertRecord(tx, new Value(value)).toString();
}
catch ( DBException e ) {
tx.cancel();
throw e;
}
finally {
if ( tx.getStatus() == Transaction.ACTIVE )
tx.commit();
}
}
public static final String[] PARAMS_setValue = {"key", "value"};
public void setValue(String key, byte[] value) throws DBException {
Transaction tx = new Transaction();
try {
col.setRecord(tx, key, new Value(value));
}
catch ( DBException e ) {
tx.cancel();
throw e;
}
finally {
if ( tx.getStatus() == Transaction.ACTIVE )
tx.commit();
}
}
public static final String[] PARAMS_getValue = {"key"};
public byte[] getValue(String key) throws DBException {
Transaction tx = new Transaction();
try {
Record rec = col.getRecord(tx, key);
if ( rec == null )
throw new DBException(FaultCodes.COL_CANNOT_RETRIEVE, "Record '"+key+"' not found");
return rec.getValue().getData();
}
catch ( DBException e ) {
tx.cancel();
throw e;
}
finally {
if ( tx.getStatus() == Transaction.ACTIVE )
tx.commit();
}
}
public static final String[] PARAMS_queryCollection = {"style", "query", "nsMap"};
public static final String REST_CONTENT_TYPE_queryCollection = Headers.TYPE_TEXT_XML;
public String queryCollection(String style, String query, Map nsMap) throws DBException {
Transaction tx = new Transaction();
try {
return ResultSetWrapper.toText(col.queryCollection(tx, style, query, new NamespaceMap(nsMap)));
}
catch ( DBException e ) {
tx.cancel();
throw e;
}
finally {
if ( tx.getStatus() == Transaction.ACTIVE )
tx.commit();
}
}
public static final String[] PARAMS_queryDocument = {"style", "query", "nsMap", "key"};
public static final String REST_CONTENT_TYPE_queryDocument = Headers.TYPE_TEXT_XML;
public String queryDocument(String style, String query, Map nsMap, String key) throws DBException {
Transaction tx = new Transaction();
try {
return ResultSetWrapper.toText(col.queryDocument(tx, style, query, new NamespaceMap(nsMap), key));
}
catch ( DBException e ) {
tx.cancel();
throw e;
}
finally {
if ( tx.getStatus() == Transaction.ACTIVE )
tx.commit();
}
}
}