From 23b6a246ca3d9cf7c64c57e41d5465e130fe8450 Mon Sep 17 00:00:00 2001
From: "Endi S. Dewata" <edewata@redhat.com>
Date: Mon, 10 Feb 2014 13:46:10 -0500
Subject: [PATCH] Upgraded RESTEasy client library.

The Dogtag client library has been modified to use RESTEasy 3.0 client
library. A new upgrade script has been added to update existing servers.

The JAXB annotation in ResourceMessage has been modified to require
explicit property mapping.

Ticket #554
---
 .classpath                                         |  1 +
 base/common/src/CMakeLists.txt                     |  9 ++-
 .../com/netscape/certsrv/base/ResourceMessage.java |  3 +
 .../com/netscape/certsrv/client/PKIConnection.java | 76 +++++++++++++---------
 .../certsrv/client/PKIErrorInterceptor.java        | 62 ------------------
 base/java-tools/pki                                |  1 +
 .../cms/servlet/csadmin/ConfigurationUtils.java    | 26 ++++----
 .../netscape/cms/servlet/csadmin/GetCookie.java    | 24 ++++++-
 .../cms/servlet/csadmin/SystemConfigService.java   | 21 +++++-
 base/server/etc/default.cfg                        |  1 +
 .../deployment/scriptlets/instance_layout.py       |  4 ++
 base/server/scripts/operations                     |  1 +
 base/server/share/conf/pki.policy                  |  4 ++
 base/server/upgrade/10.1.99/03-AddedRESTEasyClient | 76 ++++++++++++++++++++++
 scripts/compose_pki_test_package                   |  1 +
 tests/dogtag/dev_java_tests/run_junit_tests.sh     |  1 +
 16 files changed, 204 insertions(+), 107 deletions(-)
 delete mode 100644 base/common/src/com/netscape/certsrv/client/PKIErrorInterceptor.java
 create mode 100755 base/server/upgrade/10.1.99/03-AddedRESTEasyClient

diff --git a/.classpath b/.classpath
index 1e830791241f0a7272b001beda0246644b81e1af..bc0c70f6bb14fae7cc63acb840d497c40d3beb2b 100644
--- a/.classpath
+++ b/.classpath
@@ -48,6 +48,7 @@
 	<classpathentry kind="lib" path="/usr/share/java/httpcomponents/httpclient.jar"/>
 	<classpathentry kind="lib" path="/usr/share/java/httpcomponents/httpcore.jar"/>
 	<classpathentry kind="lib" path="/usr/share/java/resteasy/jaxrs-api.jar"/>
+	<classpathentry kind="lib" path="/usr/share/java/resteasy/resteasy-client.jar"/>
 	<classpathentry kind="lib" path="/usr/share/java/resteasy/resteasy-jaxb-provider.jar"/>
 	<classpathentry kind="lib" path="/usr/share/java/resteasy/resteasy-jaxrs.jar"/>
 	<classpathentry kind="lib" path="/usr/share/java/resteasy/resteasy-jackson-provider.jar"/>
diff --git a/base/common/src/CMakeLists.txt b/base/common/src/CMakeLists.txt
index e4f99992198bab76079d13faa4a5db53cbb83dcf..f48ef8786f6ea7911e638e1c28fe5a61f9c98b6b 100644
--- a/base/common/src/CMakeLists.txt
+++ b/base/common/src/CMakeLists.txt
@@ -104,6 +104,13 @@ find_file(RESTEASY_ATOM_PROVIDER_JAR
         ${RESTEASY_LIB}
 )
 
+find_file(RESTEASY_CLIENT_JAR
+    NAMES
+        resteasy-client.jar
+    PATHS
+        ${RESTEASY_LIB}
+)
+
 find_file(HTTPCLIENT_JAR
     NAMES
         httpclient.jar
@@ -128,7 +135,7 @@ javac(pki-certsrv-classes
         ${JSS_JAR} ${COMMONS_CODEC_JAR} ${COMMONS_HTTPCLIENT_JAR}
         ${APACHE_COMMONS_LANG_JAR}
         ${TOMCAT_CATALINA_JAR} ${TOMCAT_UTIL_JAR} ${SYMKEY_JAR}
-        ${JAXRS_API_JAR} ${RESTEASY_JAXRS_JAR} ${RESTEASY_ATOM_PROVIDER_JAR}
+        ${JAXRS_API_JAR} ${RESTEASY_JAXRS_JAR} ${RESTEASY_ATOM_PROVIDER_JAR} ${RESTEASY_CLIENT_JAR}
         ${HTTPCLIENT_JAR} ${HTTPCORE_JAR}
     OUTPUT_DIR
         ${CMAKE_BINARY_DIR}/classes
diff --git a/base/common/src/com/netscape/certsrv/base/ResourceMessage.java b/base/common/src/com/netscape/certsrv/base/ResourceMessage.java
index 57b5539b8f59982f46c33187b0c13cab2e61eaba..e7ef44f4bb9fd47e2fd21d45969616f747b720b0 100644
--- a/base/common/src/com/netscape/certsrv/base/ResourceMessage.java
+++ b/base/common/src/com/netscape/certsrv/base/ResourceMessage.java
@@ -13,6 +13,8 @@ import javax.xml.bind.JAXBContext;
 import javax.xml.bind.JAXBException;
 import javax.xml.bind.Marshaller;
 import javax.xml.bind.Unmarshaller;
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
 import javax.xml.bind.annotation.XmlAttribute;
 import javax.xml.bind.annotation.XmlElement;
 import javax.xml.bind.annotation.XmlRootElement;
@@ -24,6 +26,7 @@ import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
  * @author Ade Lee
  */
 @XmlRootElement(name="ResourceMessage")
+@XmlAccessorType(XmlAccessType.NONE)
 public class ResourceMessage {
 
     protected Map<String, String> attributes = new LinkedHashMap<String, String>();
diff --git a/base/common/src/com/netscape/certsrv/client/PKIConnection.java b/base/common/src/com/netscape/certsrv/client/PKIConnection.java
index 6e281dd5aace801ddd42c2d14586198b01f1e628..1c3e58a1be5d4c6a679c39e7e01547324f86724a 100644
--- a/base/common/src/com/netscape/certsrv/client/PKIConnection.java
+++ b/base/common/src/com/netscape/certsrv/client/PKIConnection.java
@@ -8,6 +8,7 @@ import java.io.InputStreamReader;
 import java.io.OutputStream;
 import java.io.PrintStream;
 import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Modifier;
 import java.net.InetAddress;
 import java.net.InetSocketAddress;
@@ -21,7 +22,8 @@ import java.util.Enumeration;
 import java.util.HashSet;
 import java.util.List;
 
-import javax.ws.rs.core.MediaType;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.MultivaluedMap;
 import javax.ws.rs.core.Response;
 
 import org.apache.commons.httpclient.ConnectTimeoutException;
@@ -53,14 +55,11 @@ import org.apache.http.impl.client.RequestWrapper;
 import org.apache.http.message.BasicHttpResponse;
 import org.apache.http.params.HttpParams;
 import org.apache.http.protocol.HttpContext;
-import org.jboss.resteasy.client.ClientExecutor;
-import org.jboss.resteasy.client.ClientRequest;
-import org.jboss.resteasy.client.ClientResponse;
-import org.jboss.resteasy.client.ClientResponseFailure;
-import org.jboss.resteasy.client.ProxyFactory;
-import org.jboss.resteasy.client.core.BaseClientResponse;
-import org.jboss.resteasy.client.core.executors.ApacheHttpClient4Executor;
-import org.jboss.resteasy.client.core.extractors.ClientErrorHandler;
+import org.jboss.resteasy.client.jaxrs.ProxyBuilder;
+import org.jboss.resteasy.client.jaxrs.ResteasyClient;
+import org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder;
+import org.jboss.resteasy.client.jaxrs.ResteasyWebTarget;
+import org.jboss.resteasy.client.jaxrs.engines.ApacheHttpClient4Engine;
 import org.jboss.resteasy.spi.ResteasyProviderFactory;
 import org.mozilla.jss.CryptoManager;
 import org.mozilla.jss.CryptoManager.NotInitializedException;
@@ -68,6 +67,8 @@ import org.mozilla.jss.crypto.X509Certificate;
 import org.mozilla.jss.ssl.SSLCertificateApprovalCallback;
 import org.mozilla.jss.ssl.SSLSocket;
 
+import com.netscape.certsrv.base.PKIException;
+
 
 public class PKIConnection {
 
@@ -82,9 +83,9 @@ public class PKIConnection {
 
     DefaultHttpClient httpClient = new DefaultHttpClient();
 
+    ApacheHttpClient4Engine engine;
+    ResteasyClient resteasyClient;
     ResteasyProviderFactory providerFactory;
-    ClientErrorHandler errorHandler;
-    ClientExecutor executor;
 
     int requestCounter;
     int responseCounter;
@@ -194,10 +195,8 @@ public class PKIConnection {
             }
         });
 
-        executor = new ApacheHttpClient4Executor(httpClient);
-        providerFactory = ResteasyProviderFactory.getInstance();
-        providerFactory.addClientErrorInterceptor(new PKIErrorInterceptor());
-        errorHandler = new ClientErrorHandler(providerFactory.getClientErrorInterceptors());
+        engine = new ApacheHttpClient4Engine(httpClient);
+        resteasyClient = new ResteasyClientBuilder().httpEngine(engine).build();
     }
 
     public void storeRequest(File file, HttpRequest request) throws IOException {
@@ -511,29 +510,48 @@ public class PKIConnection {
     }
 
     public <T> T createProxy(URI uri, Class<T> clazz) throws URISyntaxException {
-        return ProxyFactory.create(clazz, uri, executor, providerFactory);
+        ResteasyWebTarget target = resteasyClient.target(uri);
+        return ProxyBuilder.builder(clazz, target).build();
     }
 
-    @SuppressWarnings("unchecked")
     public <T> T getEntity(Response response, Class<T> clazz) {
-        BaseClientResponse<T> clientResponse = (BaseClientResponse<T>)response;
-        try {
-            clientResponse.checkFailureStatus();
 
-        } catch (ClientResponseFailure e) {
-            errorHandler.clientErrorHandling((BaseClientResponse<T>) e.getResponse(), e);
+        // handle HTTP status code 4xx and 5xx only
+        int code = response.getStatus();
+        if (code < 400) {
+            if (!response.hasEntity()) return null;
+            return response.readEntity(clazz);
+        }
+
+        String contentType = response.getHeaderString("Content-Type");
+
+        if (contentType == null)
+            throw new PKIException("HTTP Error " + code);
 
-        } catch (RuntimeException e) {
-            errorHandler.clientErrorHandling(clientResponse, e);
+        PKIException.Data data = response.readEntity(PKIException.Data.class);
+
+        Class<?> exceptionClass;
+        try {
+            exceptionClass = Class.forName(data.getClassName());
+        } catch (ClassNotFoundException e) {
+            throw new PKIException(e.getMessage(), e);
         }
 
-        return clientResponse.getEntity();
+        try {
+            throw (PKIException) exceptionClass.getConstructor(PKIException.Data.class).newInstance(data);
+        } catch (InstantiationException
+                | IllegalAccessException
+                | IllegalArgumentException
+                | InvocationTargetException
+                | NoSuchMethodException
+                | SecurityException e) {
+            throw new PKIException(e.getMessage(), e);
+        }
     }
 
-    public ClientResponse<String> post(String content) throws Exception {
-        ClientRequest request = executor.createRequest(config.getServerURI().toString());
-        request.body(MediaType.APPLICATION_FORM_URLENCODED, content);
-        return request.post(String.class);
+    public String post(MultivaluedMap<String, String> form) throws Exception {
+        ResteasyWebTarget target = resteasyClient.target(config.getServerURI());
+        return target.request().post(Entity.form(form), String.class);
     }
 
     public void addRejectedCertStatus(Integer rejectedCertStatus) {
diff --git a/base/common/src/com/netscape/certsrv/client/PKIErrorInterceptor.java b/base/common/src/com/netscape/certsrv/client/PKIErrorInterceptor.java
deleted file mode 100644
index abb892289e76536a0c89182dd7753da4083bf687..0000000000000000000000000000000000000000
--- a/base/common/src/com/netscape/certsrv/client/PKIErrorInterceptor.java
+++ /dev/null
@@ -1,62 +0,0 @@
-// --- BEGIN COPYRIGHT BLOCK ---
-// This program is free software; you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation; version 2 of the License.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License along
-// with this program; if not, write to the Free Software Foundation, Inc.,
-// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-//
-// (C) 2007 Red Hat, Inc.
-// All rights reserved.
-// --- END COPYRIGHT BLOCK ---
-package com.netscape.certsrv.client;
-
-import javax.ws.rs.core.MediaType;
-import javax.ws.rs.core.MultivaluedMap;
-
-import org.jboss.resteasy.client.ClientResponse;
-import org.jboss.resteasy.client.core.ClientErrorInterceptor;
-
-import com.netscape.certsrv.base.PKIException;
-
-public class PKIErrorInterceptor implements ClientErrorInterceptor {
-
-    public void handle(ClientResponse<?> response) {
-
-        // handle HTTP code 4xx and 5xx
-        int code = response.getResponseStatus().getStatusCode();
-        if (code < 400)
-            return;
-
-        MultivaluedMap<String, String> headers = response.getResponseHeaders();
-        String contentType = headers.getFirst("Content-Type");
-
-        // handle XML content only
-        if (contentType == null || !contentType.startsWith(MediaType.APPLICATION_XML))
-            return;
-
-        PKIException exception;
-
-        try {
-            // Requires RESTEasy 2.3.2
-            // https://issues.jboss.org/browse/RESTEASY-652
-            PKIException.Data data = response.getEntity(PKIException.Data.class);
-
-            Class<?> clazz = Class.forName(data.getClassName());
-            exception = (PKIException) clazz.getConstructor(PKIException.Data.class).newInstance(data);
-
-        } catch (Exception e) {
-            e.printStackTrace();
-            return;
-        }
-
-        throw exception;
-    }
-
-}
diff --git a/base/java-tools/pki b/base/java-tools/pki
index 87a00d708787ce872da2b621974b003595f1f215..9dfd7ec6e033dc2aec37c370bbf03708a309cc33 100755
--- a/base/java-tools/pki
+++ b/base/java-tools/pki
@@ -102,6 +102,7 @@ $ENV{CLASSPATH} = "/usr/share/java/${PRODUCT}/pki-certsrv.jar:"
                 . "/usr/share/java/jaxb-api.jar:"
                 . "${RESTEASY_LIB}/jaxrs-api.jar:"
                 . "${RESTEASY_LIB}/resteasy-atom-provider.jar:"
+                . "${RESTEASY_LIB}/resteasy-client.jar:"
                 . "${RESTEASY_LIB}/resteasy-jaxb-provider.jar:"
                 . "${RESTEASY_LIB}/resteasy-jaxrs.jar:"
                 . "${RESTEASY_LIB}/resteasy-jaxrs-jandex.jar:"
diff --git a/base/server/cms/src/com/netscape/cms/servlet/csadmin/ConfigurationUtils.java b/base/server/cms/src/com/netscape/cms/servlet/csadmin/ConfigurationUtils.java
index 30d908f2e22f6294e4e2ca4608519cb2b0ca2c1a..840b9f613454bd16e4e7f9dca1a89277011879e9 100644
--- a/base/server/cms/src/com/netscape/cms/servlet/csadmin/ConfigurationUtils.java
+++ b/base/server/cms/src/com/netscape/cms/servlet/csadmin/ConfigurationUtils.java
@@ -54,6 +54,8 @@ import java.util.Vector;
 
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
+import javax.ws.rs.core.MultivaluedHashMap;
+import javax.ws.rs.core.MultivaluedMap;
 import javax.ws.rs.core.Response;
 import javax.xml.parsers.ParserConfigurationException;
 
@@ -78,7 +80,6 @@ import netscape.security.x509.X509CertImpl;
 import netscape.security.x509.X509Key;
 
 import org.apache.velocity.context.Context;
-import org.jboss.resteasy.client.ClientResponse;
 import org.jboss.resteasy.client.ClientResponseFailure;
 import org.mozilla.jss.CryptoManager;
 import org.mozilla.jss.CryptoManager.NicknameConflictException;
@@ -213,8 +214,8 @@ public class ConfigurationUtils {
         return getHttpResponse(hostname, port, secure, uri, content, clientnickname, null);
     }
 
-    public static ClientResponse<String> getClientResponse(String hostname, int port, boolean secure,
-            String path, String content, String clientnickname,
+    public static String post(String hostname, int port, boolean secure,
+            String path, MultivaluedMap<String, String> map, String clientnickname,
             SSLCertificateApprovalCallback certApprovalCallback)
             throws Exception {
 
@@ -225,9 +226,7 @@ public class ConfigurationUtils {
 
         PKIClient client = new PKIClient(config);
         PKIConnection connection = client.getConnection();
-        ClientResponse<String> response = connection.post(content);
-
-        return response;
+        return connection.post(map);
     }
 
     //TODO - replace with Jack's connector code
@@ -328,9 +327,11 @@ public class ConfigurationUtils {
         boolean oldtoken = cs.getBoolean("cs.useOldTokenInterface", false);
 
         if (oldtoken) {
+            CMS.debug("Getting old token");
             return ConfigurationUtils.getOldToken(sdhost, sdport, user, passwd);
         }
 
+        CMS.debug("Getting old cookie");
         String csType = cs.getString("cs.type");
 
         ClientConfig config = new ClientConfig();
@@ -361,9 +362,11 @@ public class ConfigurationUtils {
             return token.getToken();
 
         } catch (ClientResponseFailure e) {
+
             if (e.getResponse().getResponseStatus() == Response.Status.NOT_FOUND) {
                 // try the old servlet
                 String tokenString = getOldCookie(sdhost, sdport, user, passwd);
+                CMS.debug("Token: " + tokenString);
                 return tokenString;
             }
 
@@ -378,12 +381,13 @@ public class ConfigurationUtils {
                 + CMS.getAdminPort() + "/ca/admin/console/config/wizard" +
                 "?p=5&subsystem=" + cs.getString("cs.type");
 
-        String content = "uid=" + URLEncoder.encode(user, "UTF-8") + "&pwd=" + URLEncoder.encode(passwd, "UTF-8") +
-                "&url=" + URLEncoder.encode(subca_url, "UTF-8");
+        MultivaluedMap<String, String> map = new MultivaluedHashMap<String, String>();
+        map.putSingle("uid", user);
+        map.putSingle("pwd", passwd);
+        map.putSingle("url", subca_url);
 
-        ClientResponse<String> response = getClientResponse(sdhost, sdport, true, "/ca/admin/ca/getCookie",
-                content, null, null);
-        String body = response.getEntity();
+        String body = post(sdhost, sdport, true, "/ca/admin/ca/getCookie",
+                map, null, null);
         return getContentValue(body, "header.session_id");
     }
 
diff --git a/base/server/cms/src/com/netscape/cms/servlet/csadmin/GetCookie.java b/base/server/cms/src/com/netscape/cms/servlet/csadmin/GetCookie.java
index b8030f01a3983bfc48e1fed1ee24ecd76197ca69..0d1bcc97f9cdbdaaa1f95a14040a59e6eb04ca7c 100644
--- a/base/server/cms/src/com/netscape/cms/servlet/csadmin/GetCookie.java
+++ b/base/server/cms/src/com/netscape/cms/servlet/csadmin/GetCookie.java
@@ -78,6 +78,15 @@ public class GetCookie extends CMSServlet {
      * @param cmsReq the object holding the request and response information
      */
     protected void process(CMSRequest cmsReq) throws EBaseException {
+        try {
+            processImpl(cmsReq);
+        } catch (Throwable t) {
+            CMS.debug(t);
+            throw t;
+        }
+    }
+
+    protected void processImpl(CMSRequest cmsReq) throws EBaseException {
         HttpServletRequest httpReq = cmsReq.getHttpReq();
         HttpServletResponse httpResp = cmsReq.getHttpResp();
 
@@ -93,13 +102,20 @@ public class GetCookie extends CMSServlet {
         Locale[] locale = new Locale[1];
 
         String url = httpReq.getParameter("url");
-        CMS.debug("GetCookie before auth, url =" + url);
+        CMS.debug("GetCookie before auth, url = " + url);
+        if (url == null) {
+            CMS.debug("GetCookie missing parameter: url");
+            throw new ECMSGWException(
+                    "GetCookie missing parameter: url");
+        }
+
         String url_e = "";
         URL u = null;
         try {
             url_e = URLDecoder.decode(url, "UTF-8");
             u = new URL(url_e);
         } catch (Exception eee) {
+            CMS.debug(eee);
             throw new ECMSGWException(
                     "GetCookie missing parameter: url");
         }
@@ -155,6 +171,7 @@ public class GetCookie extends CMSServlet {
                 httpResp.setContentType("text/html");
                 form.renderOutput(out, argSet);
             } catch (IOException ee) {
+                CMS.debug(ee);
                 log(ILogger.LL_FAILURE,
                         CMS.getLogMessage("CMSGW_ERR_OUT_STREAM_TEMPLATE", ee.toString()));
                 throw new ECMSGWException(
@@ -165,6 +182,7 @@ public class GetCookie extends CMSServlet {
 
         if (authToken != null) {
             String uid = authToken.getInString("uid");
+            CMS.debug("UID: " + uid);
 
             String addr = "";
             try {
@@ -177,6 +195,7 @@ public class GetCookie extends CMSServlet {
 
                 InstallToken installToken = processor.getInstallToken(uid, addr, subsystem);
                 String cookie = installToken.getToken();
+                CMS.debug("Cookie: " + cookie);
 
                 if (!url.startsWith("$")) {
                     try {
@@ -202,6 +221,7 @@ public class GetCookie extends CMSServlet {
                         form.renderOutput(out, argSet);
 
                     } catch (IOException e) {
+                        CMS.debug(e);
                         log(ILogger.LL_FAILURE,
                                 CMS.getLogMessage("CMSGW_ERR_OUT_STREAM_TEMPLATE", e.toString()));
                         throw new ECMSGWException(
@@ -210,7 +230,7 @@ public class GetCookie extends CMSServlet {
                 }
 
             } catch (Exception e) {
-                e.printStackTrace();
+                CMS.debug(e);
             }
         }
     }
diff --git a/base/server/cms/src/com/netscape/cms/servlet/csadmin/SystemConfigService.java b/base/server/cms/src/com/netscape/cms/servlet/csadmin/SystemConfigService.java
index 252a58472b6b50350263814ed5b33e5c7d60df4b..3b3ad225fd93fd4c32b2c9409606aa9fa3d138fe 100644
--- a/base/server/cms/src/com/netscape/cms/servlet/csadmin/SystemConfigService.java
+++ b/base/server/cms/src/com/netscape/cms/servlet/csadmin/SystemConfigService.java
@@ -118,7 +118,17 @@ public class SystemConfigService extends PKIService implements SystemConfigResou
      * @see com.netscape.cms.servlet.csadmin.SystemConfigurationResource#configure(com.netscape.cms.servlet.csadmin.data.ConfigurationData)
      */
     @Override
-    public ConfigurationResponse configure(ConfigurationRequest data){
+    public ConfigurationResponse configure(ConfigurationRequest data) {
+        try {
+            return configureImpl(data);
+        } catch (Throwable t) {
+            CMS.debug(t);
+            throw t;
+        }
+    }
+
+    public ConfigurationResponse configureImpl(ConfigurationRequest data) {
+
         if (csState.equals("1")) {
             throw new BadRequestException("System is already configured");
         }
@@ -915,6 +925,7 @@ public class SystemConfigService extends PKIService implements SystemConfigResou
         String securityDomainURL = data.getSecurityDomainUri();
 
         if (securityDomainType.equals(ConfigurationRequest.NEW_DOMAIN)) {
+            CMS.debug("Creating new security domain");
             cs.putString("preop.securitydomain.select", "new");
             cs.putString("securitydomain.select", "new");
             cs.putString("preop.securitydomain.name", securityDomainName);
@@ -931,12 +942,15 @@ public class SystemConfigService extends PKIService implements SystemConfigResou
                 cs.putString("preop.cert.subsystem.type", "local");
             }
             cs.putString("preop.cert.subsystem.profile", "subsystemCert.profile");
+
         } else {
+            CMS.debug("Joining existing security domain");
             cs.putString("preop.securitydomain.select", "existing");
             cs.putString("securitydomain.select", "existing");
             cs.putString("preop.cert.subsystem.type", "remote");
             cs.putString("preop.cert.subsystem.profile", "caInternalAuthSubsystemCert");
 
+            CMS.debug("Getting certificate chain");
             // contact and log onto security domain
             URL secdomainURL;
             String host;
@@ -953,6 +967,7 @@ public class SystemConfigService extends PKIService implements SystemConfigResou
                 throw new PKIException("Failed to import certificate chain from security domain master: " + e);
             }
 
+            CMS.debug("Getting install token");
             // log onto security domain and get token
             String user = data.getSecurityDomainUser();
             String pass = data.getSecurityDomainPassword();
@@ -960,15 +975,17 @@ public class SystemConfigService extends PKIService implements SystemConfigResou
             try {
                 installToken = ConfigurationUtils.getInstallToken(host, port, user, pass);
             } catch (Exception e) {
-                e.printStackTrace();
+                CMS.debug(e);
                 throw new PKIException("Failed to obtain installation token from security domain: " + e);
             }
 
             if (installToken == null) {
+                CMS.debug("Install token is null");
                 throw new PKIException("Failed to obtain installation token from security domain");
             }
             CMS.setConfigSDSessionId(installToken);
 
+            CMS.debug("Getting domain XML");
             try {
                 domainXML = ConfigurationUtils.getDomainXML(host, port, true);
                 ConfigurationUtils.getSecurityDomainPorts(domainXML, host, port);
diff --git a/base/server/etc/default.cfg b/base/server/etc/default.cfg
index e60cb3a140848d4bdaf8b2c7775bb26ee5ac1946..ea9c54019ba7d65c81accc0b730c09c758dff148 100644
--- a/base/server/etc/default.cfg
+++ b/base/server/etc/default.cfg
@@ -342,6 +342,7 @@ pki_jackson_xc_jar=/usr/share/java/jackson/jackson-xc.jar
 
 # RESTEasy
 pki_resteasy_atom_provider_jar=%(resteasy_lib)s/resteasy-atom-provider.jar
+pki_resteasy_client_jar=%(resteasy_lib)s/resteasy-client.jar
 pki_resteasy_jaxb_provider_jar=%(resteasy_lib)s/resteasy-jaxb-provider.jar
 pki_resteasy_jaxrs_api_jar=%(resteasy_lib)s/jaxrs-api.jar
 pki_resteasy_jaxrs_jar=%(resteasy_lib)s/resteasy-jaxrs.jar
diff --git a/base/server/python/pki/server/deployment/scriptlets/instance_layout.py b/base/server/python/pki/server/deployment/scriptlets/instance_layout.py
index 3dbf62363c9418a05d55623a043b4a5c04b81f30..83c101fa7955f7970b6f58ac8c9bb71aed6f4e41 100644
--- a/base/server/python/pki/server/deployment/scriptlets/instance_layout.py
+++ b/base/server/python/pki/server/deployment/scriptlets/instance_layout.py
@@ -198,6 +198,10 @@ class PkiScriptlet(pkiscriptlet.AbstractBasePkiScriptlet):
                 os.path.join(
                     deployer.master_dict['pki_tomcat_common_lib_path'],
                     'resteasy-atom-provider.jar'))
+            deployer.symlink.create(deployer.master_dict['pki_resteasy_client_jar'],
+                os.path.join(
+                    deployer.master_dict['pki_tomcat_common_lib_path'],
+                    'resteasy-client.jar'))
             deployer.symlink.create(deployer.master_dict['pki_resteasy_jaxb_provider_jar'],
                 os.path.join(
                     deployer.master_dict['pki_tomcat_common_lib_path'],
diff --git a/base/server/scripts/operations b/base/server/scripts/operations
index 21b4748a06da30704fc2eb83934501afee1254c3..9d4dd947b467b488dca457c4f8bd30f2c2f5d870 100644
--- a/base/server/scripts/operations
+++ b/base/server/scripts/operations
@@ -1239,6 +1239,7 @@ verify_symlinks()
             [ldapjdk.jar]=${java_dir}/ldapjdk.jar
             [pki-tomcat.jar]=${java_dir}/pki/pki-tomcat.jar
             [resteasy-atom-provider.jar]=${RESTEASY_LIB}/resteasy-atom-provider.jar
+            [resteasy-client.jar]=${RESTEASY_LIB}/resteasy-client.jar
             [resteasy-jaxb-provider.jar]=${RESTEASY_LIB}/resteasy-jaxb-provider.jar
             [resteasy-jaxrs.jar]=${RESTEASY_LIB}/resteasy-jaxrs.jar
             [resteasy-jackson-provider.jar]=${RESTEASY_LIB}/resteasy-jackson-provider.jar
diff --git a/base/server/share/conf/pki.policy b/base/server/share/conf/pki.policy
index 5ce07b2cf69b667625b8b7ea259038aae3a39abc..6a1018825beba4c7af4732d17c750bb373dea3cf 100644
--- a/base/server/share/conf/pki.policy
+++ b/base/server/share/conf/pki.policy
@@ -162,6 +162,10 @@ grant codeBase "file:${RESTEASY_LIB}/resteasy-atom-provider.jar" {
         permission java.security.AllPermission;
 };
 
+grant codeBase "file:${RESTEASY_LIB}/resteasy-client.jar" {
+        permission java.security.AllPermission;
+};
+
 grant codeBase "file:${RESTEASY_LIB}/resteasy-jaxb-provider.jar" {
         permission java.security.AllPermission;
 };
diff --git a/base/server/upgrade/10.1.99/03-AddedRESTEasyClient b/base/server/upgrade/10.1.99/03-AddedRESTEasyClient
new file mode 100755
index 0000000000000000000000000000000000000000..55bc8c367719e7f22ebd2e81d8ef4369641456b5
--- /dev/null
+++ b/base/server/upgrade/10.1.99/03-AddedRESTEasyClient
@@ -0,0 +1,76 @@
+#!/usr/bin/python
+# Authors:
+#     Endi S. Dewata <edewata@redhat.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; version 2 of the License.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# Copyright (C) 2014 Red Hat, Inc.
+# All rights reserved.
+#
+
+import grp
+import os
+import pwd
+import re
+import subprocess
+
+import pki.server.upgrade
+
+
+class AddedRESTEasyClient(pki.server.upgrade.PKIServerUpgradeScriptlet):
+
+    def __init__(self):
+
+        self.message = 'Added RESTEasy client'
+
+    def upgrade_instance(self, instance):
+
+        # Tomcat common library
+        common_lib = os.path.join(instance.base_dir, 'common', 'lib')
+
+        # Tomcat user and group
+        registry_file = os.path.join(
+            pki.server.REGISTRY_DIR, 'tomcat', instance.name, instance.name)
+
+        with open(registry_file, "r") as registry:
+            lines = registry.readlines()
+
+        for line in lines:
+            m = re.search('^PKI_USER=(.*)$', line)
+            if m:
+                user = m.group(1)
+            m = re.search('^PKI_GROUP=(.*)$', line)
+            if m:
+                group = m.group(1)
+
+        uid = pwd.getpwnam(user).pw_uid
+        gid = grp.getgrnam(group).gr_gid
+
+        # RESTEasy library
+        resteasy_lib = subprocess.check_output(
+            '. /etc/pki/pki.conf && echo $RESTEASY_LIB',
+            shell=True)
+        # workaround for pylint error E1103
+        resteasy_lib = str(resteasy_lib).strip()
+
+        # create new links
+        source = os.path.join(resteasy_lib, 'resteasy-client.jar')
+        link = os.path.join(common_lib, 'resteasy-client.jar')
+        self.create_link(source, link, uid, gid)
+
+    def create_link(self, source, link, uid, gid):
+
+        self.backup(link)
+        os.symlink(source, link)
+        os.lchown(link, uid, gid)
diff --git a/scripts/compose_pki_test_package b/scripts/compose_pki_test_package
index fc534c8e7329cc806ece9cf747672df8256c850e..5b26220a708607652c19c8afd117c615cfed0bf0 100755
--- a/scripts/compose_pki_test_package
+++ b/scripts/compose_pki_test_package
@@ -111,6 +111,7 @@ CLASSPATH=$CLASSPATH:/usr/share/java/jackson/jackson-smile.jar
 CLASSPATH=$CLASSPATH:/usr/share/java/jackson/jackson-xc.jar
 CLASSPATH=$CLASSPATH:/usr/share/java/resteasy/jaxrs-api.jar
 CLASSPATH=$CLASSPATH:/usr/share/java/resteasy/resteasy-atom-provider.jar
+CLASSPATH=$CLASSPATH:/usr/share/java/resteasy/resteasy-client.jar
 CLASSPATH=$CLASSPATH:/usr/share/java/resteasy/resteasy-jaxb-provider.jar
 CLASSPATH=$CLASSPATH:/usr/share/java/resteasy/resteasy-jaxrs.jar
 CLASSPATH=$CLASSPATH:/usr/share/java/resteasy/resteasy-jaxrs-jandex.jar
diff --git a/tests/dogtag/dev_java_tests/run_junit_tests.sh b/tests/dogtag/dev_java_tests/run_junit_tests.sh
index 31d647ca24c956d25c1562a627901d35a9e3ac23..7b298a4b7fced7f7c65dc2ea0e6e0428fe570a68 100644
--- a/tests/dogtag/dev_java_tests/run_junit_tests.sh
+++ b/tests/dogtag/dev_java_tests/run_junit_tests.sh
@@ -45,6 +45,7 @@ run_dev_junit_tests() {
     CLASSPATH=$CLASSPATH:/usr/share/java/jackson/jackson-xc.jar
     CLASSPATH=$CLASSPATH:/usr/share/java/resteasy/jaxrs-api.jar
     CLASSPATH=$CLASSPATH:/usr/share/java/resteasy/resteasy-atom-provider.jar
+    CLASSPATH=$CLASSPATH:/usr/share/java/resteasy/resteasy-client.jar
     CLASSPATH=$CLASSPATH:/usr/share/java/resteasy/resteasy-jaxb-provider.jar
     CLASSPATH=$CLASSPATH:/usr/share/java/resteasy/resteasy-jaxrs.jar
     CLASSPATH=$CLASSPATH:/usr/share/java/resteasy/resteasy-jaxrs-jandex.jar
-- 
1.8.4.2

