From 00412e75770dacfc35225d55ed731534fb7a5fda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=CC=88rg=20Prante?= Date: Fri, 7 Sep 2018 22:59:21 +0200 Subject: [PATCH] refactor to BERReader/BERWriter interface, add locking --- .../java/org/xbib/asn1/ASN1BitString.java | 2 +- .../main/java/org/xbib/asn1/ASN1Boolean.java | 2 +- .../java/org/xbib/asn1/ASN1Enumerated.java | 2 +- .../main/java/org/xbib/asn1/ASN1Integer.java | 2 +- .../org/xbib/asn1/ASN1ObjectIdentifier.java | 2 +- .../java/org/xbib/asn1/ASN1OctetString.java | 2 +- .../org/xbib/asn1/ASN1VideotexString.java | 3 +- .../java/org/xbib/asn1/BERConstructed.java | 20 +- .../main/java/org/xbib/asn1/BEREncoding.java | 188 +--------- .../main/java/org/xbib/asn1/BERPrimitive.java | 22 +- .../main/java/org/xbib/asn1/io/BERReader.java | 12 + .../main/java/org/xbib/asn1/io/BERWriter.java | 12 + .../xbib/asn1/io/InputStreamBERReader.java | 166 +++++++++ .../xbib/asn1/io/OutputStreamBERWriter.java | 96 ++++++ build.gradle | 14 +- gradle.properties | 4 +- gradle/wrapper/gradle-wrapper.jar | Bin 54333 -> 54413 bytes gradle/wrapper/gradle-wrapper.properties | 4 +- z3950/build.gradle | 2 + .../java/org/xbib/io/iso23950/ZClient.java | 325 ++++++++++-------- .../org/xbib/io/iso23950/netty/Client.java | 69 ++++ .../operations/AbstractOperation.java | 44 +++ .../io/iso23950/operations/InitOperation.java | 29 +- .../iso23950/operations/PresentOperation.java | 30 +- .../iso23950/operations/SearchOperation.java | 39 ++- .../org/xbib/io/iso23950/ZClientTest.java | 2 +- 26 files changed, 687 insertions(+), 406 deletions(-) create mode 100644 asn1/src/main/java/org/xbib/asn1/io/BERReader.java create mode 100644 asn1/src/main/java/org/xbib/asn1/io/BERWriter.java create mode 100644 asn1/src/main/java/org/xbib/asn1/io/InputStreamBERReader.java create mode 100644 asn1/src/main/java/org/xbib/asn1/io/OutputStreamBERWriter.java create mode 100644 z3950/src/main/java/org/xbib/io/iso23950/netty/Client.java create mode 100644 z3950/src/main/java/org/xbib/io/iso23950/operations/AbstractOperation.java diff --git a/asn1/src/main/java/org/xbib/asn1/ASN1BitString.java b/asn1/src/main/java/org/xbib/asn1/ASN1BitString.java index 6dc1d72..8461a5a 100644 --- a/asn1/src/main/java/org/xbib/asn1/ASN1BitString.java +++ b/asn1/src/main/java/org/xbib/asn1/ASN1BitString.java @@ -57,7 +57,7 @@ public final class ASN1BitString extends ASN1Any { } if (berEncoding instanceof BERPrimitive) { BERPrimitive ber = (BERPrimitive) berEncoding; - int[] encoding = ber.peek(); + int[] encoding = ber.getContentOctets(); if (encoding.length < 1) { throw new ASN1EncodingException("ASN1 BIT STRING: invalid encoding, length = " + encoding.length); } diff --git a/asn1/src/main/java/org/xbib/asn1/ASN1Boolean.java b/asn1/src/main/java/org/xbib/asn1/ASN1Boolean.java index aee3fe2..db68a4b 100644 --- a/asn1/src/main/java/org/xbib/asn1/ASN1Boolean.java +++ b/asn1/src/main/java/org/xbib/asn1/ASN1Boolean.java @@ -55,7 +55,7 @@ public final class ASN1Boolean extends ASN1Any { } if (berEncoding instanceof BERPrimitive) { BERPrimitive ber = (BERPrimitive) berEncoding; - int[] encoding = ber.peek(); + int[] encoding = ber.getContentOctets(); if (encoding.length != 1) { throw new ASN1EncodingException("ASN.1 BOOLEAN: invalid encoding, length = " + encoding.length); } diff --git a/asn1/src/main/java/org/xbib/asn1/ASN1Enumerated.java b/asn1/src/main/java/org/xbib/asn1/ASN1Enumerated.java index 1d58558..d214ea7 100644 --- a/asn1/src/main/java/org/xbib/asn1/ASN1Enumerated.java +++ b/asn1/src/main/java/org/xbib/asn1/ASN1Enumerated.java @@ -61,7 +61,7 @@ public final class ASN1Enumerated extends ASN1Any { throw new ASN1EncodingException("bad form, constructed"); } BERPrimitive ber = (BERPrimitive) berEncoding; - int[] encoding = ber.peek(); + int[] encoding = ber.getContentOctets(); if (encoding.length < 1) { throw new ASN1EncodingException("invalid encoding, length = " + encoding.length); } diff --git a/asn1/src/main/java/org/xbib/asn1/ASN1Integer.java b/asn1/src/main/java/org/xbib/asn1/ASN1Integer.java index e54a0a2..3dbdb75 100644 --- a/asn1/src/main/java/org/xbib/asn1/ASN1Integer.java +++ b/asn1/src/main/java/org/xbib/asn1/ASN1Integer.java @@ -63,7 +63,7 @@ public final class ASN1Integer extends ASN1Any { throw new ASN1EncodingException("bad form, constructed"); } BERPrimitive ber = (BERPrimitive) berEncoding; - int[] encoding = ber.peek(); + int[] encoding = ber.getContentOctets(); if (encoding.length < 1) { throw new ASN1EncodingException("invalid encoding, length = " + encoding.length); } diff --git a/asn1/src/main/java/org/xbib/asn1/ASN1ObjectIdentifier.java b/asn1/src/main/java/org/xbib/asn1/ASN1ObjectIdentifier.java index 81deae6..237fde8 100644 --- a/asn1/src/main/java/org/xbib/asn1/ASN1ObjectIdentifier.java +++ b/asn1/src/main/java/org/xbib/asn1/ASN1ObjectIdentifier.java @@ -58,7 +58,7 @@ public final class ASN1ObjectIdentifier extends ASN1Any { throw new ASN1EncodingException("bad form, constructed"); } BERPrimitive ber = (BERPrimitive) berEncoding; - int[] encoding = ber.peek(); + int[] encoding = ber.getContentOctets(); if (encoding.length < 2) { throw new ASN1EncodingException("invalid encoding, length = " + encoding.length); diff --git a/asn1/src/main/java/org/xbib/asn1/ASN1OctetString.java b/asn1/src/main/java/org/xbib/asn1/ASN1OctetString.java index c3cfe6b..0a1aa22 100644 --- a/asn1/src/main/java/org/xbib/asn1/ASN1OctetString.java +++ b/asn1/src/main/java/org/xbib/asn1/ASN1OctetString.java @@ -75,7 +75,7 @@ public class ASN1OctetString extends ASN1Any { } if (berEncoding instanceof BERPrimitive) { BERPrimitive ber = (BERPrimitive) berEncoding; - int[] encoding = ber.peek(); + int[] encoding = ber.getContentOctets(); StringBuilder buf = new StringBuilder(encoding.length); for (int anEncoding : encoding) { buf.append((char) (anEncoding & 0x00ff)); diff --git a/asn1/src/main/java/org/xbib/asn1/ASN1VideotexString.java b/asn1/src/main/java/org/xbib/asn1/ASN1VideotexString.java index eea4112..6fdfa41 100644 --- a/asn1/src/main/java/org/xbib/asn1/ASN1VideotexString.java +++ b/asn1/src/main/java/org/xbib/asn1/ASN1VideotexString.java @@ -28,8 +28,7 @@ public class ASN1VideotexString extends ASN1OctetString { * @param checkTag If true, it checks the tag. Use false if is implicitly tagged. * @throws ASN1Exception If the BER encoding is incorrect. */ - public ASN1VideotexString(BEREncoding ber, boolean checkTag) - throws ASN1Exception { + public ASN1VideotexString(BEREncoding ber, boolean checkTag) throws ASN1Exception { super(ber, false); if (checkTag && (ber.tagGet() != VIDEOTEX_STRING_TAG || ber.tagTypeGet() != BEREncoding.UNIVERSAL_TAG)) { throw new ASN1EncodingException("bad BER: tag=" + ber.tagGet() + diff --git a/asn1/src/main/java/org/xbib/asn1/BERConstructed.java b/asn1/src/main/java/org/xbib/asn1/BERConstructed.java index 14d8498..55850b3 100644 --- a/asn1/src/main/java/org/xbib/asn1/BERConstructed.java +++ b/asn1/src/main/java/org/xbib/asn1/BERConstructed.java @@ -1,8 +1,5 @@ package org.xbib.asn1; -import java.io.IOException; -import java.io.OutputStream; - /** * BERConstructed. * This class represents a BER encoded ASN.1 object which is @@ -38,21 +35,8 @@ public class BERConstructed extends BEREncoding { contentElements = elements; } - /** - * This method outputs the encoded octets for this object - * to the output stream. - * Note: the output is not flushed, so you must explicitly - * flush the output stream after calling this method to ensure that - * the data has been written out. - * - * @param dest OutputStream to write encoding to. - */ - @Override - public void output(OutputStream dest) throws IOException { - outputHead(dest); - for (BEREncoding contentElement : contentElements) { - contentElement.output(dest); - } + public BEREncoding[] getContentElements() { + return contentElements; } /** diff --git a/asn1/src/main/java/org/xbib/asn1/BEREncoding.java b/asn1/src/main/java/org/xbib/asn1/BEREncoding.java index 7a4d6b2..c879256 100644 --- a/asn1/src/main/java/org/xbib/asn1/BEREncoding.java +++ b/asn1/src/main/java/org/xbib/asn1/BEREncoding.java @@ -1,11 +1,5 @@ package org.xbib.asn1; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.ArrayList; -import java.util.List; - /** * This class represents a BER (Basic Encoding Rules) encoded ASN.1 object. * This is an abstract base class from which there are two specific @@ -24,8 +18,6 @@ import java.util.List; */ public abstract class BEREncoding { - private static final String ERROR = "Unexpected end in BER encoding"; - /** * Constant for indicating UNIVERSAL tag type. The value matches * the BER bit encoding. Universal tags are for types defined in @@ -53,7 +45,6 @@ public abstract class BEREncoding { */ public static final int PRIVATE_TAG = 0xC0; - private static final int MAX_BER_SIZE = 65536 * 4; /** * The tag type of this BER encoded object. This value must be * the same as that encoded in the identiferEncoding. @@ -86,153 +77,25 @@ public abstract class BEREncoding { */ private int[] lengthEncoding; - /** - * The public wrapping for doInput() method. - * - * @param inputStream the InputStream to read the raw BER from. - * @return Returns the next complete BEREncoding object read - * in from the input stream. Returns null if the - * end has been reached. - * @throws ASN1Exception If data does not represent a BER encoding - * @throws IOException On input I/O error - */ - public static BEREncoding input(InputStream inputStream) throws IOException { - int[] numBytesRead = new int[1]; - numBytesRead[0] = 0; - return doInput(inputStream, numBytesRead); + public int[] getIdentifierEncoding() { + return identifierEncoding; } - /** - * Constructs a complete BER encoding object from octets read in from - * an InputStream. - * This routine handles all forms of encoding, including the - * indefite-length method. The length is always known with this - * class. With indefinite-length encodings, - * the end-of-contents octets are not included in the returned - * object (i.e. the returned the raw BER is converted to an object - * which is in the definite-length form). - * - * @param numBytesRead a counter for all read bytes. - * @param inputStream the InputStream to read the raw BER from. - * @return the next complete BEREncoding object read - * in from the input stream. Returns null if the - * end has been reached. - * @throws IOException If data does not represent a BER encoding or input I/O error - */ - protected static BEREncoding doInput(InputStream inputStream, int[] numBytesRead) throws IOException { - int octet = inputStream.read(); - if (octet < 0) { - return null; - } - numBytesRead[0]++; - int tagType = octet & 0xC0; - boolean isCons = false; - if ((octet & 0x20) != 0) { - isCons = true; - } - int tag = octet & 0x1F; - if (tag == 0x1F) { - tag = 0; - do { - octet = inputStream.read(); - if (octet < 0) { - throw new ASN1EncodingException(ERROR); - } - numBytesRead[0]++; - tag <<= 7; - tag |= (octet & 0x7F); - } while ((octet & 0x80) != 0); - } - int length; - octet = inputStream.read(); - if (octet < 0) { - throw new ASN1EncodingException(ERROR); - } - numBytesRead[0]++; - if ((octet & 0x80) != 0) { - if ((octet & 0x7f) == 0) { - length = -1; - if (!isCons) { - throw new ASN1EncodingException("BER encoding corrupted primitive"); - } - } else { - if (4 < (octet & 0x7f)) { - throw new ASN1EncodingException("BER encoding too long"); - } - length = 0; - for (int numBytes = octet & 0x7f; 0 < numBytes; numBytes--) { - octet = inputStream.read(); - if (octet < 0) { - throw new ASN1EncodingException(ERROR); - } - numBytesRead[0]++; - length <<= 8; - length |= (octet & 0xff); - } - if (length < 0 || MAX_BER_SIZE < length) { - throw new ASN1EncodingException("BER encoding too long"); - } - } - } else { - length = octet & 0x7F; - } - if (!isCons) { - int[] contents = new int[length]; - for (int x = 0; x < length; x++) { - octet = inputStream.read(); - if (octet < 0) { - throw new ASN1EncodingException(ERROR); - } - numBytesRead[0]++; - contents[x] = octet; - } - return new BERPrimitive(tagType, tag, contents); - } else { - List chunks = new ArrayList<>(); - int totalRead = 0; - if (0 <= length) { - while (totalRead < length) { - int currentRead = numBytesRead[0]; - BEREncoding chunk = BEREncoding.doInput(inputStream, numBytesRead); - if (chunk == null) { - throw new ASN1EncodingException(ERROR); - } - chunks.add(chunk); - totalRead += numBytesRead[0] - currentRead; - } - } else { - while (true) { - BEREncoding chunk = BEREncoding.doInput(inputStream, numBytesRead); - if (chunk == null) { - throw new ASN1EncodingException(ERROR); - } - if (chunk.iTag == 0 && chunk.iTagType == BEREncoding.UNIVERSAL_TAG && chunk.iTotalLength == 2) { - break; - } else { - chunks.add(chunk); - } - } - } - int numElements = chunks.size(); - BEREncoding[] parts = new BEREncoding[numElements]; - for (int x = 0; x < numElements; x++) { - parts[x] = chunks.get(x); - } - return new BERConstructed(tagType, tag, parts); - } + public int[] getLengthEncoding() { + return lengthEncoding; } - /** - * Outputs the BER object to an OutputStream. This method should work - * with any OutputStream, whether it is from a socket, file, etc. - * Note: the output is not flushed, so you must explicitly - * flush the output stream after calling this method to ensure that - * the data has been written out. - * - * @param dest - the OutputStream to write the encoding to. - * @throws IOException On output I/O error - */ - public abstract void output(OutputStream dest) throws IOException; + public int getITag() { + return iTag; + } + + public int getITagType() { + return iTagType; + } + + public int getITotalLength() { + return iTotalLength; + } /** * Returns the BER encoded object as an array of bytes. This routine @@ -289,27 +152,6 @@ public abstract class BEREncoding { iTotalLength = identifierEncoding.length + lengthEncoding.length + length; } - /* - * This is a protected routine used for outputting an array of - * integers, interpreted as bytes, to an OutputStream. It is used - * by the superclasses to implement the "output" method. - */ - protected void outputBytes(int[] data, OutputStream dest) throws IOException { - for (int aData : data) { - dest.write(aData); - } - } - - /* - * This is a protected method used to output the encoded identifier - * and length octets to an OutputStream. It is used by the superclasses - * to implement the "output" method. - */ - protected void outputHead(OutputStream dest) throws IOException { - outputBytes(identifierEncoding, dest); - outputBytes(lengthEncoding, dest); - } - /* * Internal protected method fills in the data array (starting from index * position offset) with the encoding for the identifier and length. diff --git a/asn1/src/main/java/org/xbib/asn1/BERPrimitive.java b/asn1/src/main/java/org/xbib/asn1/BERPrimitive.java index c86b47b..5b59a47 100644 --- a/asn1/src/main/java/org/xbib/asn1/BERPrimitive.java +++ b/asn1/src/main/java/org/xbib/asn1/BERPrimitive.java @@ -1,8 +1,5 @@ package org.xbib.asn1; -import java.io.IOException; -import java.io.OutputStream; - /** * This class represents a primitive ASN.1 object encoded * according to the Basic Encoding Rules. @@ -39,8 +36,7 @@ public class BERPrimitive extends BEREncoding { * @see org.xbib.asn1.BEREncoding#CONTEXT_SPECIFIC_TAG * @see org.xbib.asn1.BEREncoding#PRIVATE_TAG */ - BERPrimitive(int asn1Class, int tag, int[] contents) - throws ASN1Exception { + public BERPrimitive(int asn1Class, int tag, int[] contents) throws ASN1Exception { init(asn1Class, false, tag, contents.length); contentsOctets = contents; } @@ -49,24 +45,10 @@ public class BERPrimitive extends BEREncoding { * This method allows the content octets to be examined. * Once again, only the ASN.1 standard objects should be using this. */ - int[] peek() { + public int[] getContentOctets() { return contentsOctets; } - /** - * This method outputs the encoded octets to the destination OutputStream. - * Note: the output is not flushed, so you must explicitly - * flush the output stream after calling this method to ensure that - * the data has been written out. - * - * @param dest - OutputStream to write encoding to. - */ - @Override - public void output(OutputStream dest) throws IOException { - outputHead(dest); - outputBytes(contentsOctets, dest); - } - /** * Returns a new String object representing this BER encoded * ASN.1 object's value. diff --git a/asn1/src/main/java/org/xbib/asn1/io/BERReader.java b/asn1/src/main/java/org/xbib/asn1/io/BERReader.java new file mode 100644 index 0000000..8776e0e --- /dev/null +++ b/asn1/src/main/java/org/xbib/asn1/io/BERReader.java @@ -0,0 +1,12 @@ +package org.xbib.asn1.io; + +import org.xbib.asn1.BEREncoding; + +import java.io.IOException; + +public interface BERReader extends AutoCloseable { + + BEREncoding read() throws IOException; + + void close() throws IOException; +} diff --git a/asn1/src/main/java/org/xbib/asn1/io/BERWriter.java b/asn1/src/main/java/org/xbib/asn1/io/BERWriter.java new file mode 100644 index 0000000..3d6f91c --- /dev/null +++ b/asn1/src/main/java/org/xbib/asn1/io/BERWriter.java @@ -0,0 +1,12 @@ +package org.xbib.asn1.io; + +import org.xbib.asn1.BEREncoding; + +import java.io.IOException; + +public interface BERWriter extends AutoCloseable { + + void write(BEREncoding ber) throws IOException; + + void close() throws IOException; +} diff --git a/asn1/src/main/java/org/xbib/asn1/io/InputStreamBERReader.java b/asn1/src/main/java/org/xbib/asn1/io/InputStreamBERReader.java new file mode 100644 index 0000000..926acb1 --- /dev/null +++ b/asn1/src/main/java/org/xbib/asn1/io/InputStreamBERReader.java @@ -0,0 +1,166 @@ +package org.xbib.asn1.io; + +import org.xbib.asn1.ASN1EncodingException; +import org.xbib.asn1.ASN1Exception; +import org.xbib.asn1.BERConstructed; +import org.xbib.asn1.BEREncoding; +import org.xbib.asn1.BERPrimitive; + +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; + +public class InputStreamBERReader implements BERReader { + + private static final String ERROR = "Unexpected end in BER encoding"; + + private static final int MAX_BER_SIZE = 65536 * 4; + + private final InputStream inputStream; + + public InputStreamBERReader(InputStream inputStream) { + this.inputStream = inputStream; + } + + /** + * The public wrapping for doInput() method. + * + * @return returns the next complete BEREncoding object read + * in from the input stream. Returns null if the + * end has been reached. + * @throws ASN1Exception If data does not represent a BER encoding + * @throws IOException On input I/O error + */ + @Override + public BEREncoding read() throws IOException { + int[] numBytesRead = new int[1]; + numBytesRead[0] = 0; + return doInput(numBytesRead); + } + + @Override + public void close() throws IOException { + inputStream.close(); + } + + /** + * Constructs a complete BER encoding object from octets read in from + * an InputStream. + * This routine handles all forms of encoding, including the + * indefite-length method. The length is always known with this + * class. With indefinite-length encodings, + * the end-of-contents octets are not included in the returned + * object (i.e. the returned the raw BER is converted to an object + * which is in the definite-length form). + * + * @param numBytesRead a counter for all read bytes. + * @return the next complete BEREncoding object read + * in from the input stream. Returns null if the + * end has been reached. + * @throws IOException If data does not represent a BER encoding or input I/O error + */ + private BEREncoding doInput(int[] numBytesRead) throws IOException { + int octet = inputStream.read(); + if (octet < 0) { + return null; + } + numBytesRead[0]++; + int tagType = octet & 0xC0; + boolean isCons = false; + if ((octet & 0x20) != 0) { + isCons = true; + } + int tag = octet & 0x1F; + if (tag == 0x1F) { + tag = 0; + do { + octet = inputStream.read(); + if (octet < 0) { + throw new ASN1EncodingException(ERROR); + } + numBytesRead[0]++; + tag <<= 7; + tag |= (octet & 0x7F); + } while ((octet & 0x80) != 0); + } + int length; + octet = inputStream.read(); + if (octet < 0) { + throw new ASN1EncodingException(ERROR); + } + numBytesRead[0]++; + if ((octet & 0x80) != 0) { + if ((octet & 0x7f) == 0) { + length = -1; + if (!isCons) { + throw new ASN1EncodingException("BER encoding corrupted primitive"); + } + } else { + if (4 < (octet & 0x7f)) { + throw new ASN1EncodingException("BER encoding too long"); + } + length = 0; + for (int numBytes = octet & 0x7f; 0 < numBytes; numBytes--) { + octet = inputStream.read(); + if (octet < 0) { + throw new ASN1EncodingException(ERROR); + } + numBytesRead[0]++; + length <<= 8; + length |= (octet & 0xff); + } + if (length < 0 || MAX_BER_SIZE < length) { + throw new ASN1EncodingException("BER encoding too long"); + } + } + } else { + length = octet & 0x7F; + } + if (!isCons) { + int[] contents = new int[length]; + for (int x = 0; x < length; x++) { + octet = inputStream.read(); + if (octet < 0) { + throw new ASN1EncodingException(ERROR); + } + numBytesRead[0]++; + contents[x] = octet; + } + return new BERPrimitive(tagType, tag, contents); + } else { + List chunks = new ArrayList<>(); + int totalRead = 0; + if (0 <= length) { + while (totalRead < length) { + int currentRead = numBytesRead[0]; + BEREncoding chunk = doInput( numBytesRead); + if (chunk == null) { + throw new ASN1EncodingException(ERROR); + } + chunks.add(chunk); + totalRead += numBytesRead[0] - currentRead; + } + } else { + while (true) { + BEREncoding chunk = doInput(numBytesRead); + if (chunk == null) { + throw new ASN1EncodingException(ERROR); + } + if (chunk.getITag() == 0 && chunk.getITagType() == BEREncoding.UNIVERSAL_TAG && + chunk.getITotalLength() == 2) { + break; + } else { + chunks.add(chunk); + } + } + } + int numElements = chunks.size(); + BEREncoding[] parts = new BEREncoding[numElements]; + for (int x = 0; x < numElements; x++) { + parts[x] = chunks.get(x); + } + return new BERConstructed(tagType, tag, parts); + } + } +} diff --git a/asn1/src/main/java/org/xbib/asn1/io/OutputStreamBERWriter.java b/asn1/src/main/java/org/xbib/asn1/io/OutputStreamBERWriter.java new file mode 100644 index 0000000..8621a22 --- /dev/null +++ b/asn1/src/main/java/org/xbib/asn1/io/OutputStreamBERWriter.java @@ -0,0 +1,96 @@ +package org.xbib.asn1.io; + +import org.xbib.asn1.BERConstructed; +import org.xbib.asn1.BEREncoding; +import org.xbib.asn1.BERPrimitive; + +import java.io.IOException; +import java.io.OutputStream; + +public class OutputStreamBERWriter implements BERWriter { + + private final OutputStream outputStream; + + private final boolean autoflush; + + public OutputStreamBERWriter(OutputStream outputStream) { + this(outputStream, true); + } + + public OutputStreamBERWriter(OutputStream outputStream, boolean autoflush) { + this.outputStream = outputStream; + this.autoflush = autoflush; + } + + /** + * Outputs the BER object to an OutputStream. This method should work + * with any OutputStream, whether it is from a socket, file, etc. + * Note: the output is not flushed, so you must explicitly + * flush the output stream after calling this method to ensure that + * the data has been written out. + * + * @throws IOException On output I/O error + */ + @Override + public void write(BEREncoding ber) throws IOException { + if (ber instanceof BERPrimitive) { + writeBERPrimitive((BERPrimitive) ber); + } else if (ber instanceof BERConstructed) { + writeBERConstructed((BERConstructed) ber); + } + if (autoflush) { + outputStream.flush(); + } + } + + @Override + public void close() throws IOException { + outputStream.close(); + } + + /** + * This method outputs the encoded octets for this object + * to the output stream. + * Note: the output is not flushed, so you must explicitly + * flush the output stream after calling this method to ensure that + * the data has been written out. + */ + private void writeBERConstructed(BERConstructed ber) throws IOException { + outputHead(ber); + for (BEREncoding contentElement : ber.getContentElements()) { + write(contentElement); + } + } + + /** + * This method outputs the encoded octets to the destination OutputStream. + * Note: the output is not flushed, so you must explicitly + * flush the output stream after calling this method to ensure that + * the data has been written out. + */ + private void writeBERPrimitive(BERPrimitive ber) throws IOException { + outputHead(ber); + outputBytes(ber.getContentOctets()); + } + + /* + * This is a protected method used to output the encoded identifier + * and length octets. It is used by the superclasses + * to implement the "output" method. + */ + private void outputHead(BEREncoding ber) throws IOException { + outputBytes(ber.getIdentifierEncoding()); + outputBytes(ber.getLengthEncoding()); + } + + /* + * This is a protected routine used for outputting an array of + * integers, interpreted as bytes, to an OutputStream. It is used + * by the superclasses to implement the "output" method. + */ + private void outputBytes(int[] data) throws IOException { + for (int aData : data) { + outputStream.write(aData); + } + } +} diff --git a/build.gradle b/build.gradle index 8ddbf05..58dec43 100644 --- a/build.gradle +++ b/build.gradle @@ -39,12 +39,18 @@ subprojects { wagon "org.apache.maven.wagon:wagon-ssh:${project.property('wagon.version')}" } - sourceCompatibility = JavaVersion.VERSION_1_8 - targetCompatibility = JavaVersion.VERSION_1_8 + compileJava { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 + } + + compileTestJava { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 + } - [compileJava, compileTestJava]*.options*.encoding = 'UTF-8' tasks.withType(JavaCompile) { - options.compilerArgs << "-Xlint:all,-fallthrough" << "-profile" << "compact1" + options.compilerArgs << "-Xlint:all,-fallthrough" } clean { diff --git a/gradle.properties b/gradle.properties index bf3b47f..ed6e0d5 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,7 +1,9 @@ group = org.xbib name = z3950 -version = 1.1.0 +version = 1.2.0 xbib-cql.version = 1.2.0 +netty.version = 4.1.29.Final + junit.version = 4.12 wagon.version = 3.0.0 \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index c44b679acd3f794ddbb3aa5e919244914911014a..0d4a9516871afd710a9d84d89e31ba77745607bd 100644 GIT binary patch delta 7645 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 databases; - private final Socket socket; + private final Integer preferredMessageSize; - private final BufferedInputStream src; + private final InitListener initListener; - private final BufferedOutputStream dest; + private final Lock lock; - public ZClient(String host, int port, String user, String pass, long timeout, + private Socket socket; + + private InputStreamBERReader berReader; + + private OutputStreamBERWriter berWriter; + + private ZClient(String host, int port, String user, String pass, long timeout, String preferredRecordSyntax, String resultSetName, String elementSetName, @@ -75,7 +85,7 @@ public class ZClient implements AutoCloseable { String type, List databases, Integer preferredMessageSize, - InitListener initListener) throws IOException { + InitListener initListener) { this.host = host; this.port = port; this.user = user; @@ -88,135 +98,82 @@ public class ZClient implements AutoCloseable { this.format = format; this.type = type; this.databases = databases; - Socket socket = new Socket(); - socket.connect(new InetSocketAddress(host, port), (int) timeout); - socket.setSoTimeout((int) timeout * 1000); - this.socket = socket; - this.src = new BufferedInputStream(socket.getInputStream()); - this.dest = new BufferedOutputStream(socket.getOutputStream()); - // always send init operation after socket init - InitOperation init = new InitOperation(); - if (init.execute(this, preferredMessageSize, initListener)) { - throw new IOException("could not initiatie connection"); - } - } - - public boolean isConnected() { - return socket != null && socket.isConnected(); + this.preferredMessageSize = preferredMessageSize; + this.initListener = initListener; + this.lock = new ReentrantLock(); } @Override public void close() { if (isConnected()) { try { - sendClose(0); - } catch (IOException e) { - logger.log(Level.WARNING, "while attempting to send close for close connection: " + e.getMessage(), e); - } - try { - if (src != null) { - src.close(); + lock.lock(); + try { + sendClose(0); + } catch (IOException e) { + logger.log(Level.WARNING, "while attempting to send close for close connection: " + e.getMessage(), e); } - } catch (IOException e) { - logger.log(Level.WARNING, "error attempting to close src: " + e.getMessage(), e); - } - try { - if (dest != null) { - dest.close(); + try { + berReader.close(); + } catch (IOException e) { + logger.log(Level.WARNING, "error attempting to close src: " + e.getMessage(), e); } - } catch (IOException e) { - logger.log(Level.WARNING, "error attempting to close dest: " + e.getMessage(), e); - } - try { - if (socket != null) { - socket.close(); + try { + berWriter.close(); + } catch (IOException e) { + logger.log(Level.WARNING, "error attempting to close dest: " + e.getMessage(), e); } - } catch (IOException e) { - logger.log(Level.WARNING, "error attempting to close socket: " + e.getMessage(), e); + try { + if (socket != null) { + socket.close(); + } + } catch (IOException e) { + logger.log(Level.WARNING, "error attempting to close socket: " + e.getMessage(), e); + } + } finally { + lock.unlock(); } } } - /** - * Send a close request to the server. - * - * @param reason reason Reason codes are: - * 0=finished 1=shutdown 2=system problem 3=cost limits - * 4=resources 5=security violation 6=protocol error 7=lack of activity - * 8=peer abort 9=unspecified - * @throws IOException if close fails - */ - public void sendClose(int reason) throws IOException { - PDU pdu = new PDU(); - pdu.c_close = new Close(); - pdu.c_close.sCloseReason = new CloseReason(); - pdu.c_close.sCloseReason.value = new ASN1Integer(reason); - pdu.c_close.sReferenceId = null; - writePDU(pdu); - // do not wait, it may hang - //waitClosePDU(); - } - - public void writePDU(PDU pdu) throws IOException { - if (dest == null) { - throw new IOException("no output stream"); - } - try { - pdu.berEncode().output(dest); - dest.flush(); - } catch (ASN1Exception ex) { - throw new IOException(ex); - } - } - - public PDU readPDU() throws IOException { - if (src == null) { - throw new IOException("no input"); - } - try { - BEREncoding ber = BEREncoding.input(src); - if (ber == null) { - throw new IOException("read PDU error"); - } - return new PDU(ber, true); - } catch (ASN1Exception ex) { - throw new IOException(ex); - } catch (NullPointerException ex) { - throw new IOException("connection read PDU error", ex); - } - } - public int executeCQL(String query, int offset, int length, ResponseListener responseListener, RecordListener recordListener) throws IOException { if (query == null) { throw new IllegalArgumentException("no query"); } - SearchOperation search = new SearchOperation(); - boolean success = search.execute(this, createRPNQueryFromCQL(query)); - if (!success) { - logger.log(Level.WARNING, MessageFormat.format("search was not a success [{0}]", query)); - } else { - if (responseListener == null) { - responseListener = (status, total, returned, elapsedMillis) -> { - logger.log(Level.INFO, MessageFormat.format("[{0}ms] [{1}] [{2}] [{3}]", - elapsedMillis, total, returned, query)); - }; - } - if (search.getCount() > 0) { - PresentOperation present = new PresentOperation(); - if (offset < 1) { - // Z39.50 present bails out when offset = 0 - offset = 1; + ensureConnected(); + try { + lock.lock(); + SearchOperation searchOperation = new SearchOperation(berReader, berWriter, resultSetName, databases, host); + boolean success = searchOperation.execute(createRPNQueryFromCQL(query)); + if (!success) { + logger.log(Level.WARNING, MessageFormat.format("search was not a success [{0}]", query)); + } else { + if (responseListener == null) { + responseListener = (status, total, returned, elapsedMillis) -> { + logger.log(Level.INFO, MessageFormat.format("[{0}ms] [{1}] [{2}] [{3}]", + elapsedMillis, total, returned, query)); + }; } - if (length > search.getCount()) { - // avoid condition 13 "Present request out-of-range" - length = search.getCount(); + if (searchOperation.getCount() > 0) { + PresentOperation present = new PresentOperation(berReader, berWriter, + resultSetName, elementSetName, preferredRecordSyntax); + if (offset < 1) { + // Z39.50 present bails out when offset = 0 + offset = 1; + } + if (length > searchOperation.getCount()) { + // avoid condition 13 "Present request out-of-range" + length = searchOperation.getCount(); + } + present.execute(offset, length, searchOperation.getCount(), responseListener, recordListener); } - present.execute(this, offset, length, search.getCount(), responseListener, recordListener); } + return searchOperation.getCount(); + } finally { + lock.unlock(); } - return search.getCount(); } public int executePQF(String query, int offset, int length, @@ -225,32 +182,39 @@ public class ZClient implements AutoCloseable { if (query == null) { throw new IllegalArgumentException("no query"); } - SearchOperation search = new SearchOperation(); - search.execute(this, createRPNQueryFromPQF(query)); - if (!search.isSuccess()) { - logger.log(Level.WARNING, MessageFormat.format("search was not a success [{0}]", query)); - } else { - if (responseListener == null) { - responseListener = (status, total, returned, elapsedMillis) -> { - logger.log(Level.INFO, MessageFormat.format("[{0}ms] [{1}] [{2}] [{3}]", - elapsedMillis, total, returned, query)); - }; - } - if (search.getCount() > 0) { - logger.log(Level.INFO, "search returned " + search.getCount()); - PresentOperation present = new PresentOperation(); - if (offset < 1) { - // Z39.50 bails out when offset = 0 - offset = 1; + ensureConnected(); + try { + lock.lock(); + SearchOperation search = new SearchOperation(berReader, berWriter, resultSetName, databases, host); + search.execute(createRPNQueryFromPQF(query)); + if (!search.isSuccess()) { + logger.log(Level.WARNING, MessageFormat.format("search was not a success [{0}]", query)); + } else { + if (responseListener == null) { + responseListener = (status, total, returned, elapsedMillis) -> { + logger.log(Level.INFO, MessageFormat.format("[{0}ms] [{1}] [{2}] [{3}]", + elapsedMillis, total, returned, query)); + }; } - if (length > search.getCount()) { - // avoid condition 13 "Present request out-of-range" - length = search.getCount(); + if (search.getCount() > 0) { + logger.log(Level.INFO, "search returned " + search.getCount()); + PresentOperation present = new PresentOperation(berReader, berWriter, + resultSetName, elementSetName, preferredRecordSyntax); + if (offset < 1) { + // Z39.50 bails out when offset = 0 + offset = 1; + } + if (length > search.getCount()) { + // avoid condition 13 "Present request out-of-range" + length = search.getCount(); + } + present.execute(offset, length, search.getCount(), responseListener, recordListener); } - present.execute(this, offset, length, search.getCount(), responseListener, recordListener); } + return search.getCount(); + } finally { + lock.unlock(); } - return search.getCount(); } public String getHost() { @@ -301,7 +265,7 @@ public class ZClient implements AutoCloseable { return databases; } - public RPNQuery createRPNQueryFromCQL(String query) throws IOException { + private RPNQuery createRPNQueryFromCQL(String query) { CQLRPNGenerator generator = new CQLRPNGenerator(); CQLParser parser = new CQLParser(query); parser.parse(); @@ -310,7 +274,7 @@ public class ZClient implements AutoCloseable { } - public RPNQuery createRPNQueryFromPQF(String query) throws IOException { + private RPNQuery createRPNQueryFromPQF(String query) { PQFRPNGenerator generator = new PQFRPNGenerator(); PQFParser parser = new PQFParser(new StringReader(query)); parser.parse(); @@ -318,6 +282,69 @@ public class ZClient implements AutoCloseable { return generator.getResult(); } + /** + * Send a close request to the server. + * + * @param reason reason Reason codes are: + * 0=finished 1=shutdown 2=system problem 3=cost limits + * 4=resources 5=security violation 6=protocol error 7=lack of activity + * 8=peer abort 9=unspecified + * @throws IOException if close fails + */ + private void sendClose(int reason) throws IOException { + if (!isConnected()) { + return; + } + try { + lock.lock(); + PDU pdu = new PDU(); + pdu.c_close = new Close(); + pdu.c_close.sCloseReason = new CloseReason(); + pdu.c_close.sCloseReason.value = new ASN1Integer(reason); + pdu.c_close.sReferenceId = null; + try { + berWriter.write(pdu.berEncode()); + } catch (ASN1Exception ex) { + throw new IOException(ex); + } + // do not wait, it may hang + //waitClosePDU(); + } finally { + lock.unlock(); + } + } + + + private void connect() throws IOException { + try { + lock.lock(); + Socket socket = new Socket(); + socket.connect(new InetSocketAddress(host, port), (int) timeout); + socket.setSoTimeout((int) timeout * 1000); + this.socket = socket; + InputStream src = new BufferedInputStream(socket.getInputStream()); + OutputStream dest = new BufferedOutputStream(socket.getOutputStream()); + this.berReader = new InputStreamBERReader(src); + this.berWriter = new OutputStreamBERWriter(dest); + InitOperation initOperation = new InitOperation(berReader, berWriter, user, pass); + if (initOperation.execute(preferredMessageSize, initListener)) { + throw new IOException("could not initiate connection"); + } + } finally { + lock.unlock(); + } + } + + private boolean isConnected() { + return socket != null && socket.isConnected(); + } + + private void ensureConnected() throws IOException { + if (!isConnected()) { + connect(); + } + } + public static Builder builder() { return new Builder(); } @@ -432,20 +459,16 @@ public class ZClient implements AutoCloseable { } public ZClient build() { - try { - return new ZClient(host, port, user, pass, timeout, - preferredRecordSyntax, - resultSetName, - elementSetName, - encoding, - format, - type, - databases, - preferredMessageSize, - initListener); - } catch (IOException e) { - throw new UncheckedIOException(e); - } + return new ZClient(host, port, user, pass, timeout, + preferredRecordSyntax, + resultSetName, + elementSetName, + encoding, + format, + type, + databases, + preferredMessageSize, + initListener); } } } diff --git a/z3950/src/main/java/org/xbib/io/iso23950/netty/Client.java b/z3950/src/main/java/org/xbib/io/iso23950/netty/Client.java new file mode 100644 index 0000000..d99010b --- /dev/null +++ b/z3950/src/main/java/org/xbib/io/iso23950/netty/Client.java @@ -0,0 +1,69 @@ +package org.xbib.io.iso23950.netty; + +import io.netty.bootstrap.Bootstrap; +import io.netty.buffer.Unpooled; +import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInitializer; +import io.netty.channel.EventLoopGroup; +import io.netty.channel.SimpleChannelInboundHandler; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.SocketChannel; +import io.netty.channel.socket.nio.NioSocketChannel; +import io.netty.util.CharsetUtil; +import org.xbib.io.iso23950.v3.PDU; + +import java.io.IOException; +import java.net.InetSocketAddress; + +public class Client { + + private final EventLoopGroup group; + private final Bootstrap clientBootstrap; + + public Client() { + this.group = new NioEventLoopGroup(); + clientBootstrap = new Bootstrap(); + clientBootstrap.group(group); + clientBootstrap.channel(NioSocketChannel.class); + clientBootstrap.remoteAddress(new InetSocketAddress("localhost", 9999)); + clientBootstrap.handler(new ChannelInitializer() { + protected void initChannel(SocketChannel socketChannel) throws Exception { + socketChannel.pipeline().addLast(new Handler()); + } + }); + } + + public void connect() throws InterruptedException { + ChannelFuture channelFuture = clientBootstrap.connect().sync(); + channelFuture.channel().closeFuture().sync(); + + } + + public void writePDU(PDU pdu) throws IOException { + + } + + public void shutdown() throws InterruptedException { + group.shutdownGracefully().sync(); + } + + class Handler extends SimpleChannelInboundHandler { + + @Override + public void channelActive(ChannelHandlerContext channelHandlerContext){ + channelHandlerContext.writeAndFlush(Unpooled.copiedBuffer("Netty Rocks!", CharsetUtil.UTF_8)); + } + + @Override + public void exceptionCaught(ChannelHandlerContext channelHandlerContext, Throwable cause){ + cause.printStackTrace(); + channelHandlerContext.close(); + } + + @Override + protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception { + + } + } +} diff --git a/z3950/src/main/java/org/xbib/io/iso23950/operations/AbstractOperation.java b/z3950/src/main/java/org/xbib/io/iso23950/operations/AbstractOperation.java new file mode 100644 index 0000000..4e68918 --- /dev/null +++ b/z3950/src/main/java/org/xbib/io/iso23950/operations/AbstractOperation.java @@ -0,0 +1,44 @@ +package org.xbib.io.iso23950.operations; + +import org.xbib.asn1.ASN1Exception; +import org.xbib.asn1.BEREncoding; +import org.xbib.asn1.io.BERReader; +import org.xbib.asn1.io.BERWriter; +import org.xbib.io.iso23950.v3.PDU; + +import java.io.IOException; + +public class AbstractOperation { + + protected final BERReader reader; + + protected final BERWriter writer; + + AbstractOperation(BERReader reader, BERWriter writer) { + this.reader = reader; + this.writer = writer; + } + + protected void writePDU(PDU pdu) throws IOException { + try { + writer.write(pdu.berEncode()); + } catch (ASN1Exception ex) { + throw new IOException(ex); + } + } + + protected PDU readPDU() throws IOException { + try { + BEREncoding ber = reader.read(); + if (ber == null) { + throw new IOException("read PDU error"); + } + return new PDU(ber, true); + } catch (ASN1Exception ex) { + throw new IOException(ex); + } catch (NullPointerException ex) { + throw new IOException("connection read PDU error", ex); + } + } + +} diff --git a/z3950/src/main/java/org/xbib/io/iso23950/operations/InitOperation.java b/z3950/src/main/java/org/xbib/io/iso23950/operations/InitOperation.java index 2ac2b1b..e565863 100644 --- a/z3950/src/main/java/org/xbib/io/iso23950/operations/InitOperation.java +++ b/z3950/src/main/java/org/xbib/io/iso23950/operations/InitOperation.java @@ -3,8 +3,9 @@ package org.xbib.io.iso23950.operations; import org.xbib.asn1.ASN1BitString; import org.xbib.asn1.ASN1GeneralString; import org.xbib.asn1.ASN1Integer; +import org.xbib.asn1.io.BERReader; +import org.xbib.asn1.io.BERWriter; import org.xbib.io.iso23950.InitListener; -import org.xbib.io.iso23950.ZClient; import org.xbib.io.iso23950.v3.IdAuthentication; import org.xbib.io.iso23950.v3.IdAuthenticationIdPass; import org.xbib.io.iso23950.v3.InitializeRequest; @@ -19,9 +20,19 @@ import java.io.IOException; /** * A Z39.50 Init operation. */ -public class InitOperation { +public class InitOperation extends AbstractOperation { - public boolean execute(ZClient client, Integer preferredMessageSize, + private final String user; + + private final String pass; + + public InitOperation(BERReader reader, BERWriter writer, String user, String pass) { + super(reader, writer); + this.user = user; + this.pass = pass; + } + + public boolean execute(Integer preferredMessageSize, InitListener initListener) throws IOException { InitializeRequest init = new InitializeRequest(); boolean[] version = new boolean[3]; @@ -56,14 +67,14 @@ public class InitOperation { init.s_implementationName.value = new ASN1GeneralString("Java ZClient"); init.s_implementationVersion = new InternationalString(); init.s_implementationVersion.value = new ASN1GeneralString("1.00"); - if (client.getUser() != null) { + if (user != null) { init.s_idAuthentication = new IdAuthentication(); init.s_idAuthentication.c_idPass = new IdAuthenticationIdPass(); init.s_idAuthentication.c_idPass.s_userId = new InternationalString(); - init.s_idAuthentication.c_idPass.s_userId.value = new ASN1GeneralString(client.getUser()); - if (client.getPass() != null) { + init.s_idAuthentication.c_idPass.s_userId.value = new ASN1GeneralString(user); + if (pass != null) { init.s_idAuthentication.c_idPass.s_password = new InternationalString(); - init.s_idAuthentication.c_idPass.s_password.value = new ASN1GeneralString(client.getPass()); + init.s_idAuthentication.c_idPass.s_password.value = new ASN1GeneralString(pass); } /*if (group != null) { init.s_idAuthentication.c_idPass.s_groupId = new InternationalString(); @@ -72,8 +83,8 @@ public class InitOperation { } PDU pduOut = new PDU(); pduOut.c_initRequest = init; - client.writePDU(pduOut); - PDU pduIn = client.readPDU(); + writePDU(pduOut); + PDU pduIn = readPDU(); InitializeResponse initResp = pduIn.c_initResponse; String targetInfo; if (initResp.s_implementationName != null) { diff --git a/z3950/src/main/java/org/xbib/io/iso23950/operations/PresentOperation.java b/z3950/src/main/java/org/xbib/io/iso23950/operations/PresentOperation.java index 4556aa9..f469191 100644 --- a/z3950/src/main/java/org/xbib/io/iso23950/operations/PresentOperation.java +++ b/z3950/src/main/java/org/xbib/io/iso23950/operations/PresentOperation.java @@ -5,11 +5,12 @@ import org.xbib.asn1.ASN1External; import org.xbib.asn1.ASN1GeneralString; import org.xbib.asn1.ASN1Integer; import org.xbib.asn1.ASN1ObjectIdentifier; +import org.xbib.asn1.io.BERReader; +import org.xbib.asn1.io.BERWriter; import org.xbib.io.iso23950.ErrorRecord; import org.xbib.io.iso23950.Record; import org.xbib.io.iso23950.RecordListener; import org.xbib.io.iso23950.ResponseListener; -import org.xbib.io.iso23950.ZClient; import org.xbib.io.iso23950.exceptions.MessageSizeTooSmallException; import org.xbib.io.iso23950.exceptions.NoRecordsReturnedException; import org.xbib.io.iso23950.exceptions.RequestTerminatedByAccessControlException; @@ -30,13 +31,26 @@ import java.io.IOException; /** * Present operation for Z39.50. */ -public class PresentOperation { +public class PresentOperation extends AbstractOperation { - public void execute(ZClient client, int offset, int length, int total, + private final String resultSetName; + + private final String elementSetName; + + private final String preferredRecordSyntax; + + public PresentOperation(BERReader reader, BERWriter writer, + String resultSetName, + String elementSetName, + String preferredRecordSyntax) { + super(reader, writer); + this.resultSetName = resultSetName; + this.elementSetName = elementSetName; + this.preferredRecordSyntax = preferredRecordSyntax; + } + + public void execute(int offset, int length, int total, ResponseListener responseListener, RecordListener recordListener) throws IOException { - String resultSetName = client.getResultSetName(); - String elementSetName = client.getElementSetName(); - String preferredRecordSyntax = client.getPreferredRecordSyntax(); PresentRequest pr = new PresentRequest(); pr.s_resultSetId = new ResultSetId(); pr.s_resultSetId.value = new InternationalString(); @@ -51,8 +65,8 @@ public class PresentOperation { PDU pdu = new PDU(); pdu.c_presentRequest = pr; long millis = System.currentTimeMillis(); - client.writePDU(pdu); - pdu = client.readPDU(); + writePDU(pdu); + pdu = readPDU(); PresentResponse response = pdu.c_presentResponse; int nReturned = response.s_numberOfRecordsReturned != null ? response.s_numberOfRecordsReturned.get() : 0; int status = response.s_presentStatus.value != null ? response.s_presentStatus.value.get() : 0; diff --git a/z3950/src/main/java/org/xbib/io/iso23950/operations/SearchOperation.java b/z3950/src/main/java/org/xbib/io/iso23950/operations/SearchOperation.java index bb89d6b..28fe693 100644 --- a/z3950/src/main/java/org/xbib/io/iso23950/operations/SearchOperation.java +++ b/z3950/src/main/java/org/xbib/io/iso23950/operations/SearchOperation.java @@ -6,7 +6,8 @@ import org.xbib.asn1.ASN1Exception; import org.xbib.asn1.ASN1GeneralString; import org.xbib.asn1.ASN1Integer; import org.xbib.asn1.ASN1Sequence; -import org.xbib.io.iso23950.ZClient; +import org.xbib.asn1.io.BERReader; +import org.xbib.asn1.io.BERWriter; import org.xbib.io.iso23950.v3.DatabaseName; import org.xbib.io.iso23950.v3.InternationalString; import org.xbib.io.iso23950.v3.OtherInformation1; @@ -26,15 +27,32 @@ import java.util.Map; /** * Base class for Z39.50 Search operation. */ -public class SearchOperation { +public class SearchOperation extends AbstractOperation { private int count = -1; private boolean status = false; - private Map results = new HashMap<>(); + private final Map results; - public boolean execute(ZClient client, RPNQuery rpn) throws IOException { + private final String resultSetName; + + private final List databases; + + private final String host; + + public SearchOperation(BERReader reader, BERWriter writer, + String resultSetName, + List databases, + String host) { + super(reader, writer); + this.resultSetName = resultSetName; + this.databases = databases; + this.host = host; + this.results = new HashMap<>(); + } + + public boolean execute(RPNQuery rpn) throws IOException { try { SearchRequest search = new SearchRequest(); search.s_query = new Query(); @@ -44,8 +62,7 @@ public class SearchOperation { search.s_mediumSetPresentNumber = new ASN1Integer(0); search.s_replaceIndicator = new ASN1Boolean(true); search.s_resultSetName = new InternationalString(); - search.s_resultSetName.value = new ASN1GeneralString(client.getResultSetName()); - List databases = client.getDatabases(); + search.s_resultSetName.value = new ASN1GeneralString(resultSetName); DatabaseName dbs[] = new DatabaseName[databases.size()]; for (int n = 0; n < databases.size(); n++) { dbs[n] = new DatabaseName(); @@ -55,8 +72,8 @@ public class SearchOperation { search.s_databaseNames = dbs; PDU pduRequest = new PDU(); pduRequest.c_searchRequest = search; - client.writePDU(pduRequest); - PDU pduResponse = client.readPDU(); + writePDU(pduRequest); + PDU pduResponse = readPDU(); SearchResponse response = pduResponse.c_searchResponse; count = response.s_resultCount.get(); ASN1Boolean b = response.s_searchStatus; @@ -71,7 +88,7 @@ public class SearchOperation { // } } - throw new IOException(client.getHost() + ": " + message); + throw new IOException(host + ": " + message); } PresentStatus presentStatus = response.s_presentStatus; if (presentStatus != null && presentStatus.value != null && presentStatus.value.get() == 5) { @@ -90,7 +107,7 @@ public class SearchOperation { if (!dbName.value.value.get().equalsIgnoreCase(databases.get(i))) { String message = "database name listed in additional search info " + "doesn't match database name in names set."; - throw new IOException(client.getHost() + ": " + message); + throw new IOException(host + ": " + message); } ASN1Integer res = (ASN1Integer) details[1]; results.put(target, res.get()); @@ -101,7 +118,7 @@ public class SearchOperation { } } } catch (SocketTimeoutException e) { - throw new IOException(client.getHost() + ": timeout", e); + throw new IOException(host + ": timeout", e); } return status; } diff --git a/z3950/src/test/java/org/xbib/io/iso23950/ZClientTest.java b/z3950/src/test/java/org/xbib/io/iso23950/ZClientTest.java index df55f7a..15f8e7c 100644 --- a/z3950/src/test/java/org/xbib/io/iso23950/ZClientTest.java +++ b/z3950/src/test/java/org/xbib/io/iso23950/ZClientTest.java @@ -56,7 +56,6 @@ public class ZClientTest { } } - private static ZClient newZClient(String name) throws IOException { return newZClient(getProperties(name)); } @@ -69,6 +68,7 @@ public class ZClientTest { } return properties; } + private static ZClient newZClient(Properties properties) { ZClient.Builder builder = ZClient.builder(); if (properties.containsKey("host")) {