package org.wyona.yarep.impl.repo.xmldb; import java.io.IOException; import java.io.StringWriter; import java.io.Writer; import org.wyona.yarep.core.RepositoryException; import org.apache.log4j.Category; import org.xmldb.api.base.Collection; import org.xmldb.api.base.Resource; import org.xmldb.api.base.XMLDBException; import org.xmldb.api.modules.BinaryResource; import org.xmldb.api.modules.XMLResource; /** * @author Andreas Wuest * * This class is marked with default protection so that it can only be * instantiated from within this package. */ class XMLDBStorageWriter extends Writer { private static Category mLog = Category.getInstance(XMLDBStorageWriter.class); private boolean mStreamIsClosed = false; private StringWriter mContentWriter; private Collection mCollection; private Resource mResource; /** * XMLDBStorageWriter constructor. * * Do not use this to write to a binary resource. Use a * XMLDBStorageOutputStream instead. Any binary data written * will be converted using UTF-8. * * @param aXMLDBStorage the database instance to work upon * @param aCollectionPath the path to the collection which contains the resource * @param aResourceName the name of the resource to write * @param aResourceType the resource type */ XMLDBStorageWriter(XMLDBStorage aXMLDBStorage, String aCollectionPath, String aResourceName, String aResourceType) throws RepositoryException { super(); mLog.error("Collection path = \"" + aCollectionPath + "\", resource name = \"" + aResourceName + "\", Resource type = \"" + aResourceType + "\"."); /* The collection cannot be passed to us directly, because we need our own * private copy of the collection, since we have to close the collection when * we are done reading, and no further operations on that collection are allowed * after that. We can't realisticly expect the caller to give us our own private * collection copy to work on it at will. */ if ((mCollection = aXMLDBStorage.getCollectionRelative(aCollectionPath)) == null) throw new RepositoryException("Parent collection \"" + aCollectionPath + "\" does not exist."); try { // check if the resource already exists mResource = mCollection.getResource(aResourceName); if (mResource != null) { /* Check if the resource types are compatible. If not, remove * the existing resource, and create a new one. */ if (!mResource.getResourceType().equals(aResourceType)) { mCollection.removeResource(mResource); mResource = mCollection.createResource(aResourceName, aResourceType); } } else { mResource = mCollection.createResource(aResourceName, aResourceType); } } catch (XMLDBException exception) { throw new RepositoryException(exception.getMessage(), exception); } mContentWriter = new StringWriter(); } /** * Closes the Writer for any further writing, and stores the * data in the database. * * @throws IOException */ public void close() throws IOException { // contract: closing a previously-closed stream has no effect if (mStreamIsClosed) return; mStreamIsClosed = true; try { // set the content on the resource if (mResource.getResourceType().equals(BinaryResource.RESOURCE_TYPE)) { mResource.setContent(mContentWriter.toString().getBytes("UTF-8")); } else if (mResource.getResourceType().equals(XMLResource.RESOURCE_TYPE)) { mResource.setContent(mContentWriter.toString()); } // store the resource in the database mCollection.storeResource(mResource); // close the collection and the stream mCollection.close(); } catch (XMLDBException exception) { throw new IOException(exception.getMessage()); } mContentWriter.close(); /* Let the finalizer know that close was called, so he does not close the collection * again. Also, make for speedy memory reclaiming, because the collection, resource * and stream objects are guaranteed to not be used again in this class. */ mCollection = null; mResource = null; mContentWriter = null; } public void flush() throws IOException { if (mStreamIsClosed) throw new IOException("Writer was already closed."); mContentWriter.flush(); } public void write(char[] aCharacterArray) throws IOException { if (mStreamIsClosed) throw new IOException("Writer was already closed."); mContentWriter.write(aCharacterArray, 0, aCharacterArray.length); } public void write(char[] aCharacterArray, int aOffset, int aLength) throws IOException { if (mStreamIsClosed) throw new IOException("Writer was already closed."); mContentWriter.write(aCharacterArray, aOffset, aLength); } public void write(int aByte) throws IOException { if (mStreamIsClosed) throw new IOException("Writer was already closed."); mContentWriter.write(aByte); } public void write(String aString) throws IOException { if (mStreamIsClosed) throw new IOException("Writer was already closed."); mContentWriter.write(aString); } public void write(String aString, int aOffset, int aLength) throws IOException { if (mStreamIsClosed) throw new IOException("Writer was already closed."); mContentWriter.write(aString, aOffset, aLength); } protected void finalize() throws Throwable { /* Close the collection, but don't persist the resource. The collection must * always be closed as per XML:DB specification, no matter what happened. On * the other hand, if the collection still exists, this means that our close() * method was never called, so the user did not want us to store the data. */ if (mCollection != null) { mCollection.close(); } super.finalize(); } }