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 c44b679..0d4a951 100644
Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index a92b9c8..531c640 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
-#Thu Feb 08 11:39:35 CET 2018
+#Fri Sep 07 18:47:56 CEST 2018
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-4.5.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-4.9-all.zip
diff --git a/z3950/build.gradle b/z3950/build.gradle
index d7620a0..90c80cf 100644
--- a/z3950/build.gradle
+++ b/z3950/build.gradle
@@ -6,4 +6,6 @@ plugins {
dependencies {
compile project(':asn1')
compile "org.xbib:cql-common:${project.property('xbib-cql.version')}"
+ compile "io.netty:netty-handler:${project.property('netty.version')}"
+ compile "io.netty:netty-transport:${project.property('netty.version')}"
}
diff --git a/z3950/src/main/java/org/xbib/io/iso23950/ZClient.java b/z3950/src/main/java/org/xbib/io/iso23950/ZClient.java
index 4f7302e..0479b5c 100644
--- a/z3950/src/main/java/org/xbib/io/iso23950/ZClient.java
+++ b/z3950/src/main/java/org/xbib/io/iso23950/ZClient.java
@@ -2,7 +2,8 @@ package org.xbib.io.iso23950;
import org.xbib.asn1.ASN1Exception;
import org.xbib.asn1.ASN1Integer;
-import org.xbib.asn1.BEREncoding;
+import org.xbib.asn1.io.InputStreamBERReader;
+import org.xbib.asn1.io.OutputStreamBERWriter;
import org.xbib.cql.CQLParser;
import org.xbib.io.iso23950.cql.CQLRPNGenerator;
import org.xbib.io.iso23950.operations.InitOperation;
@@ -18,14 +19,17 @@ import org.xbib.io.iso23950.v3.RPNQuery;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
import java.io.StringReader;
-import java.io.UncheckedIOException;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.text.MessageFormat;
import java.util.Collections;
import java.util.List;
import java.util.ResourceBundle;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
import java.util.logging.Level;
import java.util.logging.Logger;
@@ -60,13 +64,19 @@ public class ZClient implements AutoCloseable {
private final List 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")) {