From fc1672936bf30c86606a57e14990602bc1aeaaf1 Mon Sep 17 00:00:00 2001
From: Endi Sukma Dewata <edewata@redhat.com>
Date: Thu, 1 Mar 2012 19:58:34 -0600
Subject: [PATCH] Added CMSException.

The CMSException was added to simplify error handling in REST services.
The exception may include an error message and some other attributes.
When the server throws a CMSException (or its subclass), the exception
will be marshalled into XML and unmarshalled by the client, then thrown
again as a new exception which can be caught by the application.

Ticket #100
---
 .classpath                                         |    3 +
 base/common/src/CMakeLists.txt                     |    2 +
 .../src/com/netscape/certsrv/dbs/keydb/KeyId.java  |   11 ++
 .../com/netscape/certsrv/request/RequestId.java    |   11 ++
 .../admin/SystemCertificateResourceService.java    |    7 +-
 .../netscape/cms/servlet/base/CMSException.java    |  127 ++++++++++++++++++++
 .../cms/servlet/base/CMSResourceService.java       |   17 +++-
 .../cms/servlet/key/KeyResourceService.java        |    5 -
 .../cms/servlet/key/KeysResourceService.java       |    5 -
 .../servlet/request/KeyRequestResourceService.java |   10 +-
 .../request/KeyRequestsResourceService.java        |    5 -
 .../servlet/request/RequestNotFoundException.java  |   45 +++++++
 .../cms/servlet/test/DRMErrorInterceptor.java      |   43 +++++++
 .../netscape/cms/servlet/test/DRMRestClient.java   |  105 +++++++++--------
 .../src/com/netscape/cms/servlet/test/DRMTest.java |   50 +++++----
 15 files changed, 347 insertions(+), 99 deletions(-)
 create mode 100644 base/common/src/com/netscape/cms/servlet/base/CMSException.java
 create mode 100644 base/common/src/com/netscape/cms/servlet/request/RequestNotFoundException.java
 create mode 100644 base/kra/functional/src/com/netscape/cms/servlet/test/DRMErrorInterceptor.java

diff --git a/.classpath b/.classpath
index 611309f73e163013087b68c8598440b3b90f53dd..96d9712de4419c8683b70f3d40c7f7f1e4ddb1e3 100644
--- a/.classpath
+++ b/.classpath
@@ -26,6 +26,9 @@
 	<classpathentry kind="lib" path="/usr/share/java/junit4.jar"/>
 	<classpathentry kind="lib" path="/usr/share/java/ldapjdk.jar"/>
 	<classpathentry kind="lib" path="/usr/share/java/glassfish-jaxb/jaxb-impl.jar"/>
+	<classpathentry kind="lib" path="/usr/share/java/resteasy/commons-httpclient-3.1.jar"/>
+	<classpathentry kind="lib" path="/usr/share/java/resteasy/httpclient-4.1.2.jar"/>
+	<classpathentry kind="lib" path="/usr/share/java/resteasy/httpcore-4.1.2.jar"/>
 	<classpathentry kind="lib" path="/usr/share/java/resteasy/jaxrs-api.jar"/>
 	<classpathentry kind="lib" path="/usr/share/java/resteasy/resteasy-jaxb-provider.jar"/>
 	<classpathentry kind="lib" path="/usr/share/java/resteasy/resteasy-jaxrs.jar"/>
diff --git a/base/common/src/CMakeLists.txt b/base/common/src/CMakeLists.txt
index 01ff33e47760a40d8490bce131a79ef52fdee59e..ea9077f7432123bb7c4444cb7cc169bbbc482412 100644
--- a/base/common/src/CMakeLists.txt
+++ b/base/common/src/CMakeLists.txt
@@ -579,6 +579,7 @@ set(pki-cms_java_SRCS
     com/netscape/cms/servlet/base/IndexServlet.java
     com/netscape/cms/servlet/base/UserInfo.java
     com/netscape/cms/servlet/base/PortsServlet.java
+    com/netscape/cms/servlet/base/CMSException.java
     com/netscape/cms/servlet/base/CMSResourceService.java
     com/netscape/cms/servlet/base/CMSServlet.java
     com/netscape/cms/servlet/base/CMSStartServlet.java
@@ -664,6 +665,7 @@ set(pki-cms_java_SRCS
     com/netscape/cms/servlet/request/IReqParser.java
     com/netscape/cms/servlet/request/ReqParser.java
     com/netscape/cms/servlet/request/QueryReq.java
+    com/netscape/cms/servlet/request/RequestNotFoundException.java
     com/netscape/cms/servlet/request/SearchReqs.java
     com/netscape/cms/servlet/request/ProcessCertReq.java
     com/netscape/cms/servlet/request/CertReqParser.java
diff --git a/base/common/src/com/netscape/certsrv/dbs/keydb/KeyId.java b/base/common/src/com/netscape/certsrv/dbs/keydb/KeyId.java
index f998bf97a492eb0f8cd6d2ab5242b26515cb4c7d..b8bb1870b97f3bf8d6409db33d9cc6592908f032 100644
--- a/base/common/src/com/netscape/certsrv/dbs/keydb/KeyId.java
+++ b/base/common/src/com/netscape/certsrv/dbs/keydb/KeyId.java
@@ -95,6 +95,17 @@ public class KeyId {
         return value.toString();
     }
 
+    /**
+     * Converts the KeyId into its hex string representation. The string
+     * form can be stored in a database (such as the LDAP directory)
+     *
+     * @return
+     *         a string containing the hex (hex 16) value for the identifier.
+     */
+    public String toHexString() {
+        return "0x"+value.toString(16);
+    }
+
     @Override
     public int hashCode() {
         final int prime = 31;
diff --git a/base/common/src/com/netscape/certsrv/request/RequestId.java b/base/common/src/com/netscape/certsrv/request/RequestId.java
index da61f2bc0b5a80e5075b260d031b846ae99288ca..b643fa30d50c3256f806392698ffc72b5e1f9d9b 100644
--- a/base/common/src/com/netscape/certsrv/request/RequestId.java
+++ b/base/common/src/com/netscape/certsrv/request/RequestId.java
@@ -94,6 +94,17 @@ public class RequestId {
         return value.toString();
     }
 
+    /**
+     * Converts the RequestId into its hex string representation. The string
+     * form can be stored in a database (such as the LDAP directory)
+     *
+     * @return
+     *         a string containing the hex (base 16) value for the identifier.
+     */
+    public String toHexString() {
+        return "0x"+value.toString(16);
+    }
+
     @Override
     public int hashCode() {
         final int prime = 31;
diff --git a/base/common/src/com/netscape/cms/servlet/admin/SystemCertificateResourceService.java b/base/common/src/com/netscape/cms/servlet/admin/SystemCertificateResourceService.java
index 48f410c73df5740cf3858d46b4da0bedf388a69d..4dccb141fdd6b990206e04ce71f27fe2e205b1de 100644
--- a/base/common/src/com/netscape/cms/servlet/admin/SystemCertificateResourceService.java
+++ b/base/common/src/com/netscape/cms/servlet/admin/SystemCertificateResourceService.java
@@ -21,8 +21,6 @@ package com.netscape.cms.servlet.admin;
 import java.security.cert.CertificateEncodingException;
 
 import javax.ws.rs.WebApplicationException;
-import javax.ws.rs.core.Context;
-import javax.ws.rs.core.Request;
 import javax.ws.rs.core.Response;
 
 import com.netscape.certsrv.apps.CMS;
@@ -39,9 +37,6 @@ import com.netscape.cms.servlet.cert.model.CertificateData;
  */
 public class SystemCertificateResourceService extends CMSResourceService implements SystemCertificateResource {
 
-    @Context
-    Request request;
-
     /**
      * Used to retrieve the transport certificate
      */
@@ -74,7 +69,7 @@ public class SystemCertificateResourceService extends CMSResourceService impleme
             e.printStackTrace();
             throw new WebApplicationException(Response.Status.INTERNAL_SERVER_ERROR);
         }
-        return sendConditionalGetResponse(DEFAULT_LONG_CACHE_LIFETIME, cert, request);
+        return sendConditionalGetResponse(DEFAULT_LONG_CACHE_LIFETIME, cert);
     }
 
 }
diff --git a/base/common/src/com/netscape/cms/servlet/base/CMSException.java b/base/common/src/com/netscape/cms/servlet/base/CMSException.java
new file mode 100644
index 0000000000000000000000000000000000000000..48fc84b9082f8c028d51bd57366e570c3edb4d6e
--- /dev/null
+++ b/base/common/src/com/netscape/cms/servlet/base/CMSException.java
@@ -0,0 +1,127 @@
+package com.netscape.cms.servlet.base;
+
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.ExceptionMapper;
+import javax.ws.rs.ext.Provider;
+import javax.xml.bind.JAXBContext;
+import javax.xml.bind.Marshaller;
+import javax.xml.bind.annotation.XmlAttribute;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.XmlValue;
+import javax.xml.bind.annotation.adapters.XmlAdapter;
+import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
+
+public class CMSException extends RuntimeException {
+
+    private static final long serialVersionUID = 6000910362260369923L;
+
+    public CMSException(String message) {
+        super(message);
+    }
+
+    public CMSException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+    public CMSException(Data data) {
+        super(data.message);
+    }
+
+    public Data getData() {
+        Data data = new Data();
+        data.className = getClass().getName();
+        data.message = getMessage();
+        return data;
+    }
+
+    @XmlRootElement(name="CMSException")
+    public static class Data {
+
+        @XmlElement(name="ClassName")
+        public String className;
+
+        @XmlElement(name="Message")
+        public String message;
+
+        @XmlElement(name="Attributes")
+        @XmlJavaTypeAdapter(MapAdapter.class)
+        public Map<String, String> attributes = new LinkedHashMap<String, String>();
+
+        public String getAttribute(String name) {
+            return attributes.get(name);
+        }
+
+        public void setAttribute(String name, String value) {
+            attributes.put(name, value);
+        }
+    }
+
+    public static class MapAdapter extends XmlAdapter<AttributeList, Map<String, String>> {
+
+        public AttributeList marshal(Map<String, String> map) {
+            AttributeList list = new AttributeList();
+            for (Map.Entry<String, String> entry : map.entrySet()) {
+                Attribute attribute = new Attribute();
+                attribute.name = entry.getKey();
+                attribute.value = entry.getValue();
+                list.attributes.add(attribute);
+            }
+            return list;
+        }
+
+        public Map<String, String> unmarshal(AttributeList list) {
+            Map<String, String> map = new LinkedHashMap<String, String>();
+            for (Attribute attribute : list.attributes) {
+                map.put(attribute.name, attribute.value);
+            }
+            return map;
+        }
+    }
+
+    public static class AttributeList {
+        @XmlElement(name="Attribute")
+        public List<Attribute> attributes = new ArrayList<Attribute>();
+    }
+
+    public static class Attribute {
+
+        @XmlAttribute
+        public String name;
+
+        @XmlValue
+        public String value;
+    }
+
+    @Provider
+    public static class Mapper implements ExceptionMapper<CMSException> {
+
+        public Response toResponse(CMSException exception) {
+            // convert CMSException into HTTP code 500 with XML content
+            return Response
+                    .status(Response.Status.INTERNAL_SERVER_ERROR)
+                    .entity(exception.getData())
+                    .type(MediaType.TEXT_XML)
+                    .build();
+        }
+    }
+
+    public static void main(String args[]) throws Exception {
+        Data data = new Data();
+        data.className = CMSException.class.getName();
+        data.message = "An error has occured";
+        data.setAttribute("attr1", "value1");
+        data.setAttribute("attr2", "value2");
+
+        JAXBContext context = JAXBContext.newInstance(Data.class);
+        Marshaller marshaller = context.createMarshaller();
+        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
+        marshaller.marshal(data, System.out);
+    }
+}
diff --git a/base/common/src/com/netscape/cms/servlet/base/CMSResourceService.java b/base/common/src/com/netscape/cms/servlet/base/CMSResourceService.java
index acddba559c2ac0b9152ca2f43f88cec20fde0358..24c3cd74dce9717fb51d2616d11a703f47dacbf7 100644
--- a/base/common/src/com/netscape/cms/servlet/base/CMSResourceService.java
+++ b/base/common/src/com/netscape/cms/servlet/base/CMSResourceService.java
@@ -20,9 +20,11 @@ package com.netscape.cms.servlet.base;
 import java.security.cert.CertificateEncodingException;
 
 import javax.ws.rs.core.CacheControl;
+import javax.ws.rs.core.Context;
 import javax.ws.rs.core.EntityTag;
 import javax.ws.rs.core.Request;
 import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriInfo;
 import javax.ws.rs.core.Response.ResponseBuilder;
 
 import com.netscape.certsrv.apps.CMS;
@@ -35,13 +37,24 @@ import com.netscape.cms.servlet.cert.model.CertificateData;
  * 
  */
 public class CMSResourceService {
+
     public static final String HEADER = "-----BEGIN NEW CERTIFICATE REQUEST-----";
     public static final String TRAILER = "-----END NEW CERTIFICATE REQUEST-----";
 
     // caching parameters
-    protected static final int DEFAULT_LONG_CACHE_LIFETIME = 1000;
+    public static final int DEFAULT_LONG_CACHE_LIFETIME = 1000;
 
-    protected Response sendConditionalGetResponse(int ctime, Object object, Request request) {
+    @Context
+    protected UriInfo uriInfo;
+
+    @Context
+    protected Request request;
+
+    public Response createOKResponse(Object object) {
+        return Response.ok(object).build();
+    }
+
+    public Response sendConditionalGetResponse(int ctime, Object object) {
         CacheControl cc = new CacheControl();
         cc.setMaxAge(ctime);
         EntityTag tag = new EntityTag(Integer.toString(object.hashCode()));
diff --git a/base/common/src/com/netscape/cms/servlet/key/KeyResourceService.java b/base/common/src/com/netscape/cms/servlet/key/KeyResourceService.java
index 79e6ccfdb4e45cb1715835b6cac7d3ce4ff77f1c..88f2f8a827ff4825bf3f70bfc1a26824c26c5834 100644
--- a/base/common/src/com/netscape/cms/servlet/key/KeyResourceService.java
+++ b/base/common/src/com/netscape/cms/servlet/key/KeyResourceService.java
@@ -20,10 +20,8 @@ package com.netscape.cms.servlet.key;
 
 
 import javax.ws.rs.WebApplicationException;
-import javax.ws.rs.core.Context;
 import javax.ws.rs.core.MultivaluedMap;
 import javax.ws.rs.core.Response;
-import javax.ws.rs.core.UriInfo;
 
 import com.netscape.cms.servlet.base.CMSResourceService;
 import com.netscape.cms.servlet.key.model.KeyDAO;
@@ -43,9 +41,6 @@ import com.netscape.certsrv.dbs.keydb.KeyId;
  */
 public class KeyResourceService extends CMSResourceService implements KeyResource{
     
-    @Context
-    UriInfo uriInfo;
-    
     /**
      * Used to retrieve a key
      * @param data
diff --git a/base/common/src/com/netscape/cms/servlet/key/KeysResourceService.java b/base/common/src/com/netscape/cms/servlet/key/KeysResourceService.java
index a7876a6c68418a837193089d527cddf43df89cb9..d525d5a7703458ae3e7964ca5f8183e295b6f08d 100644
--- a/base/common/src/com/netscape/cms/servlet/key/KeysResourceService.java
+++ b/base/common/src/com/netscape/cms/servlet/key/KeysResourceService.java
@@ -21,9 +21,7 @@
 package com.netscape.cms.servlet.key;
 
 import javax.ws.rs.WebApplicationException;
-import javax.ws.rs.core.Context;
 import javax.ws.rs.core.Response;
-import javax.ws.rs.core.UriInfo;
  
 import com.netscape.certsrv.apps.CMS;
 import com.netscape.certsrv.base.EBaseException;
@@ -38,9 +36,6 @@ import com.netscape.cmsutil.ldap.LDAPUtil;
  */
 public class KeysResourceService extends CMSResourceService implements KeysResource {
 
-    @Context
-    UriInfo uriInfo;
-
     /**
      * Used to generate list of key infos based on the search parameters
      */
diff --git a/base/common/src/com/netscape/cms/servlet/request/KeyRequestResourceService.java b/base/common/src/com/netscape/cms/servlet/request/KeyRequestResourceService.java
index 43e58bbdc604beb6e92eb0042f2e06784afb5e3f..66f099464ead860fddafe83b841e45af5bfc2b97 100644
--- a/base/common/src/com/netscape/cms/servlet/request/KeyRequestResourceService.java
+++ b/base/common/src/com/netscape/cms/servlet/request/KeyRequestResourceService.java
@@ -19,12 +19,11 @@
 package com.netscape.cms.servlet.request;
 
 import javax.ws.rs.WebApplicationException;
-import javax.ws.rs.core.Context;
 import javax.ws.rs.core.MultivaluedMap;
 import javax.ws.rs.core.Response;
-import javax.ws.rs.core.UriInfo;
 import com.netscape.certsrv.base.EBaseException;
 import com.netscape.certsrv.request.RequestId;
+import com.netscape.cms.servlet.base.CMSException;
 import com.netscape.cms.servlet.base.CMSResourceService;
 import com.netscape.cms.servlet.request.model.ArchivalRequestData;
 import com.netscape.cms.servlet.request.model.KeyRequestDAO;
@@ -37,9 +36,6 @@ import com.netscape.cms.servlet.request.model.RecoveryRequestData;
  */
 public class KeyRequestResourceService extends CMSResourceService implements KeyRequestResource {
 
-    @Context
-    UriInfo uriInfo;
-    
     /**
      * Used to retrieve key request info for a specific request
      */
@@ -52,11 +48,11 @@ public class KeyRequestResourceService extends CMSResourceService implements Key
         } catch (EBaseException e) {
             // log error
             e.printStackTrace();
-            throw new WebApplicationException(Response.Status.INTERNAL_SERVER_ERROR);
+            throw new CMSException(e.getMessage());
         }
         if (info == null) {
             // request does not exist
-            throw new WebApplicationException(Response.Status.NOT_FOUND);
+            throw new RequestNotFoundException(id);
         }
         return info;
     }
diff --git a/base/common/src/com/netscape/cms/servlet/request/KeyRequestsResourceService.java b/base/common/src/com/netscape/cms/servlet/request/KeyRequestsResourceService.java
index 11898ef7a2d7e79e4cfdc4b0018b3e4b9d2ebf46..82b1efa07633e67668dd92b0720512bf61859cb6 100644
--- a/base/common/src/com/netscape/cms/servlet/request/KeyRequestsResourceService.java
+++ b/base/common/src/com/netscape/cms/servlet/request/KeyRequestsResourceService.java
@@ -19,9 +19,7 @@
 package com.netscape.cms.servlet.request;
 
 import javax.ws.rs.WebApplicationException;
-import javax.ws.rs.core.Context;
 import javax.ws.rs.core.Response;
-import javax.ws.rs.core.UriInfo;
 
 import com.netscape.certsrv.apps.CMS;
 import com.netscape.certsrv.base.EBaseException;
@@ -37,9 +35,6 @@ import com.netscape.cmsutil.ldap.LDAPUtil;
  */
 public class KeyRequestsResourceService extends CMSResourceService implements KeyRequestsResource{
 
-    @Context
-    UriInfo uriInfo;
-
     /**
      * Used to generate list of key requests based on the search parameters
      */
diff --git a/base/common/src/com/netscape/cms/servlet/request/RequestNotFoundException.java b/base/common/src/com/netscape/cms/servlet/request/RequestNotFoundException.java
new file mode 100644
index 0000000000000000000000000000000000000000..0eae51d0cd0a416a65674fdc67c753fd228ff8fb
--- /dev/null
+++ b/base/common/src/com/netscape/cms/servlet/request/RequestNotFoundException.java
@@ -0,0 +1,45 @@
+package com.netscape.cms.servlet.request;
+
+import com.netscape.certsrv.request.RequestId;
+import com.netscape.cms.servlet.base.CMSException;
+
+public class RequestNotFoundException extends CMSException {
+
+    private static final long serialVersionUID = -4784839378360933483L;
+
+    public RequestId requestId;
+
+    public RequestNotFoundException(RequestId requestId) {
+        this(requestId, "Request ID "+requestId.toHexString()+" not found");
+    }
+
+    public RequestNotFoundException(RequestId requestId, String message) {
+        super(message);
+        this.requestId = requestId;
+    }
+
+    public RequestNotFoundException(RequestId requestId, String message, Throwable cause) {
+        super(message, cause);
+        this.requestId = requestId;
+    }
+
+    public RequestNotFoundException(Data data) {
+        super(data);
+
+        requestId = new RequestId(data.getAttribute("requestId"));
+    }
+
+    public Data getData() {
+        Data data = super.getData();
+        data.setAttribute("requestId", requestId.toString());
+        return data;
+    }
+
+    public RequestId getRequestId() {
+        return requestId;
+    }
+
+    public void setRequestId(RequestId requestId) {
+        this.requestId = requestId;
+    }
+}
diff --git a/base/kra/functional/src/com/netscape/cms/servlet/test/DRMErrorInterceptor.java b/base/kra/functional/src/com/netscape/cms/servlet/test/DRMErrorInterceptor.java
new file mode 100644
index 0000000000000000000000000000000000000000..48d3e7a95c72c6b625bfdac1cec8a60dbac1e734
--- /dev/null
+++ b/base/kra/functional/src/com/netscape/cms/servlet/test/DRMErrorInterceptor.java
@@ -0,0 +1,43 @@
+package com.netscape.cms.servlet.test;
+
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.Response;
+
+import org.jboss.resteasy.client.ClientResponse;
+import org.jboss.resteasy.client.core.ClientErrorInterceptor;
+
+import com.netscape.cms.servlet.base.CMSException;
+
+public class DRMErrorInterceptor implements ClientErrorInterceptor  {
+
+    public void handle(ClientResponse<?> response) {
+
+        // handle HTTP code 500 only
+        if (!response.getResponseStatus().equals(Response.Status.INTERNAL_SERVER_ERROR)) return;
+
+        MultivaluedMap<String, String> headers = response.getHeaders();
+        String contentType = headers.getFirst("Content-Type");
+
+        // handle XML content only
+        if (!contentType.startsWith(MediaType.TEXT_XML)) return;
+
+        CMSException exception;
+
+        try {
+            // Requires RESTEasy 2.3.2
+            // https://issues.jboss.org/browse/RESTEASY-652
+            CMSException.Data data = response.getEntity(CMSException.Data.class);
+
+            Class<?> clazz = Class.forName(data.className);
+            exception = (CMSException) clazz.getConstructor(CMSException.Data.class).newInstance(data);
+
+        } catch (Exception e) {
+            e.printStackTrace();
+            return;
+        }
+
+        throw exception;
+    }
+
+}
diff --git a/base/kra/functional/src/com/netscape/cms/servlet/test/DRMRestClient.java b/base/kra/functional/src/com/netscape/cms/servlet/test/DRMRestClient.java
index 651873b202528a57a5c0dc71d769e20def7c8943..14b2aa3dc4df5aeb01eb0842d6945203f0f56745 100644
--- a/base/kra/functional/src/com/netscape/cms/servlet/test/DRMRestClient.java
+++ b/base/kra/functional/src/com/netscape/cms/servlet/test/DRMRestClient.java
@@ -2,9 +2,9 @@ package com.netscape.cms.servlet.test;
 
 import java.io.IOException;
 import java.net.InetAddress;
-import java.net.MalformedURLException;
 import java.net.Socket;
-import java.net.URL;
+import java.net.URI;
+import java.net.URISyntaxException;
 import java.net.UnknownHostException;
 import java.util.Collection;
 import java.util.Enumeration;
@@ -18,9 +18,14 @@ import org.apache.commons.httpclient.protocol.ProtocolSocketFactory;
 import org.jboss.resteasy.client.ClientExecutor;
 import org.jboss.resteasy.client.ClientResponse;
 import org.jboss.resteasy.client.ProxyFactory;
+import org.jboss.resteasy.client.core.executors.ApacheHttpClientExecutor;
+import org.jboss.resteasy.spi.ResteasyProviderFactory;
+import org.mozilla.jss.ssl.SSLCertificateApprovalCallback;
+import org.mozilla.jss.ssl.SSLClientCertificateSelectionCallback;
+import org.mozilla.jss.ssl.SSLSocket;
+
 import com.netscape.certsrv.dbs.keydb.KeyId;
 import com.netscape.certsrv.request.RequestId;
-import org.jboss.resteasy.client.core.executors.ApacheHttpClientExecutor;
 import com.netscape.cms.servlet.admin.SystemCertificateResource;
 import com.netscape.cms.servlet.cert.model.CertificateData;
 import com.netscape.cms.servlet.key.KeyResource;
@@ -36,10 +41,6 @@ import com.netscape.cms.servlet.request.model.KeyRequestInfos;
 import com.netscape.cms.servlet.request.model.RecoveryRequestData;
 import com.netscape.cmsutil.util.Utils;
 
-import org.mozilla.jss.ssl.SSLCertificateApprovalCallback;
-import org.mozilla.jss.ssl.SSLClientCertificateSelectionCallback;
-import org.mozilla.jss.ssl.SSLSocket;
-
 public class DRMRestClient {
 
     // Callback to approve or deny returned SSL server certs
@@ -47,7 +48,7 @@ public class DRMRestClient {
     // ToDO: Look into taking this JSS http client code and move it into
     // its own class to be used by possible future clients.
     private class ServerCertApprovalCB implements SSLCertificateApprovalCallback {
-        
+
         public boolean approve(org.mozilla.jss.crypto.X509Certificate servercert,
                 SSLCertificateApprovalCallback.ValidityStatus status) {
 
@@ -81,7 +82,7 @@ public class DRMRestClient {
                         reason == SSLCertificateApprovalCallback.ValidityStatus.BAD_CERT_DOMAIN) {
 
                     //Allow these two since we haven't necessarily installed the CA cert for trust
-                    // and we are choosing "localhost" as the host for this client. 
+                    // and we are choosing "localhost" as the host for this client.
 
                     return true;
 
@@ -93,91 +94,95 @@ public class DRMRestClient {
             return false;
         }
     }
-    
+
     private  class JSSProtocolSocketFactory implements ProtocolSocketFactory {
 
         @Override
         public Socket createSocket(String host, int port) throws IOException, UnknownHostException {
-            
+
             SSLSocket sock = createJSSSocket(host,port, null, 0, null);
-            return (Socket) sock;
+            return sock;
         }
 
         @Override
         public Socket createSocket(String host, int port, InetAddress clientHost, int clientPort) throws IOException,
                 UnknownHostException {
-            
+
             SSLSocket sock = createJSSSocket(host,port, clientHost, clientPort, null);
-            return (Socket) sock;
+            return sock;
         }
 
         @Override
         public Socket createSocket(String host, int port, InetAddress localAddress, int localPort, HttpConnectionParams params)
                 throws IOException, UnknownHostException, ConnectTimeoutException {
-            
+
            SSLSocket sock = createJSSSocket(host, port, localAddress, localPort, null);
-           return (Socket) sock;
+           return sock;
         }
     }
 
-    private SSLSocket createJSSSocket(String host, int port, InetAddress localAddress, 
+    private SSLSocket createJSSSocket(String host, int port, InetAddress localAddress,
             int localPort, SSLClientCertificateSelectionCallback clientCertSelectionCallback)
             throws IOException, UnknownHostException, ConnectTimeoutException {
-        
+
         SSLSocket sock = new SSLSocket(InetAddress.getByName(host),
                 port,
                 localAddress,
                 localPort,
                 new ServerCertApprovalCB(),
                 null);
-        
+
         if(sock != null && clientCertNickname != null) {
             sock.setClientCertNickname(clientCertNickname);
         }
-        
+
         return  sock;
-        
+
     }
     private KeyResource keyClient;
     private KeysResource keysClient;
     private KeyRequestsResource keyRequestsClient;
     private KeyRequestResource keyRequestClient;
     private SystemCertificateResource systemCertClient;
-    
+
     private String clientCertNickname = null;
-    
-    public DRMRestClient(String baseUri, String clientCertNick) throws MalformedURLException {
-        
+
+    public DRMRestClient(String baseUri, String clientCertNick) throws URISyntaxException {
+
         // For SSL we are assuming the caller has already intialized JSS and has
         // a valid CryptoManager and CryptoToken
         // optional clientCertNickname is provided for use if required.
-        
-        
-        URL url = new URL(baseUri);
-        
-        String protocol = url.getProtocol();
-        int port = url.getPort();
-        
+
+
+        URI uri = new URI(baseUri);
+
+        String protocol = uri.getScheme();
+        int port = uri.getPort();
+
         clientCertNickname = null;
         if(protocol != null && protocol.equals("https")) {
             if (clientCertNick != null) {
                 clientCertNickname = clientCertNick;
-            } 
-            
-            Protocol.registerProtocol("https", 
+            }
+
+            Protocol.registerProtocol("https",
                 new Protocol(protocol, new JSSProtocolSocketFactory(), port));
         }
-        
+
         HttpClient httpclient = new HttpClient();
         ClientExecutor executor = new ApacheHttpClientExecutor(httpclient);
 
-        systemCertClient = ProxyFactory.create(SystemCertificateResource.class, baseUri, executor);
-        keyRequestsClient = ProxyFactory.create(KeyRequestsResource.class, baseUri, executor);
-        keyRequestClient = ProxyFactory.create(KeyRequestResource.class, baseUri, executor);
-        keysClient = ProxyFactory.create(KeysResource.class, baseUri, executor);
-        keyClient = ProxyFactory.create(KeyResource.class, baseUri, executor);
+        ResteasyProviderFactory providerFactory = ResteasyProviderFactory.getInstance();
+        providerFactory.addClientErrorInterceptor(new DRMErrorInterceptor());
+
+        systemCertClient = ProxyFactory.create(SystemCertificateResource.class, uri, executor, providerFactory);
+        keyRequestsClient = ProxyFactory.create(KeyRequestsResource.class, uri, executor, providerFactory);
+        keyRequestClient = ProxyFactory.create(KeyRequestResource.class, uri, executor, providerFactory);
+        keysClient = ProxyFactory.create(KeysResource.class, uri, executor, providerFactory);
+        keyClient = ProxyFactory.create(KeyResource.class, uri, executor, providerFactory);
+        keyClient = ProxyFactory.create(KeyResource.class, uri, executor, providerFactory);
     }
-    
+
     public String getTransportCert() {
         @SuppressWarnings("unchecked")
         ClientResponse<CertificateData> response = (ClientResponse<CertificateData>) systemCertClient.getTransportCert();
@@ -185,7 +190,7 @@ public class DRMRestClient {
         String transportCert = certData.getB64();
         return transportCert;
     }
-    
+
     public Collection<KeyRequestInfo> listRequests(String requestState, String requestType) {
         KeyRequestInfos infos = keyRequestsClient.listRequests(
                 requestState, requestType, null, new RequestId(0), 100, 100, 10
@@ -193,7 +198,7 @@ public class DRMRestClient {
         Collection<KeyRequestInfo> list = infos.getRequests();
         return list;
     }
-    
+
     public KeyRequestInfo archiveSecurityData(byte[] encoded, String clientId, String dataType) {
         // create archival request
         ArchivalRequestData data = new ArchivalRequestData();
@@ -205,7 +210,7 @@ public class DRMRestClient {
         KeyRequestInfo info = keyRequestClient.archiveKey(data);
         return info;
     }
-    
+
     public KeyDataInfo getKeyData(String clientId, String status) {
         KeyDataInfos infos = keysClient.listKeys(clientId, status, 100, 10);
         Collection<KeyDataInfo> list = infos.getKeyInfos();
@@ -220,7 +225,7 @@ public class DRMRestClient {
         }
         return null;
     }
-    
+
     public KeyRequestInfo requestRecovery(KeyId keyId, byte[] rpwd, byte[] rkey, byte[] nonceData) {
         // create recovery request
         RecoveryRequestData data = new RecoveryRequestData();
@@ -239,11 +244,11 @@ public class DRMRestClient {
         KeyRequestInfo info = keyRequestClient.recoverKey(data);
         return info;
     }
-    
+
     public void approveRecovery(RequestId recoveryId) {
         keyRequestClient.approveRequest(recoveryId);
     }
-    
+
     public KeyData retrieveKey(KeyId keyId, RequestId requestId, byte[] rpwd, byte[] rkey, byte[] nonceData) {
         // create recovery request
         RecoveryRequestData data = new RecoveryRequestData();
@@ -263,4 +268,8 @@ public class DRMRestClient {
         KeyData key = keyClient.retrieveKey(data);
         return key;
     }
+
+    public KeyRequestInfo getRequest(RequestId id) {
+        return keyRequestClient.getRequestInfo(id);
+    }
 }
diff --git a/base/kra/functional/src/com/netscape/cms/servlet/test/DRMTest.java b/base/kra/functional/src/com/netscape/cms/servlet/test/DRMTest.java
index 8d83247b878c302ef6aaf6510cd418bb8d1451f4..5323777bd5535cbfab53ae7c13de7405494ed1f2 100644
--- a/base/kra/functional/src/com/netscape/cms/servlet/test/DRMTest.java
+++ b/base/kra/functional/src/com/netscape/cms/servlet/test/DRMTest.java
@@ -17,12 +17,17 @@
 // --- END COPYRIGHT BLOCK ---
 package com.netscape.cms.servlet.test;
 
-import java.net.MalformedURLException;
 import java.util.Calendar;
 import java.util.Collection;
 import java.util.Iterator;
 import java.util.Random;
 
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.CommandLineParser;
+import org.apache.commons.cli.HelpFormatter;
+import org.apache.commons.cli.Options;
+import org.apache.commons.cli.ParseException;
+import org.apache.commons.cli.PosixParser;
 import org.mozilla.jss.CryptoManager;
 import org.mozilla.jss.crypto.AlreadyInitializedException;
 import org.mozilla.jss.crypto.CryptoToken;
@@ -32,19 +37,13 @@ import org.mozilla.jss.crypto.KeyGenAlgorithm;
 import org.mozilla.jss.crypto.SymmetricKey;
 import org.mozilla.jss.util.Password;
 
-import org.apache.commons.cli.CommandLine;
-import org.apache.commons.cli.CommandLineParser;
-import org.apache.commons.cli.HelpFormatter;
-import org.apache.commons.cli.Options;
-import org.apache.commons.cli.ParseException;
-import org.apache.commons.cli.PosixParser;
-
 import com.netscape.certsrv.dbs.keydb.KeyId;
 import com.netscape.certsrv.request.RequestId;
 import com.netscape.cms.servlet.base.CMSResourceService;
 import com.netscape.cms.servlet.key.model.KeyData;
 import com.netscape.cms.servlet.key.model.KeyDataInfo;
 import com.netscape.cms.servlet.request.KeyRequestResource;
+import com.netscape.cms.servlet.request.RequestNotFoundException;
 import com.netscape.cms.servlet.request.model.KeyRequestInfo;
 import com.netscape.cmsutil.crypto.CryptoUtil;
 import com.netscape.cmsutil.util.Utils;
@@ -102,18 +101,18 @@ public class DRMTest {
             if (cmd.hasOption("d")) {
                 db_dir = cmd.getOptionValue("d");
             }
-            
+
             if (cmd.hasOption("s")) {
                 if(cmd.getOptionValue("s") != null && cmd.getOptionValue("s").equals("true")) {
                     protocol = "https";
                 }
             }
-            
+
             if (cmd.hasOption("c")) {
                 String nick = cmd.getOptionValue("c");
-                
+
                 if (nick != null && protocol.equals("https")) {
-                    clientCertNickname = nick;    
+                    clientCertNickname = nick;
                 }
             }
 
@@ -123,7 +122,7 @@ public class DRMTest {
         }
 
         // used for crypto operations
-        byte iv[] = { 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1 }; 
+        byte iv[] = { 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1 };
         IVParameterSpec ivps = null;
         IVParameterSpec ivps_server = null;
 
@@ -168,7 +167,7 @@ public class DRMTest {
         try {
             CryptoManager.initialize(db_dir);
         } catch (AlreadyInitializedException e) {
-            // it is ok if it is already initialized 
+            // it is ok if it is already initialized
         } catch (Exception e) {
             log("INITIALIZATION ERROR: " + e.toString());
             System.exit(1);
@@ -192,15 +191,14 @@ public class DRMTest {
         }
 
         // Set base URI and get client
-        
-        
+
+
         String baseUri = protocol + "://" + host + ":" + port + "/kra/pki";
         DRMRestClient client;
         try {
             client = new DRMRestClient(baseUri, clientCertNickname);
-        } catch (MalformedURLException e1) {
-            // TODO Auto-generated catch block
-            e1.printStackTrace();
+        } catch (Exception e) {
+            e.printStackTrace();
             return;
         }
 
@@ -294,7 +292,7 @@ public class DRMTest {
 
         ivps_server = new IVParameterSpec(Utils.base64decode(keyData.getNonceData()));
         try {
-            recoveredKey = CryptoUtil.unwrapUsingSymmetricKey(token, ivps_server, 
+            recoveredKey = CryptoUtil.unwrapUsingSymmetricKey(token, ivps_server,
                     Utils.base64decode(wrappedRecoveredKey),
                     recoveryKey, EncryptionAlgorithm.DES3_CBC_PAD);
         } catch (Exception e) {
@@ -406,7 +404,7 @@ public class DRMTest {
         wrappedRecoveredKey = keyData.getWrappedPrivateData();
         ivps_server = new IVParameterSpec( Utils.base64decode(keyData.getNonceData()));
         try {
-            recoveredKey = CryptoUtil.unwrapUsingSymmetricKey(token, ivps_server, 
+            recoveredKey = CryptoUtil.unwrapUsingSymmetricKey(token, ivps_server,
                     Utils.base64decode(wrappedRecoveredKey),
                     recoveryKey, EncryptionAlgorithm.DES3_CBC_PAD);
             recoveredKey = new String(Utils.base64decode(recoveredKey), "UTF-8");
@@ -476,6 +474,16 @@ public class DRMTest {
         } else {
             log("Success: recovered and archived passphrases do match!");
         }
+
+        // Test 23: Get non-existent request
+        RequestId requestId = new RequestId("0xabcdef");
+        log("Getting non-existent request: " + requestId.toHexString());
+        try {
+            client.getRequest(requestId);
+            log("Error: getting non-existent request does not throw an exception");
+        } catch (RequestNotFoundException e) {
+            log("Success: getting non-existent request throws an exception: "+e.getMessage()+" ("+e.getRequestId().toHexString()+")");
+        }
     }
 
     private static void log(String string) {
-- 
1.7.7.4

