/*
* eXist Open Source Native XML Database
* Copyright (C) 2001-06 Wolfgang M. Meier
* wolfgang@exist-db.org
* http://exist.sourceforge.net
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id$
*/
package org.exist.xmldb;
import org.exist.EXistException;
import org.exist.dom.BinaryDocument;
import org.exist.dom.DocumentImpl;
import org.apache.commons.io.output.ByteArrayOutputStream;
import org.exist.security.Permission;
import org.exist.security.Subject;
import org.exist.storage.BrokerPool;
import org.exist.storage.DBBroker;
import org.exist.storage.lock.Lock;
import org.exist.util.EXistInputSource;
import org.exist.util.LockException;
import org.xml.sax.InputSource;
import org.xmldb.api.base.Collection;
import org.xmldb.api.base.ErrorCodes;
import org.xmldb.api.base.XMLDBException;
import org.xmldb.api.modules.BinaryResource;
import java.io.*;
import java.util.Date;
import org.exist.security.PermissionDeniedException;
/**
* @author wolf
*/
public class LocalBinaryResource extends AbstractEXistResource implements ExtendedResource, BinaryResource, EXistResource {
protected InputSource inputSource = null;
protected File file = null;
protected byte[] rawData = null;
private boolean isExternal=false;
protected Date datecreated= null;
protected Date datemodified= null;
/**
*
*/
public LocalBinaryResource(Subject user, BrokerPool pool, LocalCollection collection, XmldbURI docId) {
super(user, pool, collection, docId, null);
}
/* (non-Javadoc)
* @see org.xmldb.api.base.Resource#getParentCollection()
*/
public Collection getParentCollection() throws XMLDBException {
return parent;
}
/* (non-Javadoc)
* @see org.xmldb.api.base.Resource#getId()
*/
public String getId() throws XMLDBException {
return docId.toString();
}
/* (non-Javadoc)
* @see org.xmldb.api.base.Resource#getResourceType()
*/
public String getResourceType() throws XMLDBException {
return "BinaryResource";
}
public Object getExtendedContent() throws XMLDBException {
if(file!=null)
{return file;}
if(inputSource!=null)
{return inputSource;}
final Subject preserveSubject = pool.getSubject();
DBBroker broker = null;
BinaryDocument blob = null;
InputStream rawDataStream = null;
try {
broker = pool.get(user);
blob = (BinaryDocument)getDocument(broker, Lock.READ_LOCK);
if(!blob.getPermissions().validate(user, Permission.READ))
{throw new XMLDBException(ErrorCodes.PERMISSION_DENIED,
"Permission denied to read resource");}
rawDataStream = broker.getBinaryResource(blob);
} catch(final EXistException e) {
throw new XMLDBException(ErrorCodes.VENDOR_ERROR,
"error while loading binary resource " + getId(), e);
} catch(final IOException e) {
throw new XMLDBException(ErrorCodes.VENDOR_ERROR,
"error while loading binary resource " + getId(), e);
} finally {
if(blob!=null)
{parent.getCollection().releaseDocument(blob, Lock.READ_LOCK);}
if(broker!=null)
{pool.release(broker);}
pool.setSubject(preserveSubject);
}
return rawDataStream;
}
/* (non-Javadoc)
* @see org.xmldb.api.base.Resource#getContent()
*/
public Object getContent() throws XMLDBException {
final Object res=getExtendedContent();
if(res!=null) {
if(res instanceof File) {
return readFile((File)res);
} else if(res instanceof InputSource) {
return readFile((InputSource)res);
} else if(res instanceof InputStream) {
return readFile((InputStream)res);
}
}
return res;
}
/* (non-Javadoc)
* @see org.xmldb.api.base.Resource#setContent(java.lang.Object)
*/
public void setContent(Object value) throws XMLDBException {
if(value instanceof File) {
file=(File)value;
isExternal=true;
} else if(value instanceof InputSource) {
inputSource=(InputSource)value;
isExternal=true;
} else if(value instanceof byte[]) {
rawData = (byte[])value;
isExternal=true;
} else if(value instanceof String) {
rawData = ((String)value).getBytes();
isExternal=true;
} else
{throw new XMLDBException(ErrorCodes.VENDOR_ERROR,
"don't know how to handle value of type " + value.getClass().getName());}
}
public InputStream getStreamContent() throws XMLDBException {
InputStream retval=null;
if(file!=null) {
try {
retval=new FileInputStream(file);
} catch(final FileNotFoundException fnfe) {
// Cannot fire it :-(
throw new XMLDBException(ErrorCodes.VENDOR_ERROR, fnfe.getMessage(), fnfe);
}
} else if(inputSource!=null) {
retval=inputSource.getByteStream();
} else if(rawData!=null) {
retval=new ByteArrayInputStream(rawData);
} else {
final Subject preserveSubject = pool.getSubject();
DBBroker broker = null;
BinaryDocument blob = null;
try {
broker = pool.get(user);
blob = (BinaryDocument)getDocument(broker, Lock.READ_LOCK);
if(!blob.getPermissions().validate(user, Permission.READ))
{throw new XMLDBException(ErrorCodes.PERMISSION_DENIED,
"Permission denied to read resource");}
retval = broker.getBinaryResource(blob);
} catch(final EXistException e) {
throw new XMLDBException(ErrorCodes.VENDOR_ERROR,
"error while loading binary resource " + getId(), e);
} catch(final IOException e) {
throw new XMLDBException(ErrorCodes.VENDOR_ERROR,
"error while loading binary resource " + getId(), e);
} finally {
if(blob!=null)
{parent.getCollection().releaseDocument(blob, Lock.READ_LOCK);}
if(broker!=null)
{pool.release(broker);}
pool.setSubject(preserveSubject);
}
}
return retval;
}
public void getContentIntoAFile(File tmpfile) throws XMLDBException {
try {
final FileOutputStream fos=new FileOutputStream(tmpfile);
final BufferedOutputStream bos=new BufferedOutputStream(fos);
getContentIntoAStream(bos);
bos.close();
fos.close();
} catch(final IOException ioe) {
throw new XMLDBException(ErrorCodes.VENDOR_ERROR,
"error while loading binary resource " + getId(), ioe);
}
}
public void getContentIntoAStream(OutputStream os) throws XMLDBException {
final Subject preserveSubject = pool.getSubject();
DBBroker broker = null;
BinaryDocument blob = null;
boolean doClose=false;
try {
broker = pool.get(user);
blob = (BinaryDocument)getDocument(broker, Lock.READ_LOCK);
if(!blob.getPermissions().validate(user, Permission.READ))
{throw new XMLDBException(ErrorCodes.PERMISSION_DENIED,
"Permission denied to read resource");}
// Improving the performance a bit for files!
if(os instanceof FileOutputStream) {
os = new BufferedOutputStream(os,655360);
doClose=true;
}
broker.readBinaryResource(blob, os);
} catch(final EXistException e) {
throw new XMLDBException(ErrorCodes.VENDOR_ERROR,
"error while loading binary resource " + getId(), e);
} catch(final IOException ioe) {
throw new XMLDBException(ErrorCodes.VENDOR_ERROR,
"error while loading binary resource " + getId(), ioe);
} finally {
if(blob!=null)
{parent.getCollection().releaseDocument(blob, Lock.READ_LOCK);}
if(broker!=null)
{pool.release(broker);}
pool.setSubject(preserveSubject);
if(doClose) {
try {
os.close();
} catch(final IOException ioe) {
// IgnoreIT(R)
}
}
}
}
@Override
public void freeResources()
{
if(!isExternal && file!=null) {
file=null;
}
}
public long getStreamLength() throws XMLDBException {
long retval=-1;
if(file!=null) {
retval=file.length();
} else if(inputSource!=null && inputSource instanceof EXistInputSource) {
retval=((EXistInputSource)inputSource).getByteStreamLength();
} else if(rawData!=null) {
retval=rawData.length;
} else {
final Subject preserveSubject = pool.getSubject();
DBBroker broker = null;
BinaryDocument blob = null;
try {
broker = pool.get(user);
blob = (BinaryDocument)getDocument(broker, Lock.READ_LOCK);
retval=blob.getContentLength();
} catch(final EXistException e) {
throw new XMLDBException(ErrorCodes.VENDOR_ERROR,
"error while loading binary resource " + getId(), e);
} finally {
if(blob!=null)
{parent.getCollection().releaseDocument(blob, Lock.READ_LOCK);}
if(broker!=null)
{pool.release(broker);}
pool.setSubject(preserveSubject);
}
}
return retval;
}
private byte[] readFile(File file) throws XMLDBException {
try {
return readFile(new FileInputStream(file));
} catch (final FileNotFoundException e) {
throw new XMLDBException(ErrorCodes.VENDOR_ERROR,
"file " + file.getAbsolutePath() + " could not be found", e);
}
}
private byte[] readFile(InputSource is) throws XMLDBException {
return readFile(is.getByteStream());
}
private byte[] readFile(InputStream is) throws XMLDBException {
try {
final ByteArrayOutputStream bos = new ByteArrayOutputStream(2048);
final byte[] temp = new byte[1024];
int count = 0;
while((count = is.read(temp)) > -1) {
bos.write(temp, 0, count);
}
return bos.toByteArray();
} catch (final FileNotFoundException e) {
throw new XMLDBException(ErrorCodes.VENDOR_ERROR,
"file " + file.getAbsolutePath() + " could not be found", e);
} catch (final IOException e) {
throw new XMLDBException(ErrorCodes.VENDOR_ERROR,
"IO exception while reading file " + file.getAbsolutePath(), e);
} finally {
try {
is.close();
} catch (final IOException e) {
throw new XMLDBException(ErrorCodes.VENDOR_ERROR,
"IO exception while closing stream of file " + file.getAbsolutePath(), e);
}
}
}
/* (non-Javadoc)
* @see org.exist.xmldb.EXistResource#getCreationTime()
*/
public Date getCreationTime() throws XMLDBException {
if (isNewResource)
{throw new XMLDBException(ErrorCodes.INVALID_RESOURCE, "The resource has not yet been stored");}
final Subject preserveSubject = pool.getSubject();
DBBroker broker = null;
try {
broker = pool.get(user);
final BinaryDocument blob = (BinaryDocument)getDocument(broker, Lock.NO_LOCK);
return new Date(blob.getMetadata().getCreated());
} catch (final EXistException e) {
throw new XMLDBException(ErrorCodes.UNKNOWN_ERROR, e.getMessage(), e);
} finally {
pool.release(broker);
pool.setSubject(preserveSubject);
}
}
/* (non-Javadoc)
* @see org.exist.xmldb.EXistResource#getLastModificationTime()
*/
public Date getLastModificationTime() throws XMLDBException {
if (isNewResource)
{throw new XMLDBException(ErrorCodes.INVALID_RESOURCE, "The resource has not yet been stored");}
final Subject preserveSubject = pool.getSubject();
DBBroker broker = null;
try {
broker = pool.get(user);
final BinaryDocument blob = (BinaryDocument)getDocument(broker, Lock.NO_LOCK);
return new Date(blob.getMetadata().getLastModified());
} catch (final EXistException e) {
throw new XMLDBException(ErrorCodes.UNKNOWN_ERROR, e.getMessage(), e);
} finally {
pool.release(broker);
pool.setSubject(preserveSubject);
}
}
/* (non-Javadoc)
* @see org.exist.xmldb.AbstractEXistResource#getMimeType()
*/
public String getMimeType() throws XMLDBException {
if (isNewResource)
{return mimeType;}
final Subject preserveSubject = pool.getSubject();
DBBroker broker = null;
try {
broker = pool.get(user);
final BinaryDocument blob = (BinaryDocument)getDocument(broker, Lock.NO_LOCK);
mimeType = blob.getMetadata().getMimeType();
return mimeType;
} catch (final EXistException e) {
throw new XMLDBException(ErrorCodes.UNKNOWN_ERROR, e.getMessage(), e);
} finally {
pool.release(broker);
pool.setSubject(preserveSubject);
}
}
/* (non-Javadoc)
* @see org.exist.xmldb.EXistResource#getMode()
*/
public Permission getPermissions() throws XMLDBException {
if (isNewResource)
{throw new XMLDBException(ErrorCodes.INVALID_RESOURCE, "The resource has not yet been stored");}
final Subject preserveSubject = pool.getSubject();
DBBroker broker = null;
try {
broker = pool.get(user);
final DocumentImpl document = getDocument(broker, Lock.NO_LOCK);
return document != null ? document.getPermissions() : null;
} catch (final EXistException e) {
throw new XMLDBException(ErrorCodes.INVALID_RESOURCE, e.getMessage(), e);
} finally {
pool.release(broker);
pool.setSubject(preserveSubject);
}
}
/* (non-Javadoc)
* @see org.exist.xmldb.EXistResource#getContentLength()
*/
public long getContentLength() throws XMLDBException {
if (isNewResource)
{throw new XMLDBException(ErrorCodes.INVALID_RESOURCE, "The resource has not yet been stored");}
final Subject preserveSubject = pool.getSubject();
DBBroker broker = null;
try {
broker = pool.get(user);
final DocumentImpl document = getDocument(broker, Lock.NO_LOCK);
return document.getContentLength();
} catch (final EXistException e) {
throw new XMLDBException(ErrorCodes.UNKNOWN_ERROR, e.getMessage(),
e);
} finally {
pool.release(broker);
pool.setSubject(preserveSubject);
}
}
protected DocumentImpl getDocument(DBBroker broker, int lock) throws XMLDBException {
DocumentImpl document = null;
if(lock != Lock.NO_LOCK) {
try {
document = parent.getCollection().getDocumentWithLock(broker, docId, lock);
} catch (final PermissionDeniedException e) {
throw new XMLDBException(ErrorCodes.PERMISSION_DENIED,
"Permission denied for document " + docId + ": " + e.getMessage());
} catch (final LockException e) {
throw new XMLDBException(ErrorCodes.PERMISSION_DENIED,
"Failed to acquire lock on document " + docId);
}
} else {
try {
document = parent.getCollection().getDocument(broker, docId);
} catch (final PermissionDeniedException e) {
throw new XMLDBException(ErrorCodes.PERMISSION_DENIED, "Permission denied for document " + docId + ": " + e.getMessage());
}
}
if (document == null) {
throw new XMLDBException(ErrorCodes.INVALID_RESOURCE);
}
if (document.getResourceType() != DocumentImpl.BINARY_FILE) {
throw new XMLDBException(ErrorCodes.WRONG_CONTENT_TYPE, "Document " + docId +
" is not a binary resource");
}
return document;
}
protected DocumentImpl openDocument(DBBroker broker, int lockMode) throws XMLDBException {
final DocumentImpl document = super.openDocument(broker, lockMode);
if (document.getResourceType() != DocumentImpl.BINARY_FILE) {
closeDocument(document, lockMode);
throw new XMLDBException(ErrorCodes.WRONG_CONTENT_TYPE, "Document " + docId +
" is not a binary resource");
}
return document;
}
}