refactor to BERReader/BERWriter interface, add locking
This commit is contained in:
parent
e9b3b4fa5a
commit
00412e7577
26 changed files with 687 additions and 406 deletions
|
@ -57,7 +57,7 @@ public final class ASN1BitString extends ASN1Any {
|
||||||
}
|
}
|
||||||
if (berEncoding instanceof BERPrimitive) {
|
if (berEncoding instanceof BERPrimitive) {
|
||||||
BERPrimitive ber = (BERPrimitive) berEncoding;
|
BERPrimitive ber = (BERPrimitive) berEncoding;
|
||||||
int[] encoding = ber.peek();
|
int[] encoding = ber.getContentOctets();
|
||||||
if (encoding.length < 1) {
|
if (encoding.length < 1) {
|
||||||
throw new ASN1EncodingException("ASN1 BIT STRING: invalid encoding, length = " + encoding.length);
|
throw new ASN1EncodingException("ASN1 BIT STRING: invalid encoding, length = " + encoding.length);
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,7 +55,7 @@ public final class ASN1Boolean extends ASN1Any {
|
||||||
}
|
}
|
||||||
if (berEncoding instanceof BERPrimitive) {
|
if (berEncoding instanceof BERPrimitive) {
|
||||||
BERPrimitive ber = (BERPrimitive) berEncoding;
|
BERPrimitive ber = (BERPrimitive) berEncoding;
|
||||||
int[] encoding = ber.peek();
|
int[] encoding = ber.getContentOctets();
|
||||||
if (encoding.length != 1) {
|
if (encoding.length != 1) {
|
||||||
throw new ASN1EncodingException("ASN.1 BOOLEAN: invalid encoding, length = " + encoding.length);
|
throw new ASN1EncodingException("ASN.1 BOOLEAN: invalid encoding, length = " + encoding.length);
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,7 +61,7 @@ public final class ASN1Enumerated extends ASN1Any {
|
||||||
throw new ASN1EncodingException("bad form, constructed");
|
throw new ASN1EncodingException("bad form, constructed");
|
||||||
}
|
}
|
||||||
BERPrimitive ber = (BERPrimitive) berEncoding;
|
BERPrimitive ber = (BERPrimitive) berEncoding;
|
||||||
int[] encoding = ber.peek();
|
int[] encoding = ber.getContentOctets();
|
||||||
if (encoding.length < 1) {
|
if (encoding.length < 1) {
|
||||||
throw new ASN1EncodingException("invalid encoding, length = " + encoding.length);
|
throw new ASN1EncodingException("invalid encoding, length = " + encoding.length);
|
||||||
}
|
}
|
||||||
|
|
|
@ -63,7 +63,7 @@ public final class ASN1Integer extends ASN1Any {
|
||||||
throw new ASN1EncodingException("bad form, constructed");
|
throw new ASN1EncodingException("bad form, constructed");
|
||||||
}
|
}
|
||||||
BERPrimitive ber = (BERPrimitive) berEncoding;
|
BERPrimitive ber = (BERPrimitive) berEncoding;
|
||||||
int[] encoding = ber.peek();
|
int[] encoding = ber.getContentOctets();
|
||||||
if (encoding.length < 1) {
|
if (encoding.length < 1) {
|
||||||
throw new ASN1EncodingException("invalid encoding, length = " + encoding.length);
|
throw new ASN1EncodingException("invalid encoding, length = " + encoding.length);
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,7 +58,7 @@ public final class ASN1ObjectIdentifier extends ASN1Any {
|
||||||
throw new ASN1EncodingException("bad form, constructed");
|
throw new ASN1EncodingException("bad form, constructed");
|
||||||
}
|
}
|
||||||
BERPrimitive ber = (BERPrimitive) berEncoding;
|
BERPrimitive ber = (BERPrimitive) berEncoding;
|
||||||
int[] encoding = ber.peek();
|
int[] encoding = ber.getContentOctets();
|
||||||
if (encoding.length < 2) {
|
if (encoding.length < 2) {
|
||||||
throw new ASN1EncodingException("invalid encoding, length = " +
|
throw new ASN1EncodingException("invalid encoding, length = " +
|
||||||
encoding.length);
|
encoding.length);
|
||||||
|
|
|
@ -75,7 +75,7 @@ public class ASN1OctetString extends ASN1Any {
|
||||||
}
|
}
|
||||||
if (berEncoding instanceof BERPrimitive) {
|
if (berEncoding instanceof BERPrimitive) {
|
||||||
BERPrimitive ber = (BERPrimitive) berEncoding;
|
BERPrimitive ber = (BERPrimitive) berEncoding;
|
||||||
int[] encoding = ber.peek();
|
int[] encoding = ber.getContentOctets();
|
||||||
StringBuilder buf = new StringBuilder(encoding.length);
|
StringBuilder buf = new StringBuilder(encoding.length);
|
||||||
for (int anEncoding : encoding) {
|
for (int anEncoding : encoding) {
|
||||||
buf.append((char) (anEncoding & 0x00ff));
|
buf.append((char) (anEncoding & 0x00ff));
|
||||||
|
|
|
@ -28,8 +28,7 @@ public class ASN1VideotexString extends ASN1OctetString {
|
||||||
* @param checkTag If true, it checks the tag. Use false if is implicitly tagged.
|
* @param checkTag If true, it checks the tag. Use false if is implicitly tagged.
|
||||||
* @throws ASN1Exception If the BER encoding is incorrect.
|
* @throws ASN1Exception If the BER encoding is incorrect.
|
||||||
*/
|
*/
|
||||||
public ASN1VideotexString(BEREncoding ber, boolean checkTag)
|
public ASN1VideotexString(BEREncoding ber, boolean checkTag) throws ASN1Exception {
|
||||||
throws ASN1Exception {
|
|
||||||
super(ber, false);
|
super(ber, false);
|
||||||
if (checkTag && (ber.tagGet() != VIDEOTEX_STRING_TAG || ber.tagTypeGet() != BEREncoding.UNIVERSAL_TAG)) {
|
if (checkTag && (ber.tagGet() != VIDEOTEX_STRING_TAG || ber.tagTypeGet() != BEREncoding.UNIVERSAL_TAG)) {
|
||||||
throw new ASN1EncodingException("bad BER: tag=" + ber.tagGet() +
|
throw new ASN1EncodingException("bad BER: tag=" + ber.tagGet() +
|
||||||
|
|
|
@ -1,8 +1,5 @@
|
||||||
package org.xbib.asn1;
|
package org.xbib.asn1;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.OutputStream;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* BERConstructed.
|
* BERConstructed.
|
||||||
* This class represents a BER encoded ASN.1 object which is
|
* This class represents a BER encoded ASN.1 object which is
|
||||||
|
@ -38,21 +35,8 @@ public class BERConstructed extends BEREncoding {
|
||||||
contentElements = elements;
|
contentElements = elements;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public BEREncoding[] getContentElements() {
|
||||||
* This method outputs the encoded octets for this object
|
return contentElements;
|
||||||
* to the output stream.
|
|
||||||
* Note: the output is not flushed, so you <strong>must</strong> 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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1,11 +1,5 @@
|
||||||
package org.xbib.asn1;
|
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 class represents a BER (Basic Encoding Rules) encoded ASN.1 object.
|
||||||
* This is an abstract base class from which there are two specific
|
* This is an abstract base class from which there are two specific
|
||||||
|
@ -24,8 +18,6 @@ import java.util.List;
|
||||||
*/
|
*/
|
||||||
public abstract class BEREncoding {
|
public abstract class BEREncoding {
|
||||||
|
|
||||||
private static final String ERROR = "Unexpected end in BER encoding";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constant for indicating UNIVERSAL tag type. The value matches
|
* Constant for indicating UNIVERSAL tag type. The value matches
|
||||||
* the BER bit encoding. Universal tags are for types defined in
|
* 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;
|
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 tag type of this BER encoded object. This value must be
|
||||||
* the same as that encoded in the identiferEncoding.
|
* the same as that encoded in the identiferEncoding.
|
||||||
|
@ -86,153 +77,25 @@ public abstract class BEREncoding {
|
||||||
*/
|
*/
|
||||||
private int[] lengthEncoding;
|
private int[] lengthEncoding;
|
||||||
|
|
||||||
/**
|
public int[] getIdentifierEncoding() {
|
||||||
* The public wrapping for doInput() method.
|
return identifierEncoding;
|
||||||
*
|
|
||||||
* @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[] getLengthEncoding() {
|
||||||
* Constructs a complete BER encoding object from octets read in from
|
return lengthEncoding;
|
||||||
* 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<BEREncoding> 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 getITag() {
|
||||||
* Outputs the BER object to an OutputStream. This method should work
|
return iTag;
|
||||||
* with any OutputStream, whether it is from a socket, file, etc.
|
}
|
||||||
* Note: the output is not flushed, so you <strong>must</strong> explicitly
|
|
||||||
* flush the output stream after calling this method to ensure that
|
public int getITagType() {
|
||||||
* the data has been written out.
|
return iTagType;
|
||||||
*
|
}
|
||||||
* @param dest - the OutputStream to write the encoding to.
|
|
||||||
* @throws IOException On output I/O error
|
public int getITotalLength() {
|
||||||
*/
|
return iTotalLength;
|
||||||
public abstract void output(OutputStream dest) throws IOException;
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the BER encoded object as an array of bytes. This routine
|
* 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;
|
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
|
* Internal protected method fills in the data array (starting from index
|
||||||
* position offset) with the encoding for the identifier and length.
|
* position offset) with the encoding for the identifier and length.
|
||||||
|
|
|
@ -1,8 +1,5 @@
|
||||||
package org.xbib.asn1;
|
package org.xbib.asn1;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.OutputStream;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class represents a primitive ASN.1 object encoded
|
* This class represents a primitive ASN.1 object encoded
|
||||||
* according to the Basic Encoding Rules.
|
* 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#CONTEXT_SPECIFIC_TAG
|
||||||
* @see org.xbib.asn1.BEREncoding#PRIVATE_TAG
|
* @see org.xbib.asn1.BEREncoding#PRIVATE_TAG
|
||||||
*/
|
*/
|
||||||
BERPrimitive(int asn1Class, int tag, int[] contents)
|
public BERPrimitive(int asn1Class, int tag, int[] contents) throws ASN1Exception {
|
||||||
throws ASN1Exception {
|
|
||||||
init(asn1Class, false, tag, contents.length);
|
init(asn1Class, false, tag, contents.length);
|
||||||
contentsOctets = contents;
|
contentsOctets = contents;
|
||||||
}
|
}
|
||||||
|
@ -49,24 +45,10 @@ public class BERPrimitive extends BEREncoding {
|
||||||
* This method allows the content octets to be examined.
|
* This method allows the content octets to be examined.
|
||||||
* Once again, only the ASN.1 standard objects should be using this.
|
* Once again, only the ASN.1 standard objects should be using this.
|
||||||
*/
|
*/
|
||||||
int[] peek() {
|
public int[] getContentOctets() {
|
||||||
return contentsOctets;
|
return contentsOctets;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* This method outputs the encoded octets to the destination OutputStream.
|
|
||||||
* Note: the output is not flushed, so you <strong>must</strong> 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
|
* Returns a new String object representing this BER encoded
|
||||||
* ASN.1 object's value.
|
* ASN.1 object's value.
|
||||||
|
|
12
asn1/src/main/java/org/xbib/asn1/io/BERReader.java
Normal file
12
asn1/src/main/java/org/xbib/asn1/io/BERReader.java
Normal file
|
@ -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;
|
||||||
|
}
|
12
asn1/src/main/java/org/xbib/asn1/io/BERWriter.java
Normal file
12
asn1/src/main/java/org/xbib/asn1/io/BERWriter.java
Normal file
|
@ -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;
|
||||||
|
}
|
166
asn1/src/main/java/org/xbib/asn1/io/InputStreamBERReader.java
Normal file
166
asn1/src/main/java/org/xbib/asn1/io/InputStreamBERReader.java
Normal file
|
@ -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<BEREncoding> 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 <strong>must</strong> 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 <strong>must</strong> 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 <strong>must</strong> 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
14
build.gradle
14
build.gradle
|
@ -39,12 +39,18 @@ subprojects {
|
||||||
wagon "org.apache.maven.wagon:wagon-ssh:${project.property('wagon.version')}"
|
wagon "org.apache.maven.wagon:wagon-ssh:${project.property('wagon.version')}"
|
||||||
}
|
}
|
||||||
|
|
||||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
compileJava {
|
||||||
targetCompatibility = JavaVersion.VERSION_1_8
|
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) {
|
tasks.withType(JavaCompile) {
|
||||||
options.compilerArgs << "-Xlint:all,-fallthrough" << "-profile" << "compact1"
|
options.compilerArgs << "-Xlint:all,-fallthrough"
|
||||||
}
|
}
|
||||||
|
|
||||||
clean {
|
clean {
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
group = org.xbib
|
group = org.xbib
|
||||||
name = z3950
|
name = z3950
|
||||||
version = 1.1.0
|
version = 1.2.0
|
||||||
|
|
||||||
xbib-cql.version = 1.2.0
|
xbib-cql.version = 1.2.0
|
||||||
|
netty.version = 4.1.29.Final
|
||||||
|
|
||||||
junit.version = 4.12
|
junit.version = 4.12
|
||||||
wagon.version = 3.0.0
|
wagon.version = 3.0.0
|
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
4
gradle/wrapper/gradle-wrapper.properties
vendored
4
gradle/wrapper/gradle-wrapper.properties
vendored
|
@ -1,6 +1,6 @@
|
||||||
#Thu Feb 08 11:39:35 CET 2018
|
#Fri Sep 07 18:47:56 CEST 2018
|
||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
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
|
||||||
|
|
|
@ -6,4 +6,6 @@ plugins {
|
||||||
dependencies {
|
dependencies {
|
||||||
compile project(':asn1')
|
compile project(':asn1')
|
||||||
compile "org.xbib:cql-common:${project.property('xbib-cql.version')}"
|
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')}"
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,8 @@ package org.xbib.io.iso23950;
|
||||||
|
|
||||||
import org.xbib.asn1.ASN1Exception;
|
import org.xbib.asn1.ASN1Exception;
|
||||||
import org.xbib.asn1.ASN1Integer;
|
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.cql.CQLParser;
|
||||||
import org.xbib.io.iso23950.cql.CQLRPNGenerator;
|
import org.xbib.io.iso23950.cql.CQLRPNGenerator;
|
||||||
import org.xbib.io.iso23950.operations.InitOperation;
|
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.BufferedInputStream;
|
||||||
import java.io.BufferedOutputStream;
|
import java.io.BufferedOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
import java.io.StringReader;
|
import java.io.StringReader;
|
||||||
import java.io.UncheckedIOException;
|
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import java.net.Socket;
|
import java.net.Socket;
|
||||||
import java.text.MessageFormat;
|
import java.text.MessageFormat;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.ResourceBundle;
|
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.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
@ -60,13 +64,19 @@ public class ZClient implements AutoCloseable {
|
||||||
|
|
||||||
private final List<String> databases;
|
private final List<String> 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 preferredRecordSyntax,
|
||||||
String resultSetName,
|
String resultSetName,
|
||||||
String elementSetName,
|
String elementSetName,
|
||||||
|
@ -75,7 +85,7 @@ public class ZClient implements AutoCloseable {
|
||||||
String type,
|
String type,
|
||||||
List<String> databases,
|
List<String> databases,
|
||||||
Integer preferredMessageSize,
|
Integer preferredMessageSize,
|
||||||
InitListener initListener) throws IOException {
|
InitListener initListener) {
|
||||||
this.host = host;
|
this.host = host;
|
||||||
this.port = port;
|
this.port = port;
|
||||||
this.user = user;
|
this.user = user;
|
||||||
|
@ -88,135 +98,82 @@ public class ZClient implements AutoCloseable {
|
||||||
this.format = format;
|
this.format = format;
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this.databases = databases;
|
this.databases = databases;
|
||||||
Socket socket = new Socket();
|
this.preferredMessageSize = preferredMessageSize;
|
||||||
socket.connect(new InetSocketAddress(host, port), (int) timeout);
|
this.initListener = initListener;
|
||||||
socket.setSoTimeout((int) timeout * 1000);
|
this.lock = new ReentrantLock();
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close() {
|
public void close() {
|
||||||
if (isConnected()) {
|
if (isConnected()) {
|
||||||
try {
|
try {
|
||||||
sendClose(0);
|
lock.lock();
|
||||||
} catch (IOException e) {
|
try {
|
||||||
logger.log(Level.WARNING, "while attempting to send close for close connection: " + e.getMessage(), e);
|
sendClose(0);
|
||||||
}
|
} catch (IOException e) {
|
||||||
try {
|
logger.log(Level.WARNING, "while attempting to send close for close connection: " + e.getMessage(), e);
|
||||||
if (src != null) {
|
|
||||||
src.close();
|
|
||||||
}
|
}
|
||||||
} catch (IOException e) {
|
try {
|
||||||
logger.log(Level.WARNING, "error attempting to close src: " + e.getMessage(), e);
|
berReader.close();
|
||||||
}
|
} catch (IOException e) {
|
||||||
try {
|
logger.log(Level.WARNING, "error attempting to close src: " + e.getMessage(), e);
|
||||||
if (dest != null) {
|
|
||||||
dest.close();
|
|
||||||
}
|
}
|
||||||
} catch (IOException e) {
|
try {
|
||||||
logger.log(Level.WARNING, "error attempting to close dest: " + e.getMessage(), e);
|
berWriter.close();
|
||||||
}
|
} catch (IOException e) {
|
||||||
try {
|
logger.log(Level.WARNING, "error attempting to close dest: " + e.getMessage(), e);
|
||||||
if (socket != null) {
|
|
||||||
socket.close();
|
|
||||||
}
|
}
|
||||||
} catch (IOException e) {
|
try {
|
||||||
logger.log(Level.WARNING, "error attempting to close socket: " + e.getMessage(), e);
|
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,
|
public int executeCQL(String query, int offset, int length,
|
||||||
ResponseListener responseListener,
|
ResponseListener responseListener,
|
||||||
RecordListener recordListener) throws IOException {
|
RecordListener recordListener) throws IOException {
|
||||||
if (query == null) {
|
if (query == null) {
|
||||||
throw new IllegalArgumentException("no query");
|
throw new IllegalArgumentException("no query");
|
||||||
}
|
}
|
||||||
SearchOperation search = new SearchOperation();
|
ensureConnected();
|
||||||
boolean success = search.execute(this, createRPNQueryFromCQL(query));
|
try {
|
||||||
if (!success) {
|
lock.lock();
|
||||||
logger.log(Level.WARNING, MessageFormat.format("search was not a success [{0}]", query));
|
SearchOperation searchOperation = new SearchOperation(berReader, berWriter, resultSetName, databases, host);
|
||||||
} else {
|
boolean success = searchOperation.execute(createRPNQueryFromCQL(query));
|
||||||
if (responseListener == null) {
|
if (!success) {
|
||||||
responseListener = (status, total, returned, elapsedMillis) -> {
|
logger.log(Level.WARNING, MessageFormat.format("search was not a success [{0}]", query));
|
||||||
logger.log(Level.INFO, MessageFormat.format("[{0}ms] [{1}] [{2}] [{3}]",
|
} else {
|
||||||
elapsedMillis, total, returned, query));
|
if (responseListener == null) {
|
||||||
};
|
responseListener = (status, total, returned, elapsedMillis) -> {
|
||||||
}
|
logger.log(Level.INFO, MessageFormat.format("[{0}ms] [{1}] [{2}] [{3}]",
|
||||||
if (search.getCount() > 0) {
|
elapsedMillis, total, returned, query));
|
||||||
PresentOperation present = new PresentOperation();
|
};
|
||||||
if (offset < 1) {
|
|
||||||
// Z39.50 present bails out when offset = 0
|
|
||||||
offset = 1;
|
|
||||||
}
|
}
|
||||||
if (length > search.getCount()) {
|
if (searchOperation.getCount() > 0) {
|
||||||
// avoid condition 13 "Present request out-of-range"
|
PresentOperation present = new PresentOperation(berReader, berWriter,
|
||||||
length = search.getCount();
|
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,
|
public int executePQF(String query, int offset, int length,
|
||||||
|
@ -225,32 +182,39 @@ public class ZClient implements AutoCloseable {
|
||||||
if (query == null) {
|
if (query == null) {
|
||||||
throw new IllegalArgumentException("no query");
|
throw new IllegalArgumentException("no query");
|
||||||
}
|
}
|
||||||
SearchOperation search = new SearchOperation();
|
ensureConnected();
|
||||||
search.execute(this, createRPNQueryFromPQF(query));
|
try {
|
||||||
if (!search.isSuccess()) {
|
lock.lock();
|
||||||
logger.log(Level.WARNING, MessageFormat.format("search was not a success [{0}]", query));
|
SearchOperation search = new SearchOperation(berReader, berWriter, resultSetName, databases, host);
|
||||||
} else {
|
search.execute(createRPNQueryFromPQF(query));
|
||||||
if (responseListener == null) {
|
if (!search.isSuccess()) {
|
||||||
responseListener = (status, total, returned, elapsedMillis) -> {
|
logger.log(Level.WARNING, MessageFormat.format("search was not a success [{0}]", query));
|
||||||
logger.log(Level.INFO, MessageFormat.format("[{0}ms] [{1}] [{2}] [{3}]",
|
} else {
|
||||||
elapsedMillis, total, returned, query));
|
if (responseListener == null) {
|
||||||
};
|
responseListener = (status, total, returned, elapsedMillis) -> {
|
||||||
}
|
logger.log(Level.INFO, MessageFormat.format("[{0}ms] [{1}] [{2}] [{3}]",
|
||||||
if (search.getCount() > 0) {
|
elapsedMillis, total, returned, query));
|
||||||
logger.log(Level.INFO, "search returned " + search.getCount());
|
};
|
||||||
PresentOperation present = new PresentOperation();
|
|
||||||
if (offset < 1) {
|
|
||||||
// Z39.50 bails out when offset = 0
|
|
||||||
offset = 1;
|
|
||||||
}
|
}
|
||||||
if (length > search.getCount()) {
|
if (search.getCount() > 0) {
|
||||||
// avoid condition 13 "Present request out-of-range"
|
logger.log(Level.INFO, "search returned " + search.getCount());
|
||||||
length = 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() {
|
public String getHost() {
|
||||||
|
@ -301,7 +265,7 @@ public class ZClient implements AutoCloseable {
|
||||||
return databases;
|
return databases;
|
||||||
}
|
}
|
||||||
|
|
||||||
public RPNQuery createRPNQueryFromCQL(String query) throws IOException {
|
private RPNQuery createRPNQueryFromCQL(String query) {
|
||||||
CQLRPNGenerator generator = new CQLRPNGenerator();
|
CQLRPNGenerator generator = new CQLRPNGenerator();
|
||||||
CQLParser parser = new CQLParser(query);
|
CQLParser parser = new CQLParser(query);
|
||||||
parser.parse();
|
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();
|
PQFRPNGenerator generator = new PQFRPNGenerator();
|
||||||
PQFParser parser = new PQFParser(new StringReader(query));
|
PQFParser parser = new PQFParser(new StringReader(query));
|
||||||
parser.parse();
|
parser.parse();
|
||||||
|
@ -318,6 +282,69 @@ public class ZClient implements AutoCloseable {
|
||||||
return generator.getResult();
|
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() {
|
public static Builder builder() {
|
||||||
return new Builder();
|
return new Builder();
|
||||||
}
|
}
|
||||||
|
@ -432,20 +459,16 @@ public class ZClient implements AutoCloseable {
|
||||||
}
|
}
|
||||||
|
|
||||||
public ZClient build() {
|
public ZClient build() {
|
||||||
try {
|
return new ZClient(host, port, user, pass, timeout,
|
||||||
return new ZClient(host, port, user, pass, timeout,
|
preferredRecordSyntax,
|
||||||
preferredRecordSyntax,
|
resultSetName,
|
||||||
resultSetName,
|
elementSetName,
|
||||||
elementSetName,
|
encoding,
|
||||||
encoding,
|
format,
|
||||||
format,
|
type,
|
||||||
type,
|
databases,
|
||||||
databases,
|
preferredMessageSize,
|
||||||
preferredMessageSize,
|
initListener);
|
||||||
initListener);
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new UncheckedIOException(e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
69
z3950/src/main/java/org/xbib/io/iso23950/netty/Client.java
Normal file
69
z3950/src/main/java/org/xbib/io/iso23950/netty/Client.java
Normal file
|
@ -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<SocketChannel>() {
|
||||||
|
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 {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -3,8 +3,9 @@ package org.xbib.io.iso23950.operations;
|
||||||
import org.xbib.asn1.ASN1BitString;
|
import org.xbib.asn1.ASN1BitString;
|
||||||
import org.xbib.asn1.ASN1GeneralString;
|
import org.xbib.asn1.ASN1GeneralString;
|
||||||
import org.xbib.asn1.ASN1Integer;
|
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.InitListener;
|
||||||
import org.xbib.io.iso23950.ZClient;
|
|
||||||
import org.xbib.io.iso23950.v3.IdAuthentication;
|
import org.xbib.io.iso23950.v3.IdAuthentication;
|
||||||
import org.xbib.io.iso23950.v3.IdAuthenticationIdPass;
|
import org.xbib.io.iso23950.v3.IdAuthenticationIdPass;
|
||||||
import org.xbib.io.iso23950.v3.InitializeRequest;
|
import org.xbib.io.iso23950.v3.InitializeRequest;
|
||||||
|
@ -19,9 +20,19 @@ import java.io.IOException;
|
||||||
/**
|
/**
|
||||||
* A Z39.50 Init operation.
|
* 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 {
|
InitListener initListener) throws IOException {
|
||||||
InitializeRequest init = new InitializeRequest();
|
InitializeRequest init = new InitializeRequest();
|
||||||
boolean[] version = new boolean[3];
|
boolean[] version = new boolean[3];
|
||||||
|
@ -56,14 +67,14 @@ public class InitOperation {
|
||||||
init.s_implementationName.value = new ASN1GeneralString("Java ZClient");
|
init.s_implementationName.value = new ASN1GeneralString("Java ZClient");
|
||||||
init.s_implementationVersion = new InternationalString();
|
init.s_implementationVersion = new InternationalString();
|
||||||
init.s_implementationVersion.value = new ASN1GeneralString("1.00");
|
init.s_implementationVersion.value = new ASN1GeneralString("1.00");
|
||||||
if (client.getUser() != null) {
|
if (user != null) {
|
||||||
init.s_idAuthentication = new IdAuthentication();
|
init.s_idAuthentication = new IdAuthentication();
|
||||||
init.s_idAuthentication.c_idPass = new IdAuthenticationIdPass();
|
init.s_idAuthentication.c_idPass = new IdAuthenticationIdPass();
|
||||||
init.s_idAuthentication.c_idPass.s_userId = new InternationalString();
|
init.s_idAuthentication.c_idPass.s_userId = new InternationalString();
|
||||||
init.s_idAuthentication.c_idPass.s_userId.value = new ASN1GeneralString(client.getUser());
|
init.s_idAuthentication.c_idPass.s_userId.value = new ASN1GeneralString(user);
|
||||||
if (client.getPass() != null) {
|
if (pass != null) {
|
||||||
init.s_idAuthentication.c_idPass.s_password = new InternationalString();
|
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) {
|
/*if (group != null) {
|
||||||
init.s_idAuthentication.c_idPass.s_groupId = new InternationalString();
|
init.s_idAuthentication.c_idPass.s_groupId = new InternationalString();
|
||||||
|
@ -72,8 +83,8 @@ public class InitOperation {
|
||||||
}
|
}
|
||||||
PDU pduOut = new PDU();
|
PDU pduOut = new PDU();
|
||||||
pduOut.c_initRequest = init;
|
pduOut.c_initRequest = init;
|
||||||
client.writePDU(pduOut);
|
writePDU(pduOut);
|
||||||
PDU pduIn = client.readPDU();
|
PDU pduIn = readPDU();
|
||||||
InitializeResponse initResp = pduIn.c_initResponse;
|
InitializeResponse initResp = pduIn.c_initResponse;
|
||||||
String targetInfo;
|
String targetInfo;
|
||||||
if (initResp.s_implementationName != null) {
|
if (initResp.s_implementationName != null) {
|
||||||
|
|
|
@ -5,11 +5,12 @@ import org.xbib.asn1.ASN1External;
|
||||||
import org.xbib.asn1.ASN1GeneralString;
|
import org.xbib.asn1.ASN1GeneralString;
|
||||||
import org.xbib.asn1.ASN1Integer;
|
import org.xbib.asn1.ASN1Integer;
|
||||||
import org.xbib.asn1.ASN1ObjectIdentifier;
|
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.ErrorRecord;
|
||||||
import org.xbib.io.iso23950.Record;
|
import org.xbib.io.iso23950.Record;
|
||||||
import org.xbib.io.iso23950.RecordListener;
|
import org.xbib.io.iso23950.RecordListener;
|
||||||
import org.xbib.io.iso23950.ResponseListener;
|
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.MessageSizeTooSmallException;
|
||||||
import org.xbib.io.iso23950.exceptions.NoRecordsReturnedException;
|
import org.xbib.io.iso23950.exceptions.NoRecordsReturnedException;
|
||||||
import org.xbib.io.iso23950.exceptions.RequestTerminatedByAccessControlException;
|
import org.xbib.io.iso23950.exceptions.RequestTerminatedByAccessControlException;
|
||||||
|
@ -30,13 +31,26 @@ import java.io.IOException;
|
||||||
/**
|
/**
|
||||||
* Present operation for Z39.50.
|
* 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 {
|
ResponseListener responseListener, RecordListener recordListener) throws IOException {
|
||||||
String resultSetName = client.getResultSetName();
|
|
||||||
String elementSetName = client.getElementSetName();
|
|
||||||
String preferredRecordSyntax = client.getPreferredRecordSyntax();
|
|
||||||
PresentRequest pr = new PresentRequest();
|
PresentRequest pr = new PresentRequest();
|
||||||
pr.s_resultSetId = new ResultSetId();
|
pr.s_resultSetId = new ResultSetId();
|
||||||
pr.s_resultSetId.value = new InternationalString();
|
pr.s_resultSetId.value = new InternationalString();
|
||||||
|
@ -51,8 +65,8 @@ public class PresentOperation {
|
||||||
PDU pdu = new PDU();
|
PDU pdu = new PDU();
|
||||||
pdu.c_presentRequest = pr;
|
pdu.c_presentRequest = pr;
|
||||||
long millis = System.currentTimeMillis();
|
long millis = System.currentTimeMillis();
|
||||||
client.writePDU(pdu);
|
writePDU(pdu);
|
||||||
pdu = client.readPDU();
|
pdu = readPDU();
|
||||||
PresentResponse response = pdu.c_presentResponse;
|
PresentResponse response = pdu.c_presentResponse;
|
||||||
int nReturned = response.s_numberOfRecordsReturned != null ? response.s_numberOfRecordsReturned.get() : 0;
|
int nReturned = response.s_numberOfRecordsReturned != null ? response.s_numberOfRecordsReturned.get() : 0;
|
||||||
int status = response.s_presentStatus.value != null ? response.s_presentStatus.value.get() : 0;
|
int status = response.s_presentStatus.value != null ? response.s_presentStatus.value.get() : 0;
|
||||||
|
|
|
@ -6,7 +6,8 @@ import org.xbib.asn1.ASN1Exception;
|
||||||
import org.xbib.asn1.ASN1GeneralString;
|
import org.xbib.asn1.ASN1GeneralString;
|
||||||
import org.xbib.asn1.ASN1Integer;
|
import org.xbib.asn1.ASN1Integer;
|
||||||
import org.xbib.asn1.ASN1Sequence;
|
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.DatabaseName;
|
||||||
import org.xbib.io.iso23950.v3.InternationalString;
|
import org.xbib.io.iso23950.v3.InternationalString;
|
||||||
import org.xbib.io.iso23950.v3.OtherInformation1;
|
import org.xbib.io.iso23950.v3.OtherInformation1;
|
||||||
|
@ -26,15 +27,32 @@ import java.util.Map;
|
||||||
/**
|
/**
|
||||||
* Base class for Z39.50 Search operation.
|
* Base class for Z39.50 Search operation.
|
||||||
*/
|
*/
|
||||||
public class SearchOperation {
|
public class SearchOperation extends AbstractOperation {
|
||||||
|
|
||||||
private int count = -1;
|
private int count = -1;
|
||||||
|
|
||||||
private boolean status = false;
|
private boolean status = false;
|
||||||
|
|
||||||
private Map<ASN1Any, Integer> results = new HashMap<>();
|
private final Map<ASN1Any, Integer> results;
|
||||||
|
|
||||||
public boolean execute(ZClient client, RPNQuery rpn) throws IOException {
|
private final String resultSetName;
|
||||||
|
|
||||||
|
private final List<String> databases;
|
||||||
|
|
||||||
|
private final String host;
|
||||||
|
|
||||||
|
public SearchOperation(BERReader reader, BERWriter writer,
|
||||||
|
String resultSetName,
|
||||||
|
List<String> 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 {
|
try {
|
||||||
SearchRequest search = new SearchRequest();
|
SearchRequest search = new SearchRequest();
|
||||||
search.s_query = new Query();
|
search.s_query = new Query();
|
||||||
|
@ -44,8 +62,7 @@ public class SearchOperation {
|
||||||
search.s_mediumSetPresentNumber = new ASN1Integer(0);
|
search.s_mediumSetPresentNumber = new ASN1Integer(0);
|
||||||
search.s_replaceIndicator = new ASN1Boolean(true);
|
search.s_replaceIndicator = new ASN1Boolean(true);
|
||||||
search.s_resultSetName = new InternationalString();
|
search.s_resultSetName = new InternationalString();
|
||||||
search.s_resultSetName.value = new ASN1GeneralString(client.getResultSetName());
|
search.s_resultSetName.value = new ASN1GeneralString(resultSetName);
|
||||||
List<String> databases = client.getDatabases();
|
|
||||||
DatabaseName dbs[] = new DatabaseName[databases.size()];
|
DatabaseName dbs[] = new DatabaseName[databases.size()];
|
||||||
for (int n = 0; n < databases.size(); n++) {
|
for (int n = 0; n < databases.size(); n++) {
|
||||||
dbs[n] = new DatabaseName();
|
dbs[n] = new DatabaseName();
|
||||||
|
@ -55,8 +72,8 @@ public class SearchOperation {
|
||||||
search.s_databaseNames = dbs;
|
search.s_databaseNames = dbs;
|
||||||
PDU pduRequest = new PDU();
|
PDU pduRequest = new PDU();
|
||||||
pduRequest.c_searchRequest = search;
|
pduRequest.c_searchRequest = search;
|
||||||
client.writePDU(pduRequest);
|
writePDU(pduRequest);
|
||||||
PDU pduResponse = client.readPDU();
|
PDU pduResponse = readPDU();
|
||||||
SearchResponse response = pduResponse.c_searchResponse;
|
SearchResponse response = pduResponse.c_searchResponse;
|
||||||
count = response.s_resultCount.get();
|
count = response.s_resultCount.get();
|
||||||
ASN1Boolean b = response.s_searchStatus;
|
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;
|
PresentStatus presentStatus = response.s_presentStatus;
|
||||||
if (presentStatus != null && presentStatus.value != null && presentStatus.value.get() == 5) {
|
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))) {
|
if (!dbName.value.value.get().equalsIgnoreCase(databases.get(i))) {
|
||||||
String message = "database name listed in additional search info " +
|
String message = "database name listed in additional search info " +
|
||||||
"doesn't match database name in names set.";
|
"doesn't match database name in names set.";
|
||||||
throw new IOException(client.getHost() + ": " + message);
|
throw new IOException(host + ": " + message);
|
||||||
}
|
}
|
||||||
ASN1Integer res = (ASN1Integer) details[1];
|
ASN1Integer res = (ASN1Integer) details[1];
|
||||||
results.put(target, res.get());
|
results.put(target, res.get());
|
||||||
|
@ -101,7 +118,7 @@ public class SearchOperation {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (SocketTimeoutException e) {
|
} catch (SocketTimeoutException e) {
|
||||||
throw new IOException(client.getHost() + ": timeout", e);
|
throw new IOException(host + ": timeout", e);
|
||||||
}
|
}
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,7 +56,6 @@ public class ZClientTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private static ZClient newZClient(String name) throws IOException {
|
private static ZClient newZClient(String name) throws IOException {
|
||||||
return newZClient(getProperties(name));
|
return newZClient(getProperties(name));
|
||||||
}
|
}
|
||||||
|
@ -69,6 +68,7 @@ public class ZClientTest {
|
||||||
}
|
}
|
||||||
return properties;
|
return properties;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ZClient newZClient(Properties properties) {
|
private static ZClient newZClient(Properties properties) {
|
||||||
ZClient.Builder builder = ZClient.builder();
|
ZClient.Builder builder = ZClient.builder();
|
||||||
if (properties.containsKey("host")) {
|
if (properties.containsKey("host")) {
|
||||||
|
|
Loading…
Reference in a new issue