/*
* eXist Open Source Native XML Database
* Copyright (C) 2001-2015 The eXist Project
* http://exist-db.org
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package org.exist.xmldb;
import org.exist.dom.persistent.BinaryDocument;
import org.apache.commons.io.output.ByteArrayOutputStream;
import org.exist.security.Subject;
import org.exist.storage.BrokerPool;
import org.exist.util.EXistInputSource;
import org.exist.util.FileUtils;
import org.w3c.dom.DocumentType;
import org.xml.sax.InputSource;
import org.xml.sax.ext.LexicalHandler;
import org.xmldb.api.base.ErrorCodes;
import org.xmldb.api.base.XMLDBException;
import org.xmldb.api.modules.BinaryResource;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.FileOutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
public class LocalBinaryResource extends AbstractEXistResource implements ExtendedResource, BinaryResource, EXistResource {
protected InputSource inputSource = null;
protected Path file = null;
protected byte[] rawData = null;
private boolean isExternal = false;
public LocalBinaryResource(final Subject user, final BrokerPool brokerPool, final LocalCollection collection, final XmldbURI docId) {
super(user, brokerPool, collection, docId, null);
}
@Override
public String getResourceType() throws XMLDBException {
return BinaryResource.RESOURCE_TYPE;
}
@Override
public Object getExtendedContent() throws XMLDBException {
if (file != null) {
return file;
}
if (inputSource != null) {
return inputSource;
}
return read((document, broker, transaction) -> broker.getBinaryResource(((BinaryDocument) document)));
}
@Override
public Object getContent() throws XMLDBException {
final Object res = getExtendedContent();
if(res != null) {
if(res instanceof Path) {
return readFile((Path)res);
} else if(res instanceof java.io.File) {
return readFile(((java.io.File)res).toPath());
} else if(res instanceof InputSource) {
return readFile((InputSource)res);
} else if(res instanceof InputStream) {
return readFile((InputStream)res);
}
}
return res;
}
@Override
public void setContent(final Object value) throws XMLDBException {
if(value instanceof Path) {
file = (Path)value;
} else if(value instanceof java.io.File) {
file = ((java.io.File)value).toPath();
} else if(value instanceof InputSource) {
inputSource = (InputSource)value;
} else if(value instanceof byte[]) {
rawData = (byte[])value;
} else if(value instanceof String) {
rawData = ((String)value).getBytes();
} else {
throw new XMLDBException(ErrorCodes.VENDOR_ERROR, "don't know how to handle value of type " + value.getClass().getName());
}
isExternal = true;
}
@Override
public InputStream getStreamContent() throws XMLDBException {
final InputStream retval;
if(file != null) {
try {
retval = Files.newInputStream(file);
} catch(final IOException 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 {
retval = read((document, broker, transaction) -> broker.getBinaryResource(((BinaryDocument) document)));
}
return retval;
}
@Override
public void getContentIntoAFile(final Path tmpFile) throws XMLDBException {
try(final OutputStream bos = Files.newOutputStream(tmpFile)) {
getContentIntoAStream(bos);
} catch(final IOException ioe) {
throw new XMLDBException(ErrorCodes.VENDOR_ERROR, "error while loading binary resource " + getId(), ioe);
}
}
@Override
public void getContentIntoAStream(final OutputStream os) throws XMLDBException {
read((document, broker, transaction) -> {
if(os instanceof FileOutputStream) {
try(final OutputStream bos = new BufferedOutputStream(os, 655360)) {
broker.readBinaryResource((BinaryDocument) document, bos);
}
} else {
broker.readBinaryResource((BinaryDocument) document, os);
}
return null;
});
}
@Override
public void freeResources() {
if(!isExternal && file != null) {
file = null;
}
}
@Override
public long getStreamLength() throws XMLDBException {
final long retval;
if(file != null) {
retval = FileUtils.sizeQuietly(file);
} else if(inputSource != null && inputSource instanceof EXistInputSource) {
retval = ((EXistInputSource)inputSource).getByteStreamLength();
} else if(rawData != null) {
retval = rawData.length;
} else {
retval = getContentLength();
}
return retval;
}
private byte[] readFile(final Path file) throws XMLDBException {
try(final ByteArrayOutputStream os = new ByteArrayOutputStream()) {
Files.copy(file, os);
return os.toByteArray();
} catch (final IOException e) {
throw new XMLDBException(ErrorCodes.VENDOR_ERROR, "file " + file.toAbsolutePath() + " could not be found", e);
}
}
private byte[] readFile(final InputSource inSrc) throws XMLDBException {
try(final InputStream is = inSrc.getByteStream()) {
return readFile(is);
} catch (final IOException e) {
throw new XMLDBException(ErrorCodes.VENDOR_ERROR, "Could not read InputSource", e);
}
}
private byte[] readFile(final InputStream is) throws XMLDBException {
try(final ByteArrayOutputStream bos = new ByteArrayOutputStream()) {
final byte[] buf = new byte[2048];
int read = -1;
while((read = is.read(buf)) > -1) {
bos.write(buf, 0, read);
}
return bos.toByteArray();
} catch (final IOException e) {
throw new XMLDBException(ErrorCodes.VENDOR_ERROR, "IO exception while reading file " + file.toAbsolutePath(), e);
}
}
@Override
public DocumentType getDocType() throws XMLDBException {
return null;
}
@Override
public void setDocType(final DocumentType doctype) throws XMLDBException {
}
@Override
public void setLexicalHandler(final LexicalHandler handler) {
throw new UnsupportedOperationException();
}
}