AmazonS3InputDataHandler with Groupdocs.Annotation v1.9.0 issue

Using the AmazonS3InputDataHandler with Groupdocs.Annotation v1.9.0 I’ve run into issues. After making a request and viewing 4 or 5 documents, I will eventually get the following exception.


[error] c.g.a.h.AnnotationHandler - Annotation throws exception: Unable to execute HTTP request: Timeout waiting for connection from pool
com.groupdocs.annotation.exception.AnnotationException: Unable to execute HTTP request: Timeout waiting for connection from pool
at com.groupdocs.annotation.api.shared.CommonApi.viewDocument(CommonApi.java:368)
at com.groupdocs.annotation.api.shared.WebApi.viewDocumentHandler(WebApi.java:307)
at com.groupdocs.annotation.handler.AnnotationHandler.viewDocumentHandler(AnnotationHandler.java:128)
at groupdocs.ClientViewer$.viewDocument(ClientViewer.scala:20)


I ran across this post in the AWS forums along with others that have similar issues.

https://forums.aws.amazon.com/message.jspa?messageID=471149

It looks as though the S3Object may not be getting closed properly.

I know from another project of ours in which we use the Amazon S3 client in Scala, we create a new AmazonS3Client for each method call we make. I noticed in the AmazonS3InputDataHandler that the S3Client is a private final class variable.

public class AmazonS3InputDataHandler extends InputDataHandler {
private final AmazonS3 s3;
private final String bucketName;
private final String uploadPath;

public AmazonS3InputDataHandler(String accessKey, String secretKey, String bucketName) {
this(accessKey, secretKey, bucketName, (String)null);
}

public AmazonS3InputDataHandler(String accessKey, String secretKey, String bucketName, String uploadPath) {
BasicAWSCredentials credentials = new BasicAWSCredentials(accessKey, secretKey);
this.s3 = new AmazonS3Client(credentials);
this.bucketName = bucketName;
if(uploadPath == null) {
this.uploadPath = “”;
} else {
if(!uploadPath.endsWith(“/”)) {
uploadPath = uploadPath + “/”;
}

this.uploadPath = uploadPath;
}

}

As a work around I created a new class, AwsS3InputDataHandler, in which I moved the S3Client from a class level variable into a local method variable.  This seems to resolve the issue.  See the code example below.

public class AwsS3InputDataHandler extends InputDataHandler {
private final BasicAWSCredentials credentials;
private final String bucketName;
private final String uploadPath;

public AwsS3InputDataHandler(String accessKey, String secretKey, String bucketName) {
this(accessKey, secretKey, bucketName, (String)null);
}

public AwsS3InputDataHandler(String accessKey, String secretKey, String bucketName, String uploadPath) {
credentials = new BasicAWSCredentials(accessKey, secretKey);
this.bucketName = bucketName;
if(uploadPath == null) {
this.uploadPath = “”;
} else {
if(!uploadPath.endsWith(“/”)) {
uploadPath = uploadPath + “/”;
}

this.uploadPath = uploadPath;
}

}
    public InputStream getFile(String guid) {
AmazonS3 s3 = new AmazonS3Client(credentials);
String decodedGuid = d.a(guid);
S3Object object = s3.getObject(new GetObjectRequest(this.bucketName, decodedGuid));
S3ObjectInputStream content = object.getObjectContent();

return content;
}
In order to resolve this issue I think it would require tracking down where the S3Object is being referenced and then not being closed, but for the time being moving the S3Client to a method level variable seems to be a reasonable work around.

For our specific needs this was sufficient for an implementation of an AWS S3 InputDataHandler in Scala.



class S3(key: String, secret: String, bucket: String) extends InputDataHandler {
private def getClient(): AmazonS3 = new AmazonS3Client(new BasicAWSCredentials(key, secret))

override def getFileDescription(guid: String): GroupDocsFileDescription = {
val s3: AmazonS3 = getClient()
val s3Object: S3Object = s3.getObject(new GetObjectRequest(bucket, d.a(guid)))
val fileDescription: GroupDocsFileDescription = new GroupDocsFileDescription

fileDescription.setGuid(guid)
fileDescription.setName(s3Object.getKey)
fileDescription.setSize(s3Object.getObjectMetadata.getContentLength)
fileDescription.setLastModified(s3Object.getObjectMetadata.getLastModified.getTime)

fileDescription
}

override def getFile(guid: String): InputStream = {
val s3: AmazonS3 = getClient()
val s3Object: S3Object = s3.getObject(new GetObjectRequest(bucket, d.a(guid)))

s3Object.getObjectContent
}

override def saveFile(inputStream: InputStream, s: String, integer: Integer, s1: String): String = ??? // Not needed

override def getFileDescriptionList(s: String): util.List[GroupDocsFileDescription] = {
java.util.Collections.emptyListGroupDocsFileDescription // Not needed but is called so provide empty list
}
}

object S3 {
def getInstance: S3 = {
val AWS_KEY: String = current.configuration.getString(“aws.AccessKey”) match {
case configString => configString.get
}
val AWS_SECRET: String = current.configuration.getString(“aws.SecretAccessKey”) match {
case configString => configString.get
}
val AWS_BUCKET: String = current.configuration.getString(“aws.uploadBucket”) match {
case configString => configString.get
}

new S3(AWS_KEY, AWS_SECRET, AWS_BUCKET)
}
}
Hello Tim,

Sorry to see that you have the issue. We have reproduced the issue on our side and informed our product team. Also, we have added the bug ticket "ANNOTATIONJAVA-819" and when it will be fixed you will be notified here.

Best regards
Evgen Efimov

http://groupdocs.com
Your Document Collaboration APIs
Follow us on LinkedIn, Twitter, Facebook and Google+