Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[6.x.x] Track open resources #5324

Draft
wants to merge 1 commit into
base: develop-6.x.x
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -30,18 +30,17 @@

import com.evolvedbinary.j8fu.lazy.LazyVal;

import org.apache.xmlrpc.client.XmlRpcClient;
import org.exist.security.Permission;
import org.exist.storage.serializers.EXistOutputKeys;
import org.exist.util.EXistInputSource;
import org.exist.util.FileUtils;
import org.exist.util.Leasable;
import org.exist.util.io.ByteArrayContent;
import org.exist.util.io.ContentFile;
import org.apache.commons.io.input.UnsynchronizedByteArrayInputStream;
import org.apache.commons.io.output.UnsynchronizedByteArrayOutputStream;
import org.exist.util.io.TemporaryFileManager;
import org.exist.util.io.VirtualTempPath;
import org.jline.utils.Log;
import org.xml.sax.InputSource;
import org.xmldb.api.base.Collection;
import org.xmldb.api.base.ErrorCodes;
Expand All @@ -63,6 +62,7 @@ public abstract class AbstractRemoteResource extends AbstractRemote
private Permission permissions = null;
private boolean closed;
private LazyVal<Integer> inMemoryBufferSize;
private List<Runnable> closeActions;

Date dateCreated = null;
Date dateModified = null;
Expand Down Expand Up @@ -557,10 +557,28 @@ public final void close() {
contentFile = null;
}
} finally {
if (closeActions != null) {
closeActions.removeIf(this::executeCloseAction);
}
closed = true;
}
}
}
protected void addCloseAction(Runnable action) {
if (closeActions == null) {
closeActions = new ArrayList<>(1);
}
closeActions.add(action);
}

private boolean executeCloseAction(Runnable action) {
try {
action.run();
} catch (Exception e) {
Log.error("Failed to execute close action", e);
}
return true;
}

@Override
public final void freeResources() {
Expand Down
117 changes: 72 additions & 45 deletions exist-core/src/main/java/org/exist/xmldb/RemoteResourceSet.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,7 @@
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Properties;
import java.util.*;
import java.util.zip.DataFormatException;
import java.util.zip.Inflater;
import javax.xml.transform.OutputKeys;
Expand All @@ -41,7 +36,6 @@
import org.apache.commons.codec.binary.Hex;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.xmlrpc.XmlRpcException;
import org.apache.xmlrpc.client.XmlRpcClient;
import org.exist.storage.serializers.EXistOutputKeys;
import org.exist.util.Leasable;
Expand All @@ -57,24 +51,38 @@

public class RemoteResourceSet implements ResourceSet, AutoCloseable {

private static final Logger LOG = LogManager.getLogger(RemoteResourceSet.class.getName());

private final Leasable<XmlRpcClient> leasableXmlRpcClient;
private final RemoteCollection collection;
private final List<ResourceEntry> resources;
private final Properties outputProperties;

private int handle = -1;
private int hash = -1;
private final List resources;
private final Properties outputProperties;
private boolean closed;
private LazyVal<Integer> inMemoryBufferSize;

private static Logger LOG = LogManager.getLogger(RemoteResourceSet.class.getName());

public RemoteResourceSet(final Leasable<XmlRpcClient> leasableXmlRpcClient, final RemoteCollection col, final Properties properties, final Object[] resources, final int handle, final int hash) {
this.leasableXmlRpcClient = leasableXmlRpcClient;
this.handle = handle;
this.hash = hash;
this.resources = new ArrayList(Arrays.asList(resources));
this.resources = new ArrayList(resources.length);
this.collection = col;
this.outputProperties = properties;
int index = 0;
for (Object resource : resources) {
this.resources.add(asEntry(index, resource));
index++;
}
}

private ResourceEntry asEntry(int index, Object resource) {
if (resource instanceof Resource) {
return new ResourceEntry(index, Collections.emptyMap(), (Resource) resource);
} else {
return new ResourceEntry(index, (Map<String, String>) resource, null);
}
}

private final int getInMemorySize(Properties properties) {
Expand All @@ -83,10 +91,10 @@ private final int getInMemorySize(Properties properties) {
}
return inMemoryBufferSize.get();
}

@Override
public void addResource(final Resource resource) {
resources.add(resource);
resources.add(asEntry(resources.size(), resource));
}

@Override
Expand Down Expand Up @@ -199,40 +207,12 @@ public Resource getResource(final long pos) throws XMLDBException {
if (pos >= resources.size()) {
return null;
}

if(resources.get((int) pos) instanceof Resource) {
return (Resource) resources.get((int) pos);
} else {
final Map<String, String> item = (Map<String, String>)resources.get((int)pos);

switch(item.get("type")) {
case "node()":
case "document-node()":
case "element()":
case "attribute()":
case "text()":
case "processing-instruction()":
case "comment()":
case "namespace()":
case "cdata-section()":
return getResourceNode((int)pos, item);

case "xs:base64Binary":
return getResourceBinaryValue((int)pos, item, Base64::decodeBase64);

case "xs:hexBinary":
return getResourceBinaryValue((int)pos, item, Hex::decodeHex);

default: // atomic value
return getResourceValue((int)pos, item);

}
}
return resources.get((int)pos).resource();
}

private RemoteXMLResource getResourceNode(final int pos, final Map<String, String> nodeDetail) throws XMLDBException {
final String doc = nodeDetail.get("docUri");
final Optional<String> s_id = Optional.ofNullable(nodeDetail.get("nodeId"));
final Optional<String> s_id = Optional.ofNullable(nodeDetail.get("nodeId"));
final Optional<String> s_type = Optional.ofNullable(nodeDetail.get("type"));
final XmldbURI docUri;
try {
Expand Down Expand Up @@ -270,7 +250,7 @@ private <E extends Exception> RemoteBinaryResource getResourceBinaryValue(final
final byte[] content;
try {
content = binaryDecoder.apply(valueDetail.get("value"));
} catch(final Exception e) {
} catch (final Exception e) {
throw new XMLDBException(ErrorCodes.UNKNOWN_ERROR, e);
}

Expand Down Expand Up @@ -333,5 +313,52 @@ public Resource nextResource() throws XMLDBException {
return getResource(pos++);
}
}

class ResourceEntry {
private final int pos;
private final Map<String, String> item;
private Resource resource;

private ResourceEntry(int pos, Map<String, String> item, Resource resource) {
this.pos = pos;
this.item = item;
this.resource = resource;
}

Resource resource() throws XMLDBException {
if (resource == null) {
final AbstractRemoteResource remoteResource;
switch (item.get("type")) {
case "node()":
case "document-node()":
case "element()":
case "attribute()":
case "text()":
case "processing-instruction()":
case "comment()":
case "namespace()":
case "cdata-section()":
remoteResource = getResourceNode(pos, item);
break;
case "xs:base64Binary":
remoteResource = getResourceBinaryValue(pos, item, Base64::decodeBase64);
break;
case "xs:hexBinary":
remoteResource = getResourceBinaryValue(pos, item, Hex::decodeHex);
break;
default: // atomic value
remoteResource = getResourceValue(pos, item);
break;
}
remoteResource.addCloseAction(this::reset);
resource = remoteResource;
}
return resource;
}

void reset() {
resource = null;
}
}
}

Loading