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

[WIP] attempt to make sure streams are closed #5552

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
40 changes: 21 additions & 19 deletions exist-core/src/main/java/org/exist/xquery/ArrowOperator.java
Original file line number Diff line number Diff line change
Expand Up @@ -95,23 +95,7 @@ public Sequence eval(Sequence contextSequence, final Item contextItem) throws XP
}
contextSequence = leftExpr.eval(contextSequence, null);

final FunctionReference fref;
if (fcall != null) {
fref = new FunctionReference(this, fcall);
} else {
final Sequence funcSeq = funcSpec.eval(contextSequence, contextItem);
if (funcSeq.getCardinality() != Cardinality.EXACTLY_ONE)
{throw new XPathException(this, ErrorCodes.XPTY0004,
"Expected exactly one item for the function to be called, got " + funcSeq.getItemCount() +
". Expression: " + ExpressionDumper.dump(funcSpec));}
final Item item0 = funcSeq.itemAt(0);
if (!Type.subTypeOf(item0.getType(), Type.FUNCTION_REFERENCE)) {
throw new XPathException(this, ErrorCodes.XPTY0004,
"Type error: expected function, got " + Type.getTypeName(item0.getType()));
}
fref = (FunctionReference)item0;
}
try {
try (final FunctionReference fref = getFunctionReference(contextSequence, contextItem)) {
final List<Expression> fparams = new ArrayList<>(parameters.size() + 1);
fparams.add(new ContextParam(context, contextSequence));
fparams.addAll(parameters);
Expand All @@ -122,11 +106,29 @@ public Sequence eval(Sequence contextSequence, final Item contextItem) throws XP
fref.analyze(new AnalyzeContextInfo(cachedContextInfo));
// Evaluate the function
return fref.eval(null);
} finally {
fref.close();
}
}

private FunctionReference getFunctionReference(final Sequence contextSequence, final Item contextItem) throws XPathException {
if (fcall != null) {
return new FunctionReference(this, fcall);
}

final Sequence funcSeq = funcSpec.eval(contextSequence, contextItem);
if (funcSeq.getCardinality() != Cardinality.EXACTLY_ONE) {
throw new XPathException(this, ErrorCodes.XPTY0004,
"Expected exactly one item for the function to be called, got " + funcSeq.getItemCount() +
". Expression: " + ExpressionDumper.dump(funcSpec));
}

final Item item = funcSeq.itemAt(0);
if (!Type.subTypeOf(item.getType(), Type.FUNCTION_REFERENCE)) {
throw new XPathException(this, ErrorCodes.XPTY0004,
"Type error: expected function, got " + Type.getTypeName(item.getType()));
}
return (FunctionReference) item;
}

@Override
public int returnsType() {
return fcall == null ? Type.ITEM : fcall.returnsType();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,29 +22,18 @@
package org.exist.xquery.functions.util;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.charset.UnsupportedCharsetException;
import java.util.Base64;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.exist.EXistException;
import org.exist.dom.QName;
import org.apache.commons.io.input.UnsynchronizedByteArrayInputStream;
import org.apache.commons.io.output.UnsynchronizedByteArrayOutputStream;
import org.exist.xquery.BasicFunction;
import org.exist.xquery.Cardinality;
import org.exist.xquery.FunctionSignature;
import org.exist.xquery.XPathException;
import org.exist.xquery.XQueryContext;
import org.exist.xquery.value.Base64BinaryValueType;
import org.exist.xquery.value.BinaryValue;
import org.exist.xquery.value.BinaryValueFromInputStream;
import org.exist.xquery.value.FunctionParameterSequenceType;
import org.exist.xquery.value.FunctionReturnSequenceType;
import org.exist.xquery.value.Sequence;
import org.exist.xquery.value.SequenceType;
import org.exist.xquery.value.StringValue;
import org.exist.xquery.value.Type;

import static java.nio.charset.StandardCharsets.UTF_8;
import org.exist.xquery.*;
import org.exist.xquery.value.*;

public class BinaryToString extends BasicFunction {

Expand Down Expand Up @@ -95,37 +84,47 @@ public BinaryToString(XQueryContext context, FunctionSignature signature) {
}

@Override
public Sequence eval(Sequence[] args, Sequence contextSequence)
throws XPathException {
public Sequence eval(Sequence[] args, Sequence contextSequence) throws XPathException {

if(args[0].isEmpty()) {
if (args[0].isEmpty()) {
return Sequence.EMPTY_SEQUENCE;
}
String encoding = UTF_8.name();
if(args.length == 2) {
encoding = args[1].getStringValue();
}
if(isCalledAs("binary-to-string")) {

final Charset encoding = getCharset(args);

if (isCalledAs("binary-to-string")) {
return binaryToString((BinaryValue) args[0].itemAt(0), encoding);
}

return stringToBinary(args[0].getStringValue(), encoding);
}

private Charset getCharset(Sequence[] args) throws XPathException {
final Charset encoding;
if (args.length == 2) {
final String stringValue = args[1].getStringValue();
try {
encoding = stringValue.isEmpty() ? StandardCharsets.UTF_8 : Charset.forName(stringValue);
} catch(final UnsupportedCharsetException e) {
throw new XPathException(this, UtilErrorCodes.UNRECOGNIZED_ENCODING, "Unsupported encoding: " + stringValue);
}
} else {
return stringToBinary(args[0].getStringValue(), encoding);
encoding = StandardCharsets.UTF_8;
}
return encoding;
}

protected StringValue binaryToString(BinaryValue binary, String encoding) throws XPathException {
protected StringValue binaryToString(final BinaryValue binary, final Charset encoding) throws XPathException {
try (final UnsynchronizedByteArrayOutputStream os = new UnsynchronizedByteArrayOutputStream()) {
binary.streamBinaryTo(os);
return new StringValue(this, os.toString(encoding));
} catch(final IOException ioe) {
throw new XPathException(this, ioe);
throw new XPathException(this, UtilErrorCodes.IO_ERROR, ioe);
}
}

protected BinaryValue stringToBinary(String str, String encoding) throws XPathException {
try {
return BinaryValueFromInputStream.getInstance(context, new Base64BinaryValueType(), new UnsynchronizedByteArrayInputStream(str.getBytes(encoding)), this);
} catch(final UnsupportedEncodingException e) {
throw new XPathException(this, "Unsupported encoding: " + encoding);
}
protected BinaryValue stringToBinary(final String str, final Charset encoding) throws XPathException {
return new BinaryValueFromBinaryString(new Base64BinaryValueType(),
Base64.getEncoder().encodeToString(str.getBytes(encoding)));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* eXist-db Open Source Native XML Database
* Copyright (C) 2001 The eXist-db Authors
*
* info@exist-db.org
* http://www.exist-db.org
*
* This library 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.1 of the License, or (at your option) any later version.
*
* This library 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 library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package org.exist.xquery.functions.util;

import org.exist.dom.QName;
import org.exist.xquery.ErrorCodes;

class UtilErrorCodes extends ErrorCodes.ErrorCode {
public static final ErrorCodes.ErrorCode UNRECOGNIZED_ENCODING = new UtilErrorCodes("UNRECOGNIZED_ENCODING",
"The encoding is not recognized.");

public static final ErrorCodes.ErrorCode IO_ERROR = new UtilErrorCodes("IO_ERROR",
"There was an issue accessing system resources.");

UtilErrorCodes(final String code, final String description) {
super(new QName(code, UtilModule.NAMESPACE_URI, UtilModule.PREFIX), description);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import org.exist.xquery.XPathException;

import java.io.*;
import java.nio.charset.Charset;

import static java.nio.charset.StandardCharsets.UTF_8;

Expand All @@ -45,6 +46,7 @@

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

// private final Charset encoding;
private final String value;
private boolean closed = false;

Expand All @@ -55,40 +57,26 @@
public BinaryValueFromBinaryString(final Expression expression, BinaryValueType binaryValueType, String value) throws XPathException {
super(expression, null, binaryValueType);
this.value = binaryValueType.verifyAndFormatString(value);
//this.encoding = Charset.defaultCharset();
}

public BinaryValueFromBinaryString(BinaryValueType binaryValueType, String value, Charset encoding) throws XPathException {

Check warning on line 63 in exist-core/src/main/java/org/exist/xquery/value/BinaryValueFromBinaryString.java

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

exist-core/src/main/java/org/exist/xquery/value/BinaryValueFromBinaryString.java#L63

Avoid unused constructor parameters such as 'encoding'.
super(null, null, binaryValueType);
this.value = binaryValueType.verifyAndFormatString(value);
// this.encoding = encoding;
}

@Override
public BinaryValue convertTo(BinaryValueType binaryValueType) throws XPathException {
public BinaryValue convertTo(final BinaryValueType binaryValueType) throws XPathException {
//TODO temporary approach, consider implementing a TranscodingBinaryValueFromBinaryString(BinaryValueFromBinaryString) class
//that only does the transncoding lazily

final UnsynchronizedByteArrayOutputStream baos = new UnsynchronizedByteArrayOutputStream();
FilterOutputStream fos = null;
try {

//that only does the transcoding lazily
try (final UnsynchronizedByteArrayOutputStream baos = new UnsynchronizedByteArrayOutputStream(); final FilterOutputStream fos = binaryValueType.getEncoder(baos)) {
//transcode
fos = binaryValueType.getEncoder(baos);
streamBinaryTo(fos);

return new BinaryValueFromBinaryString(getExpression(), binaryValueType, baos.toString(UTF_8));
} catch (final IOException ioe) {
throw new XPathException(getExpression(), ioe);
} finally {
if (fos != null) {
try {
fos.close();
} catch (final IOException ioe) {
LOG.error("Unable to close stream: {}", ioe.getMessage(), ioe);
}
}

try {
baos.close();
} catch (final IOException ioe) {
LOG.error("Unable to close stream: {}", ioe.getMessage(), ioe);
}
}

return new BinaryValueFromBinaryString(getExpression(), binaryValueType, baos.toString(UTF_8));
}

@Override
Expand All @@ -101,7 +89,7 @@
final FilterOutputStream fos = getBinaryValueType().getDecoder(safeOutputStream);

//write with the decoder
final byte data[] = value.getBytes();
final byte[] data = value.getBytes();
fos.write(data);

//we do have to close the decoders output stream though
Expand All @@ -117,13 +105,13 @@
@Override
public void streamTo(OutputStream os) throws IOException {
//write
final byte data[] = value.getBytes(); //TODO consider a more efficient approach for writing large strings
final byte[] data = value.getBytes(); //TODO consider a more efficient approach for writing large strings
os.write(data);
}

@Override
public InputStream getInputStream() {
//TODO consider a more efficient approach for writting large strings
//TODO consider a more efficient approach for writing large strings
final UnsynchronizedByteArrayOutputStream baos = new UnsynchronizedByteArrayOutputStream();
try {
streamBinaryTo(baos);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@
import org.exist.xquery.value.BinaryValue;
import org.exist.xquery.value.StringValue;
import org.junit.Test;

import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;

import static org.junit.Assert.assertEquals;

/**
Expand All @@ -36,9 +40,9 @@
public class BinaryToStringTest {

@Test
public void roundtrip() throws XPathException {
public void roundTrip() throws XPathException {
final String value = "hello world";
final String encoding = "UTF-8";
final Charset encoding = StandardCharsets.UTF_8;

TestableBinaryToString testable = new TestableBinaryToString(new MockXQueryContext(), null);

Expand All @@ -54,12 +58,12 @@ public TestableBinaryToString(XQueryContext context, FunctionSignature signature
}

@Override
public StringValue binaryToString(BinaryValue binary, String encoding) throws XPathException {
public StringValue binaryToString(BinaryValue binary, Charset encoding) throws XPathException {
return super.binaryToString(binary, encoding);
}

@Override
public BinaryValue stringToBinary(String str, String encoding) throws XPathException {
public BinaryValue stringToBinary(String str, Charset encoding) throws XPathException {
return super.stringToBinary(str, encoding);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,12 @@
import org.exist.xquery.value.BinaryValue;
import org.exist.xquery.value.BinaryValueFromInputStream;
import org.exist.xquery.value.FunctionParameterSequenceType;
import org.exist.xquery.value.BooleanValue;
import org.exist.xquery.value.Sequence;
import org.exist.xquery.value.SequenceType;
import org.exist.xquery.value.Type;

import static java.util.zip.Deflater.DEFAULT_COMPRESSION;


/**
* Deflate compression
Expand All @@ -52,7 +53,7 @@ public class DeflateFunction extends BasicFunction
{
private final static QName DEFLATE_FUNCTION_NAME = new QName("deflate", CompressionModule.NAMESPACE_URI, CompressionModule.PREFIX);

public final static FunctionSignature signatures[] = {
public final static FunctionSignature[] signatures = {
new FunctionSignature(
DEFLATE_FUNCTION_NAME,
"Deflate data (RFC 1950)",
Expand Down Expand Up @@ -84,25 +85,29 @@ public DeflateFunction(XQueryContext context, FunctionSignature signature)
public Sequence eval(Sequence[] args, Sequence contextSequence) throws XPathException
{
// is there some data to Deflate?
if(args[0].isEmpty())
if (args[0].isEmpty()) {
return Sequence.EMPTY_SEQUENCE;
}

BinaryValue bin = (BinaryValue) args[0].itemAt(0);
final BinaryValue bin = (BinaryValue) args[0].itemAt(0);

boolean rawflag = false;
if(args.length > 1 && !args[1].isEmpty())
rawflag = args[1].itemAt(0).convertTo(Type.BOOLEAN).effectiveBooleanValue();
boolean rawflag = false;
if (args.length > 1 && !args[1].isEmpty()) {
rawflag = args[1].itemAt(0).convertTo(Type.BOOLEAN).effectiveBooleanValue();
}

Deflater defl = new Deflater(java.util.zip.Deflater.DEFAULT_COMPRESSION, rawflag);
final Deflater deflater = new Deflater(DEFAULT_COMPRESSION, rawflag);

// deflate the data
try(final UnsynchronizedByteArrayOutputStream baos = new UnsynchronizedByteArrayOutputStream();
DeflaterOutputStream dos = new DeflaterOutputStream(baos, defl)) {
try (
final UnsynchronizedByteArrayOutputStream stream = new UnsynchronizedByteArrayOutputStream();
DeflaterOutputStream dos = new DeflaterOutputStream(stream, deflater)
) {
bin.streamBinaryTo(dos);
dos.flush();
dos.finish();

return BinaryValueFromInputStream.getInstance(context, new Base64BinaryValueType(), baos.toInputStream(), this);
return BinaryValueFromInputStream.getInstance(context, new Base64BinaryValueType(), stream.toInputStream(), this);
} catch(IOException ioe) {
throw new XPathException(this, ioe.getMessage(), ioe);
}
Expand Down
Loading