package com.c2kernel.persistency.xmldb; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; import java.util.ArrayList; import org.xmldb.api.DatabaseManager; import org.xmldb.api.base.Collection; import org.xmldb.api.base.Database; import org.xmldb.api.base.ErrorCodes; import org.xmldb.api.base.Resource; import org.xmldb.api.base.XMLDBException; import org.xmldb.api.modules.CollectionManagementService; import com.c2kernel.entity.C2KLocalObject; import com.c2kernel.persistency.ClusterStorage; import com.c2kernel.persistency.ClusterStorageException; import com.c2kernel.persistency.outcome.Outcome; import com.c2kernel.process.Gateway; import com.c2kernel.process.auth.Authenticator; import com.c2kernel.utils.Logger; public class XMLDBClusterStorage extends ClusterStorage { public static final String XMLDB_URI = "XMLDB.URI"; public static final String XMLDB_USER = "XMLDB.user"; public static final String XMLDB_PASSWORD = "XMLDB.password"; public static final String XMLDB_ROOT = "XMLDB.root"; protected Database database; protected Collection root; public XMLDBClusterStorage() throws Exception { } protected static Collection verifyCollection(Collection parent, String name, boolean create) throws ClusterStorageException { Collection coll; try { coll = parent.getChildCollection(name); if (coll == null) throw new XMLDBException(ErrorCodes.NO_SUCH_COLLECTION); } catch (XMLDBException ex) { if (ex.errorCode == ErrorCodes.NO_SUCH_COLLECTION) { if (create) { try { CollectionManagementService collManager = (CollectionManagementService)parent.getService("CollectionManagementService", "1.0"); coll = collManager.createCollection(name); } catch (Exception ex2) { throw new ClusterStorageException("Could not create XMLDB collection for item "+name); } } else // not found return null; } else { Logger.error(ex); throw new ClusterStorageException("Error loading XMLDB collection for item "+name); } } return coll; } /* (non-Javadoc) * @see com.c2kernel.persistency.ClusterStorage#open() */ @Override public void open(Authenticator auth) throws ClusterStorageException { final String driver = "org.exist.xmldb.DatabaseImpl"; // Uncomment the following for integrated existdb //System.setProperty("exist.initdb", "true"); //System.setProperty("exist.home", Gateway.getProperty("XMLDB.home")); try { Class cl = Class.forName(driver); database = (Database) cl.newInstance(); database.setProperty("create-database", "true"); DatabaseManager.registerDatabase(database); Collection db = DatabaseManager.getCollection(Gateway.getProperties().getProperty(XMLDB_URI), Gateway.getProperties().getProperty(XMLDB_USER), Gateway.getProperties().getProperty(XMLDB_PASSWORD)); String rootColl = Gateway.getProperties().getProperty(XMLDB_ROOT); if (rootColl != null && rootColl.length()>0) { root = verifyCollection(db, rootColl, true); db.close(); } else root = db; } catch (Exception ex) { Logger.error(ex); throw new ClusterStorageException("Error initializing XMLDB"); } if (root == null) throw new ClusterStorageException("Root collection is null. Problem connecting to XMLDB."); } /* (non-Javadoc) * @see com.c2kernel.persistency.ClusterStorage#close() */ @Override public void close() throws ClusterStorageException { try { root.close(); //DatabaseInstanceManager manager = (DatabaseInstanceManager)db.getService("DatabaseInstanceManager", "1.0"); //manager.shutdown(); } catch (XMLDBException e) { Logger.error(e); throw new ClusterStorageException("Error shutting down eXist XMLDB"); } } /* (non-Javadoc) * @see com.c2kernel.persistency.ClusterStorage#queryClusterSupport(java.lang.String) */ @Override public short queryClusterSupport(String clusterType) { return READWRITE; } /* (non-Javadoc) * @see com.c2kernel.persistency.ClusterStorage#getName() */ @Override public String getName() { return "XMLDB"; } /* (non-Javadoc) * @see com.c2kernel.persistency.ClusterStorage#getId() */ @Override public String getId() { return "XMLDB"; } /* (non-Javadoc) * @see com.c2kernel.persistency.ClusterStorage#get(java.lang.Integer, java.lang.String) */ @Override public C2KLocalObject get(Integer sysKey, String path) throws ClusterStorageException { String type = ClusterStorage.getClusterType(path); // Get item collection String strSysKey = String.valueOf(sysKey); Collection itemColl = verifyCollection(root, strSysKey, false); if (itemColl == null) return null; // doesn't exist try { String resourceName = path.replace('/', '.'); Resource resource = itemColl.getResource(resourceName); if (resource != null) { String objString = (String)resource.getContent(); itemColl.close(); if (type.equals(OUTCOME)) return new Outcome(path, objString); else { C2KLocalObject obj = (C2KLocalObject)Gateway.getMarshaller().unmarshall(objString); return obj; } } else return null; } catch (Exception e) { Logger.error(e); throw new ClusterStorageException("XMLDB error"); } } /* (non-Javadoc) * @see com.c2kernel.persistency.ClusterStorage#put(java.lang.Integer, com.c2kernel.entity.C2KLocalObject) */ @Override public void put(Integer sysKey, C2KLocalObject obj) throws ClusterStorageException { String resName = getPath(obj); String strSysKey = String.valueOf(sysKey); Collection itemColl = verifyCollection(root, strSysKey, true); try { resName = resName.replace('/', '.'); String objString = Gateway.getMarshaller().marshall(obj); Resource res = itemColl.getResource(resName); if (res == null) res = itemColl.createResource(resName, "XMLResource"); res.setContent(objString); itemColl.storeResource(res); itemColl.close(); } catch (Exception e) { Logger.error(e); throw new ClusterStorageException("XMLDB error"); } } /* (non-Javadoc) * @see com.c2kernel.persistency.ClusterStorage#delete(java.lang.Integer, java.lang.String) */ @Override public void delete(Integer sysKey, String path) throws ClusterStorageException { String strSysKey = String.valueOf(sysKey); Collection itemColl = verifyCollection(root, strSysKey, false); if (itemColl == null) return; try { String resource = path.replace('/', '.'); Resource res = itemColl.getResource(resource); if (res != null) itemColl.removeResource(res); itemColl.close(); itemColl.close(); } catch (Exception e) { Logger.error(e); throw new ClusterStorageException("XMLClusterStorage.delete() - Could not delete "+path+" to "+sysKey); } } /* (non-Javadoc) * @see com.c2kernel.persistency.ClusterStorage#getClusterContents(java.lang.Integer, java.lang.String) */ @Override public String[] getClusterContents(Integer sysKey, String path) throws ClusterStorageException { String strSysKey = String.valueOf(sysKey); Collection coll = verifyCollection(root, strSysKey, false); if (coll == null) return new String[0]; ArrayList contents = new ArrayList(); // Find prefix for our path level StringBuffer resPrefix = new StringBuffer(); String[] pathComps = path.split("/"); if (pathComps.length > 0) for (int i = 0; i < pathComps.length; i++) { if (pathComps[i].length()>0) resPrefix.append(pathComps[i]).append("."); } // Look at each entry for matches. Trim off the ends. try { for (String res: coll.listResources()) { if (res.startsWith(resPrefix.toString())) { String resName = URLDecoder.decode(res.substring(resPrefix.length()), "UTF-8"); if (resName.indexOf('.')>-1) resName = resName.substring(0, resName.indexOf('.')); if (!contents.contains(resName)) contents.add(resName); } } } catch (XMLDBException e) { Logger.error(e); throw new ClusterStorageException("Error listing collection resources for item "+strSysKey); } catch (UnsupportedEncodingException e) { Logger.error(e); throw new ClusterStorageException("Error listing decoding resource name for item "+strSysKey); } return contents.toArray(new String[contents.size()]); } }