/*
* eXist Open Source Native XML Database
* Copyright (C) 2010-2012 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id$
*/
package org.exist.util.io;
import java.io.*;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import javax.xml.transform.OutputKeys;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;
import org.exist.EXistException;
import org.exist.collections.Collection;
import org.exist.collections.Collection.CollectionEntry;
import org.exist.collections.IndexInfo;
import org.exist.collections.triggers.TriggerException;
import org.exist.dom.persistent.BinaryDocument;
import org.exist.dom.persistent.DocumentImpl;
import org.exist.dom.persistent.DocumentMetadata;
import org.exist.dom.persistent.LockToken;
import org.exist.security.Permission;
import org.exist.security.PermissionDeniedException;
import org.exist.security.Subject;
import org.exist.storage.BrokerPool;
import org.exist.storage.DBBroker;
import org.exist.storage.lock.Lock.LockMode;
import org.exist.storage.serializers.EXistOutputKeys;
import org.exist.storage.serializers.Serializer;
import org.exist.storage.txn.TransactionManager;
import org.exist.storage.txn.Txn;
import org.exist.util.*;
import org.exist.xmldb.XmldbURI;
import org.xml.sax.SAXException;
import static org.exist.security.Permission.*;
import static java.nio.charset.StandardCharsets.UTF_8;
/**
* eXist's resource. It extend java.io.File
*
* @author <a href="mailto:shabanovd@gmail.com">Dmitriy Shabanov</a>
*/
public class Resource extends File {
private final static Logger LOG = LogManager.getLogger(Resource.class);
private static final long serialVersionUID = -3450182389919974961L;
public static final char separatorChar = '/';
// default output properties for the XML serialization
public final static Properties XML_OUTPUT_PROPERTIES = new Properties();
static {
XML_OUTPUT_PROPERTIES.setProperty(OutputKeys.INDENT, "yes");
XML_OUTPUT_PROPERTIES.setProperty(OutputKeys.ENCODING, "UTF-8");
XML_OUTPUT_PROPERTIES.setProperty(OutputKeys.OMIT_XML_DECLARATION, "no");
XML_OUTPUT_PROPERTIES.setProperty(EXistOutputKeys.EXPAND_XINCLUDES, "no");
XML_OUTPUT_PROPERTIES.setProperty(EXistOutputKeys.PROCESS_XSL_PI, "no");
}
public final static int DEFAULT_COLLECTION_PERM = 0777;
public final static int DEFAULT_RESOURCE_PERM = 0644;
private static final SecureRandom random = new SecureRandom();
static File generateFile(String prefix, String suffix, File dir) {
long n = random.nextLong();
if (n == Long.MIN_VALUE) {
n = 0; // corner case
} else {
n = Math.abs(n);
}
return new Resource(dir, prefix + Long.toString(n) + suffix);
}
public static File createTempFile(String prefix, String suffix, File directory) throws IOException {
if (prefix.length() < 3) {
throw new IllegalArgumentException("Prefix string too short");
}
if (suffix == null) {
suffix = ".tmp";
}
return generateFile(prefix, suffix, directory);
}
protected XmldbURI uri;
protected boolean initialized = false;
private Collection collection = null;
private DocumentImpl resource = null;
Path file = null;
public Resource(XmldbURI uri) {
super(uri.toString());
this.uri = uri;
}
public Resource(String uri) {
this(XmldbURI.create(uri));
}
public Resource(File file, String child) {
this((Resource) file, child);
}
public Resource(Resource resource, String child) {
this(resource.uri.append(child));
// this(child.startsWith("/db") ? XmldbURI.create(child) : resource.uri.append(child));
}
public Resource(String parent, String child) {
this(XmldbURI.create(parent).append(child));
// this(child.startsWith("/db") ? XmldbURI.create(child) : XmldbURI.create(parent).append(child));
}
public Resource getParentFile() {
final XmldbURI parentPath = uri.removeLastSegment();
if (parentPath == XmldbURI.EMPTY_URI) {
if (uri.startsWith(XmldbURI.DB))
return null;
return new Resource(XmldbURI.DB);
}
return new Resource(parentPath);
}
public Resource getAbsoluteFile() {
return this; //UNDERSTAND: is it correct?
}
public File getCanonicalFile() throws IOException {
return this;
}
public String getName() {
return uri.lastSegment().toString();
}
private void closeFile(InputStream is) {
if (is == null) {
return;
}
try {
is.close();
} catch (final IOException e) {
//
}
}
public boolean mkdir() {
final BrokerPool db;
try {
db = BrokerPool.getInstance();
} catch (final EXistException e) {
return false;
}
try (final DBBroker broker = db.getBroker()) {
final Collection collection = broker.getCollection(uri.toCollectionPathURI());
if (collection != null) {
return true;
}
final Collection parent_collection = broker.getCollection(uri.toCollectionPathURI().removeLastSegment());
if (parent_collection == null) {
return false;
}
final TransactionManager tm = db.getTransactionManager();
try (final Txn transaction = tm.beginTransaction()) {
final Collection child = broker.getOrCreateCollection(transaction, uri.toCollectionPathURI());
broker.saveCollection(transaction, child);
tm.commit(transaction);
} catch (final Exception e) {
LOG.error(e);
return false;
}
} catch (final Exception e) {
LOG.error(e);
return false;
}
return true;
}
public boolean mkdirs() {
final BrokerPool db;
try {
db = BrokerPool.getInstance();
} catch (final EXistException e) {
return false;
}
try (final DBBroker broker = db.getBroker()) {
final Collection collection = broker.getCollection(uri.toCollectionPathURI());
if (collection != null) {
return true;
}
final TransactionManager tm = db.getTransactionManager();
try (final Txn transaction = tm.beginTransaction()) {
final Collection child = broker.getOrCreateCollection(transaction, uri.toCollectionPathURI());
broker.saveCollection(transaction, child);
tm.commit(transaction);
} catch (final Exception e) {
LOG.error(e);
return false;
}
} catch (final Exception e) {
LOG.error(e);
return false;
}
return true;
}
public boolean isDirectory() {
try {
init();
} catch (final IOException e) {
return false;
}
return (resource == null);
}
public boolean isFile() {
try {
init();
} catch (final IOException e) {
return false;
}
return (resource != null);
}
public boolean exists() {
try {
init();
} catch (final IOException e) {
return false;
}
return ((collection != null) || (resource != null));
}
public boolean _renameTo(File dest) {
final XmldbURI destinationPath = ((Resource) dest).uri;
BrokerPool db = null;
TransactionManager tm;
try {
db = BrokerPool.getInstance();
tm = db.getTransactionManager();
} catch (final EXistException e) {
return false;
}
org.exist.collections.Collection destination = null;
org.exist.collections.Collection source = null;
XmldbURI newName;
try (final DBBroker broker = db.getBroker()) {
source = broker.openCollection(uri.removeLastSegment(), LockMode.WRITE_LOCK);
if (source == null) {
return false;
}
final DocumentImpl doc = source.getDocument(broker, uri.lastSegment());
if (doc == null) {
return false;
}
destination = broker.openCollection(destinationPath.removeLastSegment(), LockMode.WRITE_LOCK);
if (destination == null) {
return false;
}
newName = destinationPath.lastSegment();
try (final Txn transaction = tm.beginTransaction()) {
broker.moveResource(transaction, doc, destination, newName);
tm.commit(transaction);
}
return true;
} catch (final Exception e) {
e.printStackTrace();
return false;
} finally {
if (source != null) {
source.release(LockMode.WRITE_LOCK);
}
if (destination != null) {
destination.release(LockMode.WRITE_LOCK);
}
}
}
public boolean renameTo(File dest) {
// System.out.println("rename from "+uri+" to "+dest.getPath());
final XmldbURI destinationPath = ((Resource) dest).uri;
final BrokerPool db;
try {
db = BrokerPool.getInstance();
} catch (final EXistException e) {
return false;
}
try (final DBBroker broker = db.getBroker()) {
org.exist.collections.Collection destination = null;
org.exist.collections.Collection source = null;
XmldbURI newName;
try {
source = broker.openCollection(uri.removeLastSegment(), LockMode.WRITE_LOCK);
if (source == null) {
return false;
}
final DocumentImpl doc = source.getDocument(broker, uri.lastSegment());
if (doc == null) {
return false;
}
destination = broker.openCollection(destinationPath.removeLastSegment(), LockMode.WRITE_LOCK);
if (destination == null) {
return false;
}
newName = destinationPath.lastSegment();
final TransactionManager tm = db.getTransactionManager();
try (final Txn transaction = tm.beginTransaction()) {
moveResource(broker, transaction, doc, source, destination, newName);
// resource = null;
// collection = null;
// initialized = false;
// uri = ((Resource)dest).uri;
tm.commit(transaction);
return true;
}
} catch (final Exception e) {
e.printStackTrace();
return false;
} finally {
if (source != null) {
source.release(LockMode.WRITE_LOCK);
}
if (destination != null) {
destination.release(LockMode.WRITE_LOCK);
}
}
} catch (final EXistException e) {
return false;
}
}
private synchronized Path serialize(final DBBroker broker, final DocumentImpl doc) throws IOException {
if (file != null) {
throw new IOException(doc.getFileURI().toString() + " locked.");
}
try {
final Serializer serializer = broker.getSerializer();
serializer.setUser(broker.getCurrentSubject());
serializer.setProperties(XML_OUTPUT_PROPERTIES);
file = Files.createTempFile("eXist-resource-", ".xml");
file.toFile().deleteOnExit();
try (final Writer w = Files.newBufferedWriter(file, UTF_8)) {
serializer.serialize(doc, w);
}
return file;
} catch (final Exception e) {
throw new IOException(e);
}
}
protected void freeFile() throws IOException {
if (isXML()) {
if (file == null) {
//XXX: understand why can't throw exception
//throw new IOException();
return;
}
FileUtils.deleteQuietly(file);
file = null;
}
}
protected synchronized void uploadTmpFile() throws IOException {
if (file == null) {
throw new IOException();
}
final BrokerPool db;
try {
db = BrokerPool.getInstance();
} catch (final EXistException e) {
throw new IOException(e);
}
final TransactionManager tm = db.getTransactionManager();
try (final DBBroker broker = db.getBroker();
final Txn txn = tm.beginTransaction()) {
FileInputSource is = new FileInputSource(file);
final IndexInfo info = collection.validateXMLResource(txn, broker, uri.lastSegment(), is);
// info.getDocument().getMetadata().setMimeType(mimeType.getName());
is = new FileInputSource(file);
collection.store(txn, broker, info, is);
tm.commit(txn);
} catch (final Exception e) {
e.printStackTrace();
}
}
private void moveResource(DBBroker broker, Txn txn, DocumentImpl doc, Collection source, Collection destination, XmldbURI newName) throws PermissionDeniedException, LockException, IOException, SAXException, EXistException {
final MimeTable mimeTable = MimeTable.getInstance();
final boolean isXML = mimeTable.isXMLContent(newName.toString());
final MimeType mimeType = mimeTable.getContentTypeFor(newName);
if (mimeType != null && !mimeType.getName().equals(doc.getMetadata().getMimeType())) {
doc.getMetadata().setMimeType(mimeType.getName());
broker.storeXMLResource(txn, doc);
doc = source.getDocument(broker, uri.lastSegment());
}
if (isXML) {
if (doc.getResourceType() == DocumentImpl.XML_FILE) {
//XML to XML
//move to same type as it
broker.moveResource(txn, doc, destination, newName);
} else {
//convert BINARY to XML
final Path file = broker.getBinaryFile((BinaryDocument) doc);
FileInputSource is = new FileInputSource(file);
final IndexInfo info = destination.validateXMLResource(txn, broker, newName, is);
info.getDocument().getMetadata().setMimeType(mimeType.getName());
is = new FileInputSource(file);
destination.store(txn, broker, info, is);
source.removeBinaryResource(txn, broker, doc);
}
} else {
if (doc.getResourceType() == DocumentImpl.BINARY_FILE) {
//BINARY to BINARY
//move to same type as it
broker.moveResource(txn, doc, destination, newName);
} else {
//convert XML to BINARY
// xml file
final Serializer serializer = broker.getSerializer();
serializer.setUser(broker.getCurrentSubject());
serializer.setProperties(XML_OUTPUT_PROPERTIES);
File tempFile = null;
FileInputStream is = null;
try {
tempFile = File.createTempFile("eXist-resource-", ".xml");
tempFile.deleteOnExit();
final Writer w = new OutputStreamWriter(new FileOutputStream(tempFile), "UTF-8");
serializer.serialize(doc, w);
w.flush();
w.close();
is = new FileInputStream(tempFile);
final DocumentMetadata meta = doc.getMetadata();
final Date created = new Date(meta.getCreated());
final Date lastModified = new Date(meta.getLastModified());
BinaryDocument binary = destination.validateBinaryResource(txn, broker, newName);
binary = destination.addBinaryResource(txn, broker, binary, is, mimeType.getName(), -1, created, lastModified);
source.removeXMLResource(txn, broker, doc.getFileURI());
} finally {
if (is != null) {
is.close();
}
if (tempFile != null) {
tempFile.delete();
}
}
}
}
}
public boolean delete() {
final BrokerPool db;
final TransactionManager tm;
try {
db = BrokerPool.getInstance();
tm = db.getTransactionManager();
} catch (final EXistException e) {
return false;
}
try (final DBBroker broker = db.getBroker()) {
collection = broker.openCollection(uri.removeLastSegment(), LockMode.WRITE_LOCK);
if (collection == null) {
return false;
}
// keep the write lock in the transaction
//transaction.registerLock(collection.getLock(), LockMode.WRITE_LOCK);
final DocumentImpl doc = collection.getDocument(broker, uri.lastSegment());
if (doc == null) {
return true;
}
try (final Txn txn = tm.beginTransaction()) {
if (doc.getResourceType() == DocumentImpl.BINARY_FILE) {
collection.removeBinaryResource(txn, broker, doc);
} else {
collection.removeXMLResource(txn, broker, uri.lastSegment());
}
tm.commit(txn);
return true;
}
} catch (final EXistException | IOException | PermissionDeniedException | LockException | TriggerException e) {
LOG.error(e);
return false;
} finally {
if(collection != null) {
collection.release(LockMode.WRITE_LOCK);
}
}
}
public boolean createNewFile() throws IOException {
final BrokerPool db;
try {
db = BrokerPool.getInstance();
} catch (final EXistException e) {
throw new IOException(e);
}
try (final DBBroker broker = db.getBroker()) {
// if (!uri.startsWith("/db"))
// uri = XmldbURI.DB.append(uri);
//
try {
if (uri.endsWith("/")) {
throw new IOException("It collection, but should be resource: " + uri);
}
} catch (final Exception e) {
throw new IOException(e);
}
final XmldbURI collectionURI = uri.removeLastSegment();
collection = broker.getCollection(collectionURI);
if (collection == null) {
throw new IOException("Collection not found: " + collectionURI);
}
final XmldbURI fileName = uri.lastSegment();
// try {
// resource = broker.getXMLResource(uri, LockMode.READ_LOCK);
// } catch (final PermissionDeniedException e1) {
// } finally {
// if (resource != null) {
// resource.getUpdateLock().release(LockMode.READ_LOCK);
// collection = resource.getCollection();
// initialized = true;
//
// return false;
// }
// }
//
try {
resource = broker.getResource(uri, Permission.READ);
} catch (final PermissionDeniedException e1) {
} finally {
if (resource != null) {
collection = resource.getCollection();
initialized = true;
return false;
}
}
MimeType mimeType = MimeTable.getInstance().getContentTypeFor(fileName);
if (mimeType == null) {
mimeType = MimeType.BINARY_TYPE;
}
final TransactionManager tm = db.getTransactionManager();
try (final Txn transaction = tm.beginTransaction()) {
if (mimeType.isXMLType()) {
// store as xml resource
final String str = "<empty/>";
final IndexInfo info = collection.validateXMLResource(transaction, broker, fileName, str);
info.getDocument().getMetadata().setMimeType(mimeType.getName());
info.getDocument().getPermissions().setMode(DEFAULT_RESOURCE_PERM);
collection.store(transaction, broker, info, str);
} else {
// store as binary resource
try (final InputStream is = new ByteArrayInputStream("".getBytes(UTF_8))) {
final BinaryDocument blob = new BinaryDocument(db, collection, fileName);
blob.getPermissions().setMode(DEFAULT_RESOURCE_PERM);
collection.addBinaryResource(transaction, broker, blob, is,
mimeType.getName(), 0L, new Date(), new Date());
}
}
tm.commit(transaction);
} catch (final Exception e) {
LOG.error(e);
throw new IOException(e);
}
} catch (final Exception e) {
LOG.error(e);
return false;
}
return true;
}
private synchronized void init() throws IOException {
if (initialized) {
collection = null;
resource = null;
initialized = false;
}
try {
final BrokerPool db = BrokerPool.getInstance();
try (final DBBroker broker = db.getBroker()) {
//collection
if (uri.endsWith("/")) {
collection = broker.getCollection(uri);
if (collection == null) {
throw new IOException("Resource not found: " + uri);
}
//resource
} else {
try {
resource = broker.getXMLResource(uri, LockMode.READ_LOCK);
if (resource == null) {
//may be, it's collection ... checking ...
collection = broker.getCollection(uri);
if (collection == null) {
throw new IOException("Resource not found: " + uri);
}
} else {
collection = resource.getCollection();
}
} finally {
if (resource != null) {
resource.getUpdateLock().release(LockMode.READ_LOCK);
}
}
}
}
} catch (final IOException e) {
throw e;
} catch (final Exception e) {
throw new IOException(e);
}
initialized = true;
}
private Permission getPermission() throws IOException {
init();
if (resource != null) {
return resource.getPermissions();
}
if (collection != null) {
return collection.getPermissionsNoLock();
}
throw new IOException("this never should happen");
}
private Subject getBrokerUser() throws IOException {
try {
final BrokerPool db = BrokerPool.getInstance();
try (final DBBroker broker = db.getBroker()) {
return broker.getCurrentSubject();
}
} catch (final EXistException e) {
throw new IOException(e);
}
}
public Reader getReader() throws IOException {
final InputStream is = getConnection().getInputStream();
final BufferedInputStream bis = new BufferedInputStream(is);
return new InputStreamReader(bis);
}
public BufferedReader getBufferedReader() throws IOException {
return new BufferedReader(getReader());
}
private URLConnection connection = null;
private URLConnection getConnection() throws IOException {
if (connection == null) {
try {
final BrokerPool db = BrokerPool.getInstance();
try (final DBBroker broker = db.getBroker()) {
final Subject subject = broker.getCurrentSubject();
final URL url = new URL("xmldb:exist://jsessionid:" + subject.getSessionId() + "@" + uri.toString());
connection = url.openConnection();
}
} catch (final IllegalArgumentException e) {
throw new IOException(e);
} catch (final MalformedURLException e) {
throw new IOException(e);
} catch (final EXistException e) {
throw new IOException(e);
}
}
return connection;
}
public InputStream getInputStream() throws IOException {
return getConnection().getInputStream();
}
public Writer getWriter() throws IOException {
return new BufferedWriter(new OutputStreamWriter(getOutputStream(false)));
}
public OutputStream getOutputStream() throws IOException {
return getOutputStream(false);
}
public OutputStream getOutputStream(boolean append) throws IOException {
//XXX: code append
if (append) {
LOG.error("BUG: OutputStream in append mode!");
}
return getConnection().getOutputStream();
}
public DocumentImpl getDocument() throws IOException {
init();
return resource;
}
public Collection getCollection() throws IOException {
if (!initialized) {
try {
final BrokerPool db = BrokerPool.getInstance();
try (final DBBroker broker = db.getBroker()) {
if (uri.endsWith("/")) {
collection = broker.getCollection(uri);
} else {
collection = broker.getCollection(uri);
if (collection == null) {
collection = broker.getCollection(uri.removeLastSegment());
}
}
if (collection == null) {
throw new IOException("Collection not found: " + uri);
}
return collection;
}
} catch (final Exception e) {
throw new IOException(e);
}
}
if (resource == null) {
return collection;
} else {
return resource.getCollection();
}
}
public String[] list() {
if (isDirectory()) {
try {
final BrokerPool db = BrokerPool.getInstance();
try (final DBBroker broker = db.getBroker()) {
final List<String> list = new ArrayList<>();
for (final CollectionEntry entry : collection.getEntries(broker)) {
list.add(entry.getUri().lastSegment().toString());
}
return list.toArray(new String[list.size()]);
}
} catch (final LockException | PermissionDeniedException | EXistException e) {
LOG.error(e);
return new String[0];
}
}
return new String[0];
}
// public String[] list(FilenameFilter filter) {
// throw new IllegalAccessError("not implemeted");
// }
public File[] listFiles() {
if (!isDirectory()) {
return null;
}
if (collection == null) {
return null;
}
try {
final BrokerPool db = BrokerPool.getInstance();
try (final DBBroker broker = db.getBroker()) {
collection.getLock().acquire(LockMode.READ_LOCK);
final File[] children = new File[collection.getChildCollectionCount(broker) +
collection.getDocumentCount(broker)];
//collections
int j = 0;
for (final Iterator<XmldbURI> i = collection.collectionIterator(broker); i.hasNext(); j++)
children[j] = new Resource(collection.getURI().append(i.next()));
//collections
final List<XmldbURI> allresources = new ArrayList<XmldbURI>();
DocumentImpl doc = null;
for (final Iterator<DocumentImpl> i = collection.iterator(broker); i.hasNext(); ) {
doc = i.next();
// Include only when (1) locktoken is present or (2)
// locktoken indicates that it is not a null resource
final LockToken lock = doc.getMetadata().getLockToken();
if (lock == null || (!lock.isNullResource())) {
allresources.add(doc.getURI());
}
}
// Copy content of list into String array.
for (final Iterator<XmldbURI> i = allresources.iterator(); i.hasNext(); j++) {
children[j] = new Resource(i.next());
}
return children;
} catch (final LockException e) {
//throw new IOException("Failed to acquire lock on collection '" + uri + "'");
return null;
} catch (final Exception e) {
return null;
} finally {
collection.release(LockMode.READ_LOCK);
}
} catch (final Exception e) {
return null;
}
}
public File[] listFiles(FilenameFilter filter) {
throw new IllegalAccessError("not implemeted");
}
public File[] listFiles(FileFilter filter) {
throw new IllegalAccessError("not implemeted");
}
public synchronized long length() {
try {
init();
} catch (final IOException e) {
return 0L;
}
if (resource != null) {
//report size for binary resource only
if (resource instanceof BinaryDocument) {
return resource.getContentLength();
}
}
return 0L;
}
private static XmldbURI normalize(final XmldbURI uri) {
return uri.startsWith(XmldbURI.ROOT_COLLECTION_URI) ?
uri :
uri.prepend(XmldbURI.ROOT_COLLECTION_URI);
}
public String getPath() {
return normalize(uri).toString();// uri.toString();
}
public String getAbsolutePath() {
return normalize(uri).toString();// uri.toString();
}
public boolean isXML() throws IOException {
init();
if (resource != null) {
if (resource instanceof BinaryDocument) {
return false;
} else {
return true;
}
}
return false;
}
protected Path getFile() throws FileNotFoundException {
if (isDirectory()) {
throw new FileNotFoundException("unsupported operation for collection.");
}
DocumentImpl doc;
try {
if (!exists()) {
createNewFile();
}
doc = getDocument();
} catch (final IOException e) {
throw new FileNotFoundException(e.getMessage());
}
try {
final BrokerPool db = BrokerPool.getInstance();
try (final DBBroker broker = db.getBroker()) {
if (doc instanceof BinaryDocument) {
return broker.getBinaryFile(((BinaryDocument) doc));
} else {
return serialize(broker, doc);
}
}
} catch (final Exception e) {
throw new FileNotFoundException(e.getMessage());
}
// throw new FileNotFoundException("unsupported operation for "+doc.getClass()+".");
}
public boolean setReadOnly() {
try {
modifyMetadata(new ModifyMetadata() {
@Override
public void modify(DocumentImpl resource) throws IOException {
Permission perm = resource.getPermissions();
try {
perm.setMode(perm.getMode() | (READ << 6) & ~(WRITE << 6));
} catch (PermissionDeniedException e) {
throw new IOException(e);
}
}
@Override
public void modify(Collection collection) throws IOException {
Permission perm = collection.getPermissionsNoLock();
try {
perm.setMode(perm.getMode() | (READ << 6) & ~(WRITE << 6));
} catch (PermissionDeniedException e) {
throw new IOException(e);
}
}
});
} catch (IOException e) {
return false;
}
return true;
}
public boolean setExecutable(boolean executable, boolean ownerOnly) {
try {
modifyMetadata(new ModifyMetadata() {
@Override
public void modify(DocumentImpl resource) throws IOException {
Permission perm = resource.getPermissions();
try {
perm.setMode(perm.getMode() | (EXECUTE << 6));
} catch (PermissionDeniedException e) {
throw new IOException(e);
}
}
@Override
public void modify(Collection collection) throws IOException {
Permission perm = collection.getPermissionsNoLock();
try {
perm.setMode(perm.getMode() | (EXECUTE << 6));
} catch (PermissionDeniedException e) {
throw new IOException(e);
}
}
});
} catch (IOException e) {
return false;
}
return true;
}
public boolean canExecute() {
try {
return getPermission().validate(getBrokerUser(), EXECUTE);
} catch (final IOException e) {
return false;
}
}
public boolean canRead() {
try {
return getPermission().validate(getBrokerUser(), READ);
} catch (final IOException e) {
return false;
}
}
long lastModified = 0L;
public boolean setLastModified(final long time) {
lastModified = time;
try {
modifyMetadata(new ModifyMetadata() {
@Override
public void modify(DocumentImpl resource) throws IOException {
resource.getMetadata().setLastModified(time);
}
@Override
public void modify(Collection collection) throws IOException {
throw new IOException("LastModified can't be set for collection.");
}
});
} catch (IOException e) {
return false;
}
return true;
}
public long lastModified() {
try {
init();
} catch (final IOException e) {
return lastModified;
}
if (resource != null) {
return resource.getMetadata().getLastModified();
}
if (collection != null) {
//TODO: need lastModified for collection
return collection.getCreationTime();
}
return lastModified;
}
interface ModifyMetadata {
public void modify(DocumentImpl resource) throws IOException;
public void modify(Collection collection) throws IOException;
}
private void modifyMetadata(ModifyMetadata method) throws IOException {
// if (initialized) {return;}
final BrokerPool db;
try {
db = BrokerPool.getInstance();
} catch (final EXistException e) {
throw new IOException(e);
}
try (final DBBroker broker = db.getBroker()) {
final TransactionManager tm = db.getTransactionManager();
try {
//collection
if (uri.endsWith("/")) {
collection = broker.getCollection(uri);
if (collection == null) {
throw new IOException("Resource not found: " + uri);
}
//resource
} else {
resource = broker.getXMLResource(uri, LockMode.READ_LOCK);
if (resource == null) {
//may be, it's collection ... checking ...
collection = broker.getCollection(uri);
if (collection == null) {
throw new IOException("Resource not found: " + uri);
}
try (final Txn txn = tm.beginTransaction()) {
method.modify(collection);
broker.saveCollection(txn, collection);
tm.commit(txn);
}
} else {
collection = resource.getCollection();
try (final Txn txn = tm.beginTransaction()) {
method.modify(resource);
broker.storeMetadata(txn, resource);
tm.commit(txn);
}
}
}
} catch (final Exception e) {
LOG.error(e);
throw new IOException(e);
} finally {
if (resource != null) {
resource.getUpdateLock().release(LockMode.READ_LOCK);
}
}
} catch (final EXistException e) {
LOG.error(e);
throw new IOException(e);
}
initialized = true;
}
}