summaryrefslogtreecommitdiffstats
path: root/pki/base/common/src/com/netscape/cmscore/connector
diff options
context:
space:
mode:
Diffstat (limited to 'pki/base/common/src/com/netscape/cmscore/connector')
-rw-r--r--pki/base/common/src/com/netscape/cmscore/connector/HttpConnFactory.java315
-rw-r--r--pki/base/common/src/com/netscape/cmscore/connector/HttpConnection.java238
-rw-r--r--pki/base/common/src/com/netscape/cmscore/connector/HttpConnector.java198
-rw-r--r--pki/base/common/src/com/netscape/cmscore/connector/HttpPKIMessage.java221
-rw-r--r--pki/base/common/src/com/netscape/cmscore/connector/HttpRequestEncoder.java73
-rw-r--r--pki/base/common/src/com/netscape/cmscore/connector/LocalConnector.java203
-rw-r--r--pki/base/common/src/com/netscape/cmscore/connector/RemoteAuthority.java70
-rw-r--r--pki/base/common/src/com/netscape/cmscore/connector/RequestTransfer.java124
-rw-r--r--pki/base/common/src/com/netscape/cmscore/connector/Resender.java249
9 files changed, 1691 insertions, 0 deletions
diff --git a/pki/base/common/src/com/netscape/cmscore/connector/HttpConnFactory.java b/pki/base/common/src/com/netscape/cmscore/connector/HttpConnFactory.java
new file mode 100644
index 000000000..208fc5e7f
--- /dev/null
+++ b/pki/base/common/src/com/netscape/cmscore/connector/HttpConnFactory.java
@@ -0,0 +1,315 @@
+// --- 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.cmscore.connector;
+
+
+import com.netscape.certsrv.base.*;
+import com.netscape.certsrv.logging.*;
+import com.netscape.certsrv.apps.*;
+import com.netscape.certsrv.connector.*;
+import com.netscape.cmscore.connector.*;
+import com.netscape.certsrv.logging.*;
+import com.netscape.cmsutil.http.*;
+import com.netscape.cmsutil.net.*;
+import com.netscape.certsrv.authority.*;
+import java.util.*;
+import java.io.*;
+
+
+/**
+ * Factory for getting HTTP Connections to a HTTPO server
+ */
+public class HttpConnFactory {
+ protected int mMinConns = 1;
+ protected int mMaxConns = 30;
+
+ private ILogger mLogger = CMS.getLogger();
+
+ private int mNumConns = 0; // number of available conns in array
+ private int mTotal = 0; // total num conns
+ private IHttpConnection mMasterConn = null; // master connection object.
+ private IHttpConnection mConns[];
+ private IAuthority mSource = null;
+ private IRemoteAuthority mDest = null;
+ private String mNickname = "";
+ private int mTimeout = 0;
+
+ /**
+ * default value for the above at init time.
+ */
+ private boolean mDefErrorIfDown = false;
+
+ /**
+ * Constructor for initializing from the config store.
+ * must be followed by init(IConfigStore)
+ */
+ public HttpConnFactory() {
+ }
+
+ /**
+ * Constructor for HttpConnFactory
+ * @param minConns minimum number of connections to have available
+ * @param maxConns max number of connections to have available. This is
+ * @param serverInfo server connection info - host, port, etc.
+ */
+ public HttpConnFactory(int minConns, int maxConns, IAuthority source, IRemoteAuthority dest, String nickname, int timeout
+ ) throws EBaseException {
+
+ CMS.debug("In HttpConnFactory constructor mTimeout " + timeout);
+ mSource = source;
+ mDest = dest;
+ mNickname = nickname;
+ mTimeout = timeout;
+
+ init(minConns, maxConns);
+ }
+
+ /**
+ * initialize parameters obtained from either constructor or
+ * config store
+ * @param minConns minimum number of connection handls to have available.
+ * @param maxConns maximum total number of connections to ever have.
+ * @param connInfo ldap connection info.
+ * @param authInfo ldap authentication info.
+ * @exception ELdapException if any error occurs.
+ */
+ private void init(int minConns, int maxConns
+ )
+ throws EBaseException {
+
+ CMS.debug("min conns " + minConns + " maxConns " + maxConns);
+ if (minConns <= 0 || maxConns <= 0 || minConns > maxConns) {
+ CMS.debug("bad values from CMS.cfg");
+
+ } else {
+
+ mMinConns = minConns;
+ mMaxConns = maxConns;
+ }
+
+ CMS.debug("before creating httpconn array");
+
+ mConns = new IHttpConnection[mMaxConns];
+
+ // Create connection handle and make initial connection
+
+ CMS.debug("before makeConnection");
+
+ CMS.debug(
+ "initializing HttpConnFactory with mininum " + mMinConns + " and maximum " + mMaxConns +
+ " connections to ");
+
+ // initalize minimum number of connection handles available.
+ //makeMinimum();
+
+ CMS.debug("leaving HttpConnFactory init.");
+ }
+
+ private IHttpConnection createConnection() throws EBaseException {
+
+ IHttpConnection retConn = null;
+
+ CMS.debug("In HttpConnFactory.createConnection.");
+
+ try {
+ ISocketFactory tFactory = new JssSSLSocketFactory(mNickname);
+
+ if (mTimeout == 0) {
+ retConn = CMS.getHttpConnection(mDest, tFactory);
+ } else {
+ retConn = CMS.getHttpConnection(mDest, tFactory, mTimeout);
+ }
+
+ } catch (Exception e) {
+
+ CMS.debug("can't make new Htpp Connection");
+
+ throw new EBaseException(
+ "Can't create new Http Connection");
+ }
+
+ return retConn;
+ }
+
+ /**
+ * makes the minumum number of connections
+ */
+ private void makeMinimum() throws EBaseException {
+
+ CMS.debug("In HttpConnFactory.makeMinimum.");
+ int increment;
+
+ if (mNumConns < mMinConns && mTotal <= mMaxConns) {
+
+ increment = Math.min(mMinConns - mNumConns, mMaxConns - mTotal);
+
+ if (increment == 0)
+ return;
+
+ CMS.debug(
+ "increasing minimum connections by " + increment);
+ for (int i = increment - 1; i >= 0; i--) {
+ mConns[i] = (IHttpConnection) createConnection();
+ }
+ mTotal += increment;
+ mNumConns += increment;
+ CMS.debug("new total available http connections " + mTotal);
+ CMS.debug("new number of http connections " + mNumConns);
+ }
+ }
+
+ /**
+ * gets a conenction from this factory.
+ * All connections obtained from the factory must be returned by
+ * returnConn() method.
+ * The best thing to do is to put returnConn in a finally clause so it
+ * always gets called. For example,
+ * <pre>
+ * IHttpConnection c = null;
+ * try {
+ * c = factory.getConn();
+ * myclass.do_something_with_c(c);
+ * }
+ * catch (EBaseException e) {
+ * handle_error_here();
+ * }
+ * finally {
+ * factory.returnConn(c);
+ * }
+ * </pre>
+ */
+ public IHttpConnection getConn()
+ throws EBaseException {
+ return getConn(true);
+ }
+
+ /**
+ * Returns a Http connection - a clone of the master connection.
+ * All connections should be returned to the factory using returnConn()
+ * to recycle connection objects.
+ * If not returned the limited max number is affected but if that
+ * number is large not much harm is done.
+ * Returns null if maximum number of connections reached.
+ * The best thing to do is to put returnConn in a finally clause so it
+ * always gets called. For example,
+ * <pre>
+ * IHttpConnnection c = null;
+ * try {
+ * c = factory.getConn();
+ * myclass.do_something_with_c(c);
+ * }
+ * catch (EBaseException e) {
+ * handle_error_here();
+ * }
+ * finally {
+ * factory.returnConn(c);
+ * }
+ * </pre>
+ */
+ public synchronized IHttpConnection getConn(boolean waitForConn)
+ throws EBaseException {
+ boolean waited = false;
+
+ CMS.debug("In HttpConnFactory.getConn");
+ if (mNumConns == 0)
+ makeMinimum();
+ if (mNumConns == 0) {
+ if (!waitForConn)
+ return null;
+ try {
+ CMS.debug("getConn: out of http connections");
+ log(ILogger.LL_WARN,
+ "Ran out of http connections available "
+ );
+ waited = true;
+ CMS.debug("HttpConn:about to wait for a new http connection");
+ while (mNumConns == 0)
+ wait();
+
+ CMS.debug("HttpConn:done waiting for new http connection");
+ } catch (InterruptedException e) {
+ }
+ }
+ mNumConns--;
+ IHttpConnection conn = mConns[mNumConns];
+
+ mConns[mNumConns] = null;
+
+ if (waited) {
+ CMS.debug("HttpConn:had to wait for an available connection from pool");
+ log(ILogger.LL_WARN,
+ "Http connections are available again in http connection pool "
+ );
+ }
+ CMS.debug("HttpgetConn: mNumConns now " + mNumConns);
+
+ return conn;
+ }
+
+ /**
+ * Teturn connection to the factory.
+ * This is mandatory after a getConn().
+ * The best thing to do is to put returnConn in a finally clause so it
+ * always gets called. For example,
+ * <pre>
+ * IHttpConnection c = null;
+ * try {
+ * c = factory.getConn();
+ * myclass.do_something_with_c(c);
+ * }
+ * catch (EBaseException e) {
+ * handle_error_here();
+ * }
+ * finally {
+ * factory.returnConn(c);
+ * }
+ * </pre>
+ */
+ public synchronized void returnConn(IHttpConnection conn) {
+
+ CMS.debug("In HttpConnFactory.returnConn");
+ if (conn == null) {
+ return;
+ }
+ IHttpConnection boundconn = (IHttpConnection) conn;
+
+ for (int i = 0; i < mNumConns; i++) {
+ if (mConns[i] == conn) {
+ CMS.debug(
+ "returnConn: previously returned connection. " + conn);
+
+ }
+ }
+ mConns[mNumConns++] = boundconn;
+ CMS.debug("HttpreturnConn: mNumConns now " + mNumConns);
+ notify();
+ }
+
+ /**
+ * handy routine for logging in this class.
+ */
+ private void log(int level, String msg) {
+ mLogger.log(ILogger.EV_SYSTEM, ILogger.S_LDAP, level,
+ "In Http (bound) connection pool to" +
+ msg);
+ }
+
+ protected void finalize()
+ throws Exception {
+ }
+}
diff --git a/pki/base/common/src/com/netscape/cmscore/connector/HttpConnection.java b/pki/base/common/src/com/netscape/cmscore/connector/HttpConnection.java
new file mode 100644
index 000000000..7db508fb6
--- /dev/null
+++ b/pki/base/common/src/com/netscape/cmscore/connector/HttpConnection.java
@@ -0,0 +1,238 @@
+// --- 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.cmscore.connector;
+
+
+import com.netscape.cmsutil.http.*;
+import com.netscape.cmsutil.net.*;
+import com.netscape.certsrv.request.IRequest;
+import com.netscape.certsrv.connector.*;
+import com.netscape.certsrv.authority.*;
+import com.netscape.certsrv.base.*;
+import com.netscape.cmscore.util.Debug;
+import com.netscape.certsrv.apps.*;
+import java.io.*;
+import java.util.*;
+
+
+public class HttpConnection implements IHttpConnection {
+ protected IRemoteAuthority mDest = null;
+ protected HttpRequest mHttpreq = new HttpRequest();
+ protected IRequestEncoder mReqEncoder = null;
+ protected HttpClient mHttpClient = null;
+
+ protected boolean Connect(String host, HttpClient client)
+ {
+ StringTokenizer st = new StringTokenizer(host, " ");
+ while (st.hasMoreTokens()) {
+ String hp = st.nextToken(); // host:port
+ StringTokenizer st1 = new StringTokenizer(hp, ":");
+ try {
+ String h = st1.nextToken();
+ int p = Integer.parseInt(st1.nextToken());
+ client.connect(h, p);
+ return true;
+ } catch (Exception e) {
+ // may want to log the failure
+ }
+ try {
+ Thread.sleep(5000); // 5 seconds
+ } catch (Exception e) {
+ }
+
+ }
+ return false;
+ }
+
+ public HttpConnection(IRemoteAuthority dest, ISocketFactory factory) {
+ mDest = dest;
+ mReqEncoder = new HttpRequestEncoder();
+ mHttpClient = new HttpClient(factory);
+ if (Debug.ON)
+ Debug.trace("Created HttpClient");
+ try {
+ mHttpreq.setMethod("POST");
+ mHttpreq.setURI(mDest.getURI());
+ mHttpreq.setHeader("Connection", "Keep-Alive");
+ CMS.debug("HttpConnection: connecting to " + dest.getHost() + ":" + dest.getPort());
+ String host = dest.getHost();
+ // we could have a list of host names in the host parameters
+ // the format is, for example,
+ // "directory.knowledge.com:1050 people.catalog.com 199.254.1.2"
+ if (host != null && host.indexOf(' ') != -1) {
+ // try to do client-side failover
+ boolean connected = false;
+ do {
+ connected = Connect(host, mHttpClient);
+ } while (!connected);
+ } else {
+ mHttpClient.connect(host, dest.getPort());
+ }
+ CMS.debug("HttpConnection: connected to " + dest.getHost() + ":" + dest.getPort());
+ } catch (IOException e) {
+ // server's probably down. that's fine. try later.
+ //System.out.println(
+ //"Can't connect to server in connection creation");
+ }
+ }
+
+ // Inserted by beomsuk
+ public HttpConnection(IRemoteAuthority dest, ISocketFactory factory, int timeout) {
+ mDest = dest;
+ mReqEncoder = new HttpRequestEncoder();
+ mHttpClient = new HttpClient(factory);
+ CMS.debug("HttpConn:Created HttpConnection: factory " + factory + "client " + mHttpClient);
+ try {
+ mHttpreq.setMethod("POST");
+ mHttpreq.setURI(mDest.getURI());
+ mHttpreq.setHeader("Connection", "Keep-Alive");
+ CMS.debug("HttpConnection: connecting to " + dest.getHost() + ":" + dest.getPort() + " timeout:" + timeout);
+ mHttpClient.connect(dest.getHost(), dest.getPort(), timeout);
+ CMS.debug("HttpConnection: connected to " + dest.getHost() + ":" + dest.getPort() + " timeout:" + timeout);
+ } catch (IOException e) {
+ // server's probably down. that's fine. try later.
+ //System.out.println(
+ //"Can't connect to server in connection creation");
+ CMS.debug("CMSConn:IOException in creating HttpConnection " + e.toString());
+ }
+ }
+
+ // Insert end
+ /**
+ * sends a request to remote RA/CA, returning the result.
+ * @throws EBaseException if request could not be encoded
+ */
+ public IPKIMessage send(IPKIMessage tomsg)
+ throws EBaseException {
+ IPKIMessage replymsg = null;
+
+ CMS.debug("in HttpConnection.send " + this);
+ if (Debug.ON)
+ Debug.trace("encoding request ");
+ String content = null;
+
+ try {
+ content = mReqEncoder.encode(tomsg);
+ } catch (IOException e) {
+ throw new EBaseException(CMS.getUserMessage("CMS_BASE_INVALID_ATTRIBUTE", "Could not encode request"));
+ }
+ if (Debug.ON) {
+ Debug.trace("encoded request");
+ Debug.trace("------ " + content.length() + "-----");
+ Debug.trace(content);
+ Debug.trace("--------------------------");
+ }
+ boolean reconnect = false;
+
+ mHttpreq.setHeader("Content-Length",
+ Integer.toString(content.length()));
+ if (Debug.ON)
+ Debug.trace("request encoded length " + content.length());
+ mHttpreq.setContent(content);
+
+ HttpResponse p = null;
+
+ try {
+ if (!mHttpClient.connected()) {
+ mHttpClient.connect(mDest.getHost(), mDest.getPort());
+ CMS.debug("HttpConn:reconnected to " + mDest.getHost() + ":" + mDest.getPort());
+ reconnect = true;
+ }
+ } catch (IOException e) {
+ if (e.getMessage().indexOf("Peer's certificate issuer has been marked as not trusted") != -1) {
+ throw new EBaseException(CMS.getUserMessage("CMS_BASE_CONN_FAILED", "(This local authority cannot connect to the remote authority. The local authority's signing certificate must chain to a CA certificate trusted for client authentication in the certificate database. Use the certificate manager, or command line tool such as certutil to verify that the trust permissions of the local authority's issuer cert have 'CT' setting in the SSL client auth field.)"));
+ }
+ CMS.debug("HttpConn:Couldn't reconnect " + e);
+ throw new EBaseException(CMS.getUserMessage("CMS_BASE_CONN_FAILED", "Couldn't reconnect " + e));
+ }
+
+ // if remote closed connection want to reconnect and resend.
+ while (p == null) {
+ try {
+ if (Debug.ON)
+ Debug.trace("sending request");
+ p = mHttpClient.send(mHttpreq);
+ } catch (IOException e) {
+ CMS.debug("HttpConn: mHttpClient.send failed " + e.toString());
+ if (reconnect) {
+ CMS.debug("HttpConn:resend failed again. " + e);
+ throw new EBaseException(CMS.getUserMessage("CMS_BASE_CONN_FAILED", "resend failed again. " + e));
+ }
+ try {
+ CMS.debug("HttpConn: trying a reconnect ");
+ mHttpClient.connect(mDest.getHost(), mDest.getPort());
+ } catch (IOException ex) {
+ CMS.debug("reconnect for resend failed. " + ex);
+ throw new EBaseException(CMS.getUserMessage("CMS_BASE_CONN_FAILED", "reconnect for resend failed." + ex));
+ }
+ reconnect = true;
+ }
+ }
+
+ // got reply; check status
+ String statusStr = p.getStatusCode();
+
+ CMS.debug("HttpConn:server returned status " + statusStr);
+ int statuscode = -1;
+
+ try {
+ statuscode = Integer.parseInt(statusStr);
+ } catch (NumberFormatException e) {
+ statuscode = -1;
+ }
+
+ /* HttpServletResponse.SC_OK = 200 */
+ if (statuscode != 200) {
+
+ /* HttpServletResponse.SC_UNAUTHORIZED = 401 */
+ if (statuscode == 401) {
+ // XXX what to do here.
+ String msg = "request no good " + statuscode + " " + p.getReasonPhrase();
+
+ if (Debug.ON)
+ Debug.trace(msg);
+ throw new EBaseException(CMS.getUserMessage("CMS_BASE_AUTHENTICATE_FAILED", msg));
+ } else {
+ // XXX what to do here.
+ String msg = "HttpConn:request no good " + statuscode + " " + p.getReasonPhrase();
+
+ CMS.debug(msg);
+ throw new EBaseException(CMS.getUserMessage("CMS_BASE_INVALID_ATTRIBUTE", msg));
+ }
+ }
+
+ // decode reply.
+ // if reply is bad, error is thrown and request will be resent
+ String pcontent = p.getContent();
+
+ if (Debug.ON) {
+ Debug.trace("Server returned\n");
+ Debug.trace("-------");
+ Debug.trace(pcontent);
+ Debug.trace("-------");
+ }
+
+ try {
+ replymsg = (IPKIMessage) mReqEncoder.decode(pcontent);
+ } catch (IOException e) {
+ throw new EBaseException(CMS.getUserMessage("CMS_BASE_INVALID_ATTRIBUTE", "Could not decode content"));
+ }
+ CMS.debug("HttpConn:decoded reply");
+ return replymsg;
+ }
+}
diff --git a/pki/base/common/src/com/netscape/cmscore/connector/HttpConnector.java b/pki/base/common/src/com/netscape/cmscore/connector/HttpConnector.java
new file mode 100644
index 000000000..4ce3ed848
--- /dev/null
+++ b/pki/base/common/src/com/netscape/cmscore/connector/HttpConnector.java
@@ -0,0 +1,198 @@
+// --- 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.cmscore.connector;
+
+
+import com.netscape.certsrv.apps.*;
+import com.netscape.certsrv.request.*;
+import com.netscape.certsrv.base.*;
+import com.netscape.certsrv.connector.*;
+import com.netscape.certsrv.authority.*;
+import com.netscape.certsrv.logging.*;
+import com.netscape.cmsutil.http.*;
+import com.netscape.cmsutil.net.*;
+import java.util.*;
+
+
+public class HttpConnector implements IConnector {
+ protected IAuthority mSource = null;
+ protected IRemoteAuthority mDest = null;
+ protected ISocketFactory mFactory = null;
+
+ // XXX todo make this a pool.
+ // XXX use CMMF in the future.
+ protected IHttpConnection mConn = null;
+ private Thread mResendThread = null;
+ private IResender mResender = null;
+ private int mTimeout;
+
+ private HttpConnFactory mConnFactory = null;
+ public HttpConnector(IAuthority source, String nickName,
+ IRemoteAuthority dest, int resendInterval, IConfigStore config) throws EBaseException {
+
+ mTimeout = 0;
+ mSource = source;
+ mDest = dest;
+ mFactory = new JssSSLSocketFactory(nickName);
+
+ int minConns = config.getInteger("minHttpConns", 1);
+ int maxConns = config.getInteger("maxHttpConns", 15);
+
+ CMS.debug("HttpConn: min " + minConns);
+ CMS.debug("HttpConn: max " + maxConns);
+
+ try {
+ mConnFactory = new HttpConnFactory(minConns, maxConns, source, dest, nickName, 0);
+ } catch (EBaseException e) {
+ CMS.debug("can't create new HttpConnFactory " + e.toString());
+ }
+
+ // mConn = CMS.getHttpConnection(dest, mFactory);
+ // this will start resending past requests in parallel.
+ mResender = CMS.getResender(mSource, nickName, dest, resendInterval);
+ mResendThread = new Thread(mResender, "HttpConnector");
+ }
+
+ // Inserted by beomsuk
+ public HttpConnector(IAuthority source, String nickName,
+ IRemoteAuthority dest, int resendInterval, IConfigStore config, int timeout) throws EBaseException {
+ mSource = source;
+ mDest = dest;
+ mTimeout = timeout;
+ mFactory = new JssSSLSocketFactory(nickName);
+
+ int minConns = config.getInteger("minHttpConns", 1);
+ int maxConns = config.getInteger("maxHttpConns", 15);
+
+ CMS.debug("HttpConn: min " + minConns);
+ CMS.debug("HttpConn: max " + maxConns);
+
+ try {
+ mConnFactory = new HttpConnFactory(minConns, maxConns, source, dest, nickName, timeout);
+ } catch (EBaseException e) {
+ CMS.debug("can't create new HttpConnFactory");
+ }
+
+ // this will start resending past requests in parallel.
+ mResender = CMS.getResender(mSource, nickName, dest, resendInterval);
+ mResendThread = new Thread(mResender, "HttpConnector");
+ }
+
+ // Insert end
+
+ public boolean send(IRequest r)
+ throws EBaseException {
+ IHttpConnection curConn = null;
+
+ try {
+ IHttpPKIMessage tomsg = (IHttpPKIMessage) CMS.getHttpPKIMessage();
+ HttpPKIMessage replymsg = null;
+
+ tomsg.fromRequest(r);
+ CMS.debug("Before synch");
+
+ curConn = mConnFactory.getConn();
+
+ CMS.debug("HttpConnector.send " + curConn);
+
+ replymsg = (HttpPKIMessage) curConn.send(tomsg);
+
+ if (replymsg == null) {
+ CMS.debug("HttpConncter. replymsg is null");
+ return false;
+ }
+
+ CMS.debug("HttpConncter.send has been called");
+
+ RequestStatus replyStatus;
+ RequestId replyRequestId;
+
+ replyStatus = RequestStatus.fromString(replymsg.reqStatus);
+ int index = replymsg.reqId.lastIndexOf(':');
+
+ replyRequestId = new RequestId(replymsg.reqId.substring(index + 1));
+ CMS.debug("reply request id " + replyRequestId);
+ r.setExtData(IRequest.REMOTE_REQID, replyRequestId.toString());
+
+ CMS.debug("reply request type " + r.getRequestType());
+ CMS.debug("reply status " + replyStatus);
+
+ // non terminal states.
+ // XXX hack: don't resend get revocation info requests since
+ // resent results are ignored.
+ if ((!r.getRequestType().equals(
+ IRequest.GETREVOCATIONINFO_REQUEST)) &&
+ (replyStatus == RequestStatus.BEGIN ||
+ replyStatus == RequestStatus.PENDING ||
+ replyStatus == RequestStatus.SVC_PENDING ||
+ replyStatus == RequestStatus.APPROVED)) {
+ CMS.debug("HttpConn: remote request id still pending " +
+ r.getRequestId() + " state " + replyStatus);
+ mSource.log(ILogger.LL_INFO, CMS.getLogMessage("CMSCORE_CONNECTOR_REQUEST_NOT_COMPLETED", r.getRequestId().toString()));
+ mResender.addRequest(r);
+ return false;
+ }
+
+ // request was completed.
+ replymsg.toRequest(r); // this only copies contents.
+
+ // terminal states other than completed
+ if (replyStatus == RequestStatus.REJECTED ||
+ replyStatus == RequestStatus.CANCELED) {
+ CMS.debug(
+ "remote request id " + r.getRequestId() +
+ " was rejected or cancelled.");
+ r.setExtData(IRequest.REMOTE_STATUS, replyStatus.toString());
+ r.setExtData(IRequest.RESULT, IRequest.RES_ERROR);
+ r.setExtData(IRequest.ERROR,
+ new EBaseException(CMS.getUserMessage("CMS_BASE_REMOTE_AUTHORITY_ERROR")));
+ // XXX overload svcerrors for now.
+ Vector policyErrors = r.getExtDataInStringVector(IRequest.ERRORS);
+
+ if (policyErrors != null && policyErrors.size() > 0) {
+ r.setExtData(IRequest.SVCERRORS, policyErrors);
+ }
+ }
+
+ CMS.debug(
+ "remote request id " + r.getRequestId() + " was completed");
+ return true;
+ } catch (EBaseException e) {
+ CMS.debug("HttpConn: inside EBaseException " + e.toString());
+
+ if (!r.getRequestType().equals(IRequest.GETREVOCATIONINFO_REQUEST))
+ mResender.addRequest(r);
+
+ CMS.debug("HttpConn: error sending request to cert " + e.toString());
+ mSource.log(ILogger.LL_FAILURE, CMS.getLogMessage("CMSCORE_CONNECTOR_SEND_REQUEST", r.getRequestId().toString(), mDest.getHost(), Integer.toString(mDest.getPort())));
+ // mSource.log(ILogger.LL_INFO,
+ // "Queing " + r.getRequestId() + " for resend.");
+ return false;
+ } finally {
+
+ if (curConn != null) {
+ mConnFactory.returnConn(curConn);
+ }
+ }
+ }
+
+ public void start() {
+ mResendThread.start();
+ }
+
+}
diff --git a/pki/base/common/src/com/netscape/cmscore/connector/HttpPKIMessage.java b/pki/base/common/src/com/netscape/cmscore/connector/HttpPKIMessage.java
new file mode 100644
index 000000000..b9e32cbd9
--- /dev/null
+++ b/pki/base/common/src/com/netscape/cmscore/connector/HttpPKIMessage.java
@@ -0,0 +1,221 @@
+// --- 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.cmscore.connector;
+
+
+import com.netscape.certsrv.request.IRequest;
+import com.netscape.certsrv.connector.*;
+import com.netscape.certsrv.apps.*;
+import com.netscape.cmscore.util.Debug;
+import java.util.*;
+import java.io.*;
+
+
+/**
+ * simple name/value pair message.
+ */
+public class HttpPKIMessage implements IHttpPKIMessage {
+ // initialized to "" because nulls don't serialize well.
+ public String reqType = "";
+ public String reqId = "";
+ protected String reqStatus = "";
+ protected Vector mNameVals = new Vector(); // sequence of name/vals.
+
+ public HttpPKIMessage() {
+ }
+
+ public String getReqStatus() {
+ return reqStatus;
+ }
+
+ public String getReqType() {
+ return reqType;
+ }
+
+ public String getReqId() {
+ return reqId;
+ }
+
+ /**
+ * copy contents of request to make a simple name/value message.
+ */
+ public void fromRequest(IRequest r) {
+ // actually don't need to copy source id since
+ reqType = r.getRequestType();
+ reqId = r.getRequestId().toString();
+ reqStatus = r.getRequestStatus().toString();
+
+ CMS.debug("HttpPKIMessage.fromRequest: requestId=" + r.getRequestId().toString() + " requestStatus=" + reqStatus + " instance=" + r);
+
+ String attrs[] = RequestTransfer.getTransferAttributes(r);
+ int len = attrs.length;
+ String[] names = attrs;
+ Object value = null;
+
+ for (int i = 0; i < len; i++) {
+ String key = names[i];
+ if (r.isSimpleExtDataValue(key)) {
+ value = r.getExtDataInString(key);
+ } else {
+ value = r.getExtDataInHashtable(key);
+ }
+ if (value != null) {
+ mNameVals.addElement(key);
+ mNameVals.addElement(value);
+ }
+ }
+ }
+
+ /**
+ * copy contents to request.
+ */
+ public void toRequest(IRequest r) {
+ // id, type and status
+ // type had to have been set in instantiation.
+ // id is checked but not reset.
+ // request status cannot be set, but can be looked at.
+ reqStatus = r.getRequestStatus().toString();
+ CMS.debug("HttpPKMessage.toRequest: requestStatus=" + reqStatus);
+
+ int len = RequestTransfer.getTransferAttributes(r).length;
+ String key;
+ Object value;
+ Enumeration enum1 = mNameVals.elements();
+
+ while (enum1.hasMoreElements()) {
+ key = (String) enum1.nextElement();
+ try {
+ value = enum1.nextElement();
+ if (value instanceof String) {
+ r.setExtData(key, (String) value);
+ } else if (value instanceof Hashtable) {
+ r.setExtData(key, (Hashtable) value);
+ } else {
+ CMS.debug("HttpPKIMessage.toRequest(): key: " + key +
+ " has unexpected type " + value.getClass().toString());
+ }
+ } catch (NoSuchElementException e) {
+ CMS.debug("Incorrect pairing of name/value for " + key);
+ }
+ }
+ }
+
+ private void writeObject(java.io.ObjectOutputStream out)
+ throws IOException {
+ CMS.debug("writeObject");
+ out.writeObject(reqType);
+ if (Debug.ON)
+ Debug.trace("read object req type " + reqType);
+ out.writeObject(reqId);
+ if (Debug.ON)
+ Debug.trace("read object req id " + reqId);
+ out.writeObject(reqStatus);
+ if (Debug.ON)
+ Debug.trace("read object req source status " + reqStatus);
+ Enumeration enum1 = mNameVals.elements();
+
+ while (enum1.hasMoreElements()) {
+ Object key = null;
+ Object val = null;
+ key = enum1.nextElement();
+ try {
+ val = enum1.nextElement();
+ // test if key and value are serializable
+ ObjectOutputStream os =
+ new ObjectOutputStream(new ByteArrayOutputStream());
+ os.writeObject(key);
+ os.writeObject(val);
+
+ // ok, if we dont have problem serializing the objects,
+ // then write the objects into the real object stream
+ out.writeObject(key);
+ out.writeObject(val);
+ } catch (Exception e) {
+ // skip not serialiable attribute in DRM
+ // DRM does not need to store the enrollment request anymore
+ CMS.debug("HttpPKIMessage:skipped key=" +
+ key.getClass().getName());
+ if (val == null) {
+ CMS.debug("HttpPKIMessage:skipped val= null");
+ } else {
+ CMS.debug("HttpPKIMessage:skipped val=" +
+ val.getClass().getName());
+ }
+ }
+ }
+ }
+
+ private void readObject(java.io.ObjectInputStream in)
+ throws IOException, ClassNotFoundException, OptionalDataException {
+ reqType = (String) in.readObject();
+ reqId = (String) in.readObject();
+ reqStatus = (String) in.readObject();
+ mNameVals = new Vector();
+ Object keyorval = null;
+
+ try {
+ boolean iskey = true;
+
+ while (true) {
+ boolean skipped = false;
+ try {
+ keyorval = in.readObject();
+ } catch (OptionalDataException e) {
+ throw e;
+ } catch (IOException e) {
+ // just skipped parameter
+ CMS.debug("skipped attribute in request e="+e);
+ if (!iskey) {
+ int s = mNameVals.size();
+ if (s > 0) {
+ // remove previous key if this is value
+ mNameVals.removeElementAt(s - 1);
+ skipped = true;
+ keyorval = "";
+ }
+ }
+ }
+ if (iskey) {
+ if (Debug.ON)
+ Debug.trace("read key " + keyorval);
+ iskey = false;
+ } else {
+ if (Debug.ON)
+ Debug.trace("read val " + keyorval);
+ iskey = true;
+ }
+ if (Debug.ON)
+ Debug.trace("read " + keyorval);
+ if (!skipped) {
+ if (keyorval == null)
+ break;
+ mNameVals.addElement(keyorval);
+ }
+ }
+ } catch (OptionalDataException e) {
+ if (e.eof == true) {
+ if (Debug.ON)
+ Debug.trace("end of stream");
+ } else {
+ if (Debug.ON)
+ Debug.trace(" " + e.length);
+ throw e;
+ }
+ }
+ }
+}
diff --git a/pki/base/common/src/com/netscape/cmscore/connector/HttpRequestEncoder.java b/pki/base/common/src/com/netscape/cmscore/connector/HttpRequestEncoder.java
new file mode 100644
index 000000000..50b09fd2a
--- /dev/null
+++ b/pki/base/common/src/com/netscape/cmscore/connector/HttpRequestEncoder.java
@@ -0,0 +1,73 @@
+// --- 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.cmscore.connector;
+
+
+import com.netscape.certsrv.request.*;
+import com.netscape.certsrv.connector.*;
+import com.netscape.cmscore.util.Debug;
+
+import java.io.*;
+
+
+/**
+ * encodes a request by serializing it.
+ */
+public class HttpRequestEncoder implements IRequestEncoder {
+ public String encode(Object r)
+ throws IOException {
+ String s = null;
+ byte[] serial;
+ ByteArrayOutputStream ba = new ByteArrayOutputStream();
+ ObjectOutputStream os = new ObjectOutputStream(ba);
+
+ os.writeObject(r);
+ serial = ba.toByteArray();
+ s = com.netscape.osutil.OSUtil.BtoA(serial);
+ return s;
+ }
+
+ public Object decode(String s)
+ throws IOException {
+ Object result = null;
+ byte[] serial = null;
+
+ try {
+
+ serial = com.netscape.osutil.OSUtil.AtoB(s);
+ ByteArrayInputStream ba = new ByteArrayInputStream(serial);
+ ObjectInputStream is = new ObjectInputStream(ba);
+
+ result = is.readObject();
+ } catch (ClassNotFoundException e) {
+ // XXX hack: change this
+ if (Debug.ON)
+ Debug.trace("class not found ex " + e + e.getMessage());
+ throw new IOException("Class Not Found " + e.getMessage());
+ } catch (OptionalDataException e) {
+ if (e.eof == true) {
+ if (Debug.ON)
+ Debug.trace("done reading input stream " + result);
+ } else {
+ if (Debug.ON)
+ Debug.trace(e.length + " more bytes of primitive data");
+ }
+ }
+ return result;
+ }
+}
diff --git a/pki/base/common/src/com/netscape/cmscore/connector/LocalConnector.java b/pki/base/common/src/com/netscape/cmscore/connector/LocalConnector.java
new file mode 100644
index 000000000..efc97c435
--- /dev/null
+++ b/pki/base/common/src/com/netscape/cmscore/connector/LocalConnector.java
@@ -0,0 +1,203 @@
+// --- 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.cmscore.connector;
+
+
+import java.util.*;
+import com.netscape.cmscore.util.*;
+import com.netscape.certsrv.request.*;
+import com.netscape.certsrv.connector.*;
+import com.netscape.certsrv.authority.*;
+import com.netscape.certsrv.base.*;
+import com.netscape.certsrv.logging.*;
+import com.netscape.certsrv.apps.*;
+
+
+public class LocalConnector implements IConnector {
+ ILogger mLogger = CMS.getLogger();
+ ICertAuthority mSource = null;
+ IAuthority mDest = null;
+ Hashtable mSourceReqs = new Hashtable();
+
+ public LocalConnector(ICertAuthority source, IAuthority dest) {
+ mSource = source;
+ // mSource.log(ILogger.LL_DEBUG, "Local connector setup for source " +
+ // mSource.getId());
+ mDest = dest;
+ CMS.debug("Local connector setup for dest " +
+ mDest.getId());
+ // register for events.
+ mDest.registerRequestListener(new LocalConnListener());
+ CMS.debug("Connector inited");
+ }
+
+ /**
+ * send request to local authority.
+ * returns resulting request
+ */
+ public boolean send(IRequest r) throws EBaseException {
+ if (Debug.ON) {
+ Debug.print("send request type " + r.getRequestType() + " status=" + r.getRequestStatus() + " to " + mDest.getId() + " id=" + r.getRequestId() + "\n");
+ }
+ CMS.debug("send request type " + r.getRequestType() +
+ " to " + mDest.getId());
+
+ IRequestQueue destQ = mDest.getRequestQueue();
+ IRequest destreq = destQ.newRequest(r.getRequestType());
+
+ CMS.debug("local connector dest req " +
+ destreq.getRequestId() + " created for source rId " + r.getRequestId());
+ // mSource.log(ILogger.LL_DEBUG,
+ // "setting connector dest " + mDest.getId() +
+ // " source id to " + r.getRequestId());
+
+ // XXX set context to the real identity later.
+ destreq.setSourceId(
+ mSource.getX500Name().toString() + ":" + r.getRequestId().toString());
+ //destreq.copyContents(r); // copy meta attributes in request.
+ transferRequest(r, destreq);
+ // XXX requestor type is not transferred on return.
+ destreq.setExtData(IRequest.REQUESTOR_TYPE,
+ IRequest.REQUESTOR_RA);
+ CMS.debug("connector dest " + mDest.getId() +
+ " processing " + destreq.getRequestId());
+
+ // set context before calling process request so
+ // that request subsystem can record the creator
+ // of the request
+ SessionContext s = SessionContext.getContext();
+
+ if (s.get(SessionContext.USER_ID) == null) {
+ // use $local$ to represent it is not a user who
+ // submit the request, but it is a local subsystem
+ s.put(SessionContext.USER_ID, "$local$" + mSource.getId());
+ }
+
+ // Locally cache the source request so that we
+ // can update it when the dest request is
+ // processed (when LocalConnListener is being called).
+ mSourceReqs.put(r.getRequestId().toString(), r);
+ try {
+ destQ.processRequest(destreq);
+ } catch (EBaseException ex) {
+ throw ex;
+ } finally {
+ // release the source id either success or failure
+ mSourceReqs.remove(r.getRequestId().toString());
+ }
+
+ CMS.debug("connector dest " + mDest.getId() +
+ " processed " + destreq.getRequestId() +
+ " status " + destreq.getRequestStatus());
+
+ if (destreq.getRequestStatus() == RequestStatus.COMPLETE) {
+ // no need to transfer contents if request wasn't complete.
+ transferRequest(destreq, r);
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ public class LocalConnListener implements IRequestListener {
+
+ public void init(ISubsystem sys, IConfigStore config)
+ throws EBaseException {
+ }
+
+ public void set(String name, String val) {
+ }
+
+ public void accept(IRequest destreq) {
+ if (Debug.ON) {
+ Debug.print("dest " + mDest.getId() + " done with " + destreq.getRequestId());
+ }
+ CMS.debug(
+ "dest " + mDest.getId() + " done with " + destreq.getRequestId());
+
+ IRequestQueue sourceQ = mSource.getRequestQueue();
+ // accept requests that only belong to us.
+ // XXX review death scenarios here. - If system dies anywhere
+ // here need to check all requests at next server startup.
+ String sourceNameAndId = destreq.getSourceId();
+ String sourceName = mSource.getX500Name().toString();
+
+ if (sourceNameAndId == null ||
+ !sourceNameAndId.toString().regionMatches(0,
+ sourceName, 0, sourceName.length())) {
+ CMS.debug("request " + destreq.getRequestId() +
+ " from " + sourceNameAndId + " not ours.");
+ return;
+ }
+ int index = sourceNameAndId.lastIndexOf(':');
+
+ if (index == -1) {
+ mSource.log(ILogger.LL_FAILURE,
+ "request " + destreq.getRequestId() +
+ " for " + sourceNameAndId + " malformed.");
+ return;
+ }
+ String sourceId = sourceNameAndId.substring(index + 1);
+ RequestId rId = new RequestId(sourceId);
+
+ // mSource.log(ILogger.LL_DEBUG, mDest.getId() + " " +
+ // destreq.getRequestId() + " mapped to " + mSource.getId() + " " + rId);
+
+ IRequest r = null;
+
+ // 391439: Previously, we try to access the request
+ // via request queue here. Due to the recent
+ // performance enhancement, approved request will
+ // not be immediately available in the database. So
+ // retrieving the request from the queue within
+ // the serviceRequest() function will have
+ // diffculities.
+ // You may wonder what happen if the system crashes
+ // during the request servicing. Yes, the request
+ // will be lost. This is ok because the users will
+ // resubmit their requests again.
+ // Note that the pending requests, on the other hand,
+ // are persistent before the servicing.
+ // Please see stateEngine() function in
+ // ARequestQueue.java for details.
+ r = (IRequest) mSourceReqs.get(rId);
+ if (r != null) {
+ if (r.getRequestStatus() != RequestStatus.SVC_PENDING) {
+ mSource.log(ILogger.LL_FAILURE,
+ "request state of " + rId + "not pending " +
+ " from dest authority " + mDest.getId());
+ sourceQ.releaseRequest(r);
+ return;
+ }
+ transferRequest(destreq, r);
+ sourceQ.markAsServiced(r);
+ sourceQ.releaseRequest(r);
+
+ CMS.debug("released request " + r.getRequestId());
+ }
+ }
+ }
+
+ public void start() {
+ }
+
+ protected void transferRequest(IRequest src, IRequest dest) {
+ RequestTransfer.transfer(src, dest);
+ }
+}
+
diff --git a/pki/base/common/src/com/netscape/cmscore/connector/RemoteAuthority.java b/pki/base/common/src/com/netscape/cmscore/connector/RemoteAuthority.java
new file mode 100644
index 000000000..6a9703b69
--- /dev/null
+++ b/pki/base/common/src/com/netscape/cmscore/connector/RemoteAuthority.java
@@ -0,0 +1,70 @@
+// --- 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.cmscore.connector;
+
+
+import com.netscape.certsrv.base.*;
+import com.netscape.certsrv.connector.*;
+
+
+public class RemoteAuthority implements IRemoteAuthority {
+ String mHost = null;
+ int mPort = -1;
+ String mURI = null;
+ int mTimeout = 0;
+
+ /**
+ * host parameter can be:
+ * "directory.knowledge.com"
+ * "199.254.1.2"
+ * "directory.knowledge.com:1050 people.catalog.com 199.254.1.2"
+ */
+ public RemoteAuthority(String host, int port, String uri, int timeout) {
+ mHost = host;
+ mPort = port;
+ mURI = uri;
+ mTimeout = timeout;
+ }
+
+ public RemoteAuthority() {
+ }
+
+ public void init(IConfigStore c)
+ throws EBaseException {
+ mHost = c.getString("host");
+ mPort = c.getInteger("port");
+ mURI = c.getString("uri");
+ mTimeout = c.getInteger("timeout");
+ }
+
+ public String getHost() {
+ return mHost;
+ }
+
+ public int getPort() {
+ return mPort;
+ }
+
+ public String getURI() {
+ return mURI;
+ }
+
+ public int getTimeout() {
+ return mTimeout;
+ }
+}
diff --git a/pki/base/common/src/com/netscape/cmscore/connector/RequestTransfer.java b/pki/base/common/src/com/netscape/cmscore/connector/RequestTransfer.java
new file mode 100644
index 000000000..825f4d564
--- /dev/null
+++ b/pki/base/common/src/com/netscape/cmscore/connector/RequestTransfer.java
@@ -0,0 +1,124 @@
+// --- 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.cmscore.connector;
+
+
+import java.util.*;
+
+import com.netscape.certsrv.request.*;
+import com.netscape.certsrv.apps.*;
+
+import com.netscape.cmscore.authentication.ChallengePhraseAuthentication;
+
+
+public class RequestTransfer {
+
+ private static final String DOT = ".";
+
+ private static String[] transferAttributes = {
+ IRequest.HTTP_PARAMS,
+ IRequest.AGENT_PARAMS,
+ IRequest.CERT_INFO,
+ IRequest.ISSUED_CERTS,
+ IRequest.OLD_CERTS,
+ IRequest.OLD_SERIALS,
+ IRequest.REVOKED_CERTS,
+ IRequest.CACERTCHAIN,
+ IRequest.CRL,
+ IRequest.ERRORS,
+ IRequest.RESULT,
+ IRequest.ERROR,
+ IRequest.SVCERRORS,
+ IRequest.REMOTE_STATUS,
+ IRequest.REMOTE_REQID,
+ IRequest.REVOKED_CERT_RECORDS,
+ IRequest.CERT_STATUS,
+ ChallengePhraseAuthentication.CHALLENGE_PHRASE,
+ ChallengePhraseAuthentication.SUBJECTNAME,
+ ChallengePhraseAuthentication.SERIALNUMBER,
+ ChallengePhraseAuthentication.SERIALNOARRAY,
+ IRequest.ISSUERDN,
+ IRequest.CERT_FILTER,
+ "keyRecord",
+ "uid", // UidPwdDirAuthentication.CRED_UID,
+ "udn", // UdnPwdDirAuthentication.CRED_UDN,
+ };
+
+ public static boolean isProfileRequest(IRequest request) {
+ String profileId = request.getExtDataInString("profileId");
+
+ if (profileId == null || profileId.equals(""))
+ return false;
+ else
+ return true;
+ }
+
+ public static String[] getTransferAttributes(IRequest r) {
+ if (isProfileRequest(r)) {
+ // copy everything in the request
+ CMS.debug("RequestTransfer: profile request " +
+ r.getRequestId().toString());
+ Enumeration e = r.getExtDataKeys();
+ Vector v = new Vector();
+
+ while (e.hasMoreElements()) {
+ String k = (String) e.nextElement();
+
+ if (k.equals("requestType"))
+ continue;
+ if (k.equals("requestId"))
+ continue;
+ if (k.equals("requestVersion"))
+ continue;
+ if (k.equals("AUTH_TOKEN"))
+ continue;
+ CMS.debug("RequestTransfer: attribute=" + k);
+ if (k.equals("requestStatus")) {
+ CMS.debug("RequestTransfer : requestStatus=" +
+ r.getExtDataInString("requestStatus"));
+ }
+ v.addElement(k);
+ }
+ CMS.debug("RequestTransfer: attribute size=" + v.size());
+ if (v.size() == 0)
+ return null;
+ String attrs[] = new String[v.size()];
+
+ v.copyInto(attrs);
+ return attrs;
+ } else {
+ return transferAttributes;
+ }
+ }
+
+ public static void transfer(IRequest src, IRequest dest) {
+ CMS.debug("Transfer srcId=" +
+ src.getRequestId().toString() +
+ " destId=" + dest.getRequestId().toString());
+ String attrs[] = getTransferAttributes(src);
+
+ for (int i = 0; i < attrs.length; i++) {
+ String key = attrs[i];
+ if (src.isSimpleExtDataValue(key)) {
+ dest.setExtData(key, src.getExtDataInString(key));
+ } else {
+ dest.setExtData(key, src.getExtDataInHashtable(key));
+ }
+ }
+ }
+}
diff --git a/pki/base/common/src/com/netscape/cmscore/connector/Resender.java b/pki/base/common/src/com/netscape/cmscore/connector/Resender.java
new file mode 100644
index 000000000..78fddaf5c
--- /dev/null
+++ b/pki/base/common/src/com/netscape/cmscore/connector/Resender.java
@@ -0,0 +1,249 @@
+// --- 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.cmscore.connector;
+
+
+import com.netscape.certsrv.base.*;
+import com.netscape.certsrv.apps.*;
+import com.netscape.certsrv.connector.*;
+import com.netscape.certsrv.authority.*;
+import com.netscape.certsrv.request.*;
+import com.netscape.certsrv.authentication.*;
+import com.netscape.certsrv.logging.*;
+import com.netscape.cmscore.util.Debug;
+import com.netscape.cmsutil.http.*;
+
+import java.util.Vector;
+import java.util.Enumeration;
+import java.io.*;
+
+
+/**
+ * Resend requests at intervals to the server to check if it's been completed.
+ * Default interval is 5 minutes.
+ */
+public class Resender implements IResender {
+ public static final int SECOND = 1000; //milliseconds
+ public static final int MINUTE = 60 * SECOND;
+ public static final int HOUR = 60 * MINUTE;
+ public static final int DAY = 24 * HOUR;
+
+ protected IAuthority mAuthority = null;
+ IRequestQueue mQueue = null;
+ protected IRemoteAuthority mDest = null;
+
+ /* Vector of Request Id *Strings* */
+ protected Vector mRequestIds = new Vector();
+
+ protected HttpConnection mConn = null;
+
+ protected String mNickName = null;
+
+ // default interval.
+ // XXX todo add another interval for requests unsent because server
+ // was down (versus being serviced in request queue)
+ protected int mInterval = 1 * MINUTE;
+
+ public Resender(IAuthority authority, String nickName, IRemoteAuthority dest) {
+ mAuthority = authority;
+ mQueue = mAuthority.getRequestQueue();
+ mDest = dest;
+ mNickName = nickName;
+
+ //mConn = new HttpConnection(dest,
+ // new JssSSLSocketFactory(nickName));
+ }
+
+ public Resender(
+ IAuthority authority, String nickName,
+ IRemoteAuthority dest, int interval) {
+ mAuthority = authority;
+ mQueue = mAuthority.getRequestQueue();
+ mDest = dest;
+ if (interval > 0)
+ mInterval = interval * SECOND; // interval specified in seconds.
+
+ //mConn = new HttpConnection(dest,
+ // new JssSSLSocketFactory(nickName));
+ }
+
+ // must be done after a subsystem 'start' so queue is initialized.
+ private void initRequests() {
+ mQueue = mAuthority.getRequestQueue();
+ // get all requests in mAuthority that are still pending.
+ IRequestList list =
+ mQueue.listRequestsByStatus(RequestStatus.SVC_PENDING);
+
+ while (list != null && list.hasMoreElements()) {
+ RequestId rid = list.nextRequestId();
+
+ CMS.debug(
+ "added request Id " + rid + " in init to resend queue.");
+ // note these are added as strings
+ mRequestIds.addElement(rid.toString());
+ }
+ }
+
+ public void addRequest(IRequest r) {
+ synchronized (mRequestIds) {
+ // note the request ids are added as strings.
+ mRequestIds.addElement(r.getRequestId().toString());
+ }
+ CMS.debug(
+ "added " + r.getRequestId() + " to resend queue");
+ }
+
+ public void run() {
+
+ CMS.debug("Resender: In resender Thread run:");
+ mConn = new HttpConnection(mDest,
+ new JssSSLSocketFactory(mNickName));
+ initRequests();
+
+ do {
+ resend();
+ try {
+ Thread.sleep(mInterval);
+ } catch (InterruptedException e) {
+ mAuthority.log(ILogger.LL_INFO, CMS.getLogMessage("CMSCORE_CONNECTOR_RESENDER_INTERRUPTED"));
+ continue;
+ }
+ }
+ while (true);
+ }
+
+ private void resend() {
+ // clone a seperate list so mRequestIds can be modified
+ Vector rids = (Vector) mRequestIds.clone();
+ Vector completedRids = new Vector();
+
+ // resend each request to CA to ping for status.
+ Enumeration enum1 = rids.elements();
+
+ while (enum1.hasMoreElements()) {
+ // request ids are added as strings.
+ String ridString = (String) enum1.nextElement();
+ RequestId rid = new RequestId(ridString);
+ IRequest r = null;
+
+ CMS.debug(
+ "resend processing request id " + rid);
+
+ try {
+ r = mQueue.findRequest(rid);
+ } catch (EBaseException e) {
+ // XXX bad case. should we remove the rid now ?
+ mAuthority.log(ILogger.LL_WARN, CMS.getLogMessage("CMSCORE_CONNECTOR_REQUEST_NOT_FOUND", rid.toString()));
+ continue;
+ }
+ try {
+ if (r.getRequestStatus() != RequestStatus.SVC_PENDING) {
+ // request not pending anymore - aborted or cancelled.
+ completedRids.addElement(rid);
+ CMS.debug(
+ "request id " + rid + " no longer service pending");
+ } else {
+ boolean completed = send(r);
+
+ if (completed) {
+ completedRids.addElement(rid);
+ mAuthority.log(ILogger.LL_INFO, CMS.getLogMessage("CMSCORE_CONNECTOR_REQUEST_COMPLETED", rid.toString()));
+ }
+ }
+ } catch (IOException e) {
+ mAuthority.log(ILogger.LL_WARN, CMS.getLogMessage("CMSCORE_CONNECTOR_REQUEST_ERROR", rid.toString(), e.toString()));
+ } catch (EBaseException e) {
+ // if connection is down, don't send the remaining request
+ // as it will sure fail.
+ mAuthority.log(ILogger.LL_WARN, CMS.getLogMessage("CMSCORE_CONNECTOR_DOWN"));
+ if (e.toString().indexOf("connection not available")
+ >= 0)
+ break;
+ }
+ }
+
+ // remove completed ones from list so they won't be resent.
+ Enumeration en = completedRids.elements();
+
+ synchronized (mRequestIds) {
+ while (en.hasMoreElements()) {
+ RequestId id = (RequestId) en.nextElement();
+
+ CMS.debug(
+ "Connector: Removed request " + id + " from re-send queue");
+ mRequestIds.removeElement(id.toString());
+ CMS.debug(
+ "Connector: mRequestIds now has " +
+ mRequestIds.size() + " elements.");
+ }
+ }
+ }
+
+ // this is almost the same as connector's send.
+ private boolean send(IRequest r)
+ throws IOException, EBaseException {
+ IRequest reply = null;
+
+ try {
+ HttpPKIMessage tomsg = new HttpPKIMessage();
+ HttpPKIMessage replymsg = null;
+
+ tomsg.fromRequest(r);
+ replymsg = (HttpPKIMessage) mConn.send(tomsg);
+ if(replymsg==null)
+ return false;
+ CMS.debug(
+ r.getRequestId() + " resent to CA");
+
+ RequestStatus replyStatus =
+ RequestStatus.fromString(replymsg.reqStatus);
+ int index = replymsg.reqId.lastIndexOf(':');
+ RequestId replyRequestId =
+ new RequestId(replymsg.reqId.substring(index + 1));
+
+ if (Debug.ON)
+ Debug.trace("reply request id " + replyRequestId +
+ " for request " + r.getRequestId());
+
+ if (replyStatus != RequestStatus.COMPLETE) {
+ CMS.debug("resend " +
+ r.getRequestId() + " still not completed.");
+ return false;
+ }
+
+ // request was completed. copy relevant contents.
+ replymsg.toRequest(r);
+ if (Debug.ON)
+ Debug.trace("resend request id was completed " + r.getRequestId());
+ mQueue.markAsServiced(r);
+ mQueue.releaseRequest(r);
+ CMS.debug(
+ "resend released request " + r.getRequestId());
+ return true;
+ } catch (EBaseException e) {
+ // same as not having sent it, so still want to resend.
+ mAuthority.log(ILogger.LL_FAILURE, CMS.getLogMessage("CMSCORE_CONNECTOR_RESEND_ERROR", r.getRequestId().toString(), e.toString()));
+ if (e.toString().indexOf("Connection refused by peer") > 0)
+ throw new EBaseException("connection not available");
+ }
+ return false;
+
+ }
+
+}
+