You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@juneau.apache.org by ja...@apache.org on 2016/08/01 00:08:29 UTC

[40/51] [partial] incubator-juneau git commit: Initial Juno contents from IBM JazzHub repo

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/deprecated/CertificateStore.java
----------------------------------------------------------------------
diff --git a/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/deprecated/CertificateStore.java b/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/deprecated/CertificateStore.java
new file mode 100755
index 0000000..f06eaf5
--- /dev/null
+++ b/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/deprecated/CertificateStore.java
@@ -0,0 +1,127 @@
+/*******************************************************************************
+ * Licensed Materials - Property of IBM
+ * (c) Copyright IBM Corporation 2010, 2015. All Rights Reserved.
+ *
+ * The source code for this program is not published or otherwise
+ * divested of its trade secrets, irrespective of what has been
+ * deposited with the U.S. Copyright Office.
+ *******************************************************************************/
+package com.ibm.juno.client.deprecated;
+
+import java.io.*;
+import java.security.*;
+import java.security.cert.*;
+import java.security.cert.Certificate;
+import java.util.*;
+
+/**
+ * Specialized certificate storage based on {@link KeyStore} for managing trusted certificates.
+ */
+@Deprecated // Use SimpleX509TrustManager
+public class CertificateStore {
+
+	private final KeyStore keyStore;
+
+	/**
+	 * Get the underlying KeyStore.
+	 */
+	KeyStore getKeyStore() {
+		return keyStore;
+	}
+
+	/**
+	 * Helper method that creates a {@link KeyStore} by reading it from a file.
+	 */
+	static KeyStore load(File file, String password) throws NoSuchAlgorithmException, CertificateException, IOException, KeyStoreException {
+		KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
+		try {
+			InputStream input = new FileInputStream(file);
+			try {
+				ks.load(input, password == null ? null : password.toCharArray());
+			} finally {
+				input.close();
+			}
+		} catch (IOException e) {
+			// Return an empty initialized KeyStore
+			ks.load(null, null);
+		}
+		return ks;
+	}
+
+	/**
+	 * Helper method that writes a {@link KeyStore} to a file.
+	 */
+	static void store(KeyStore ks, File file, String password) throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException {
+		OutputStream output = new FileOutputStream(file);
+		try {
+			ks.store(output, password == null ? null : password.toCharArray());
+		} finally {
+			output.close();
+		}
+	}
+
+	/**
+	 * Helper to compute a unique alias within the trust store for a specified certificate.
+	 * @param cert The certificate to compute an alias for.
+	 */
+	static String computeAlias(Certificate cert) {
+		// There appears to be no standard way to construct certificate aliases,
+		// but this class never depends on looking up a certificate by its
+		// computed alias, so just create an alias that's unique and be done.
+		return UUID.randomUUID().toString();
+	}
+
+	/**
+	 * Construct a new TrustStore initially containing no certificates.
+	 */
+	public CertificateStore() throws NoSuchAlgorithmException, CertificateException, IOException {
+		try {
+			keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
+		} catch (KeyStoreException e) {
+			// If the code above caused a KeyStoreException, then the JVM classpath is probably messed up.
+			throw new RuntimeException("KeyStoreException: ["+e.getLocalizedMessage()+"]. "
+				+ "Likely cause is that the Java Cryptography Extension libraries are missing from the JRE classpath.  "
+				+ "Make sure %JAVA_HOME%/lib/ext is specified in your JVM's java.ext.dirs system property.");
+		}
+		keyStore.load(null, null);
+	}
+
+	/**
+	 * Does the trust store contain the specified certificate?
+	 */
+	public boolean containsCertificate(Certificate cert) throws KeyStoreException {
+		return (keyStore.getCertificateAlias(cert) != null);
+	}
+
+	/**
+	 * Enter the specified certificate into the trust store.
+	 */
+	public void enterCertificate(Certificate cert) throws KeyStoreException {
+		if (! containsCertificate(cert))
+			keyStore.setCertificateEntry(computeAlias(cert), cert);
+	}
+
+	/*
+	 * Helper to copy all the certificate entries, and none of the other
+	 * entries, from a {@link KeyStore} into the trust store.
+	 */
+	private void enterCertificates(KeyStore ks) throws KeyStoreException {
+		for (Enumeration<String> e = ks.aliases(); e.hasMoreElements();) {
+			String alias = e.nextElement();
+			if (ks.isCertificateEntry(alias)) {
+				Certificate cert = ks.getCertificate(alias);
+				enterCertificate(cert);
+			}
+		}
+	}
+
+	/**
+	 * Load the specified {@link KeyStore} file and copy all of the certificates
+	 * it contains into the trust store. Only certificates, and not any other
+	 * entries, are loaded.
+	 */
+	public void loadCertificates(File file, String password) throws NoSuchAlgorithmException, CertificateException, KeyStoreException, IOException {
+		KeyStore ks = load(file, password);
+		enterCertificates(ks);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/deprecated/ICertificateValidator$Trust.class
----------------------------------------------------------------------
diff --git a/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/deprecated/ICertificateValidator$Trust.class b/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/deprecated/ICertificateValidator$Trust.class
new file mode 100755
index 0000000..3051b31
Binary files /dev/null and b/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/deprecated/ICertificateValidator$Trust.class differ

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/deprecated/ICertificateValidator.class
----------------------------------------------------------------------
diff --git a/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/deprecated/ICertificateValidator.class b/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/deprecated/ICertificateValidator.class
new file mode 100755
index 0000000..d470011
Binary files /dev/null and b/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/deprecated/ICertificateValidator.class differ

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/deprecated/ICertificateValidator.java
----------------------------------------------------------------------
diff --git a/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/deprecated/ICertificateValidator.java b/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/deprecated/ICertificateValidator.java
new file mode 100755
index 0000000..0ee07e8
--- /dev/null
+++ b/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/deprecated/ICertificateValidator.java
@@ -0,0 +1,46 @@
+/*******************************************************************************
+ * Licensed Materials - Property of IBM
+ * (c) Copyright IBM Corporation 2010, 2015. All Rights Reserved.
+ *
+ * The source code for this program is not published or otherwise
+ * divested of its trade secrets, irrespective of what has been
+ * deposited with the U.S. Copyright Office.
+ *******************************************************************************/
+package com.ibm.juno.client.deprecated;
+
+import java.security.cert.*;
+
+/**
+ * Validator of certificates presented by a server when establishing an SSL
+ * connection.
+ */
+@Deprecated // Use SimpleX509TrustManager
+public interface ICertificateValidator {
+
+	/** Action to take for a server-supplied certificate. */
+	public enum Trust {
+
+		/** Do not accept the certificate. */
+		REJECT,
+
+		/** Accept the certificate temporarily for the current connection. */
+		ACCEPT_CONNECTION,
+
+		/** Accept the certificate temporarily for the current session. */
+		ACCEPT_SESSION,
+
+		/** Accept the certificate permanently, by saving it in the user's trust store.*/
+		ACCEPT_PERMANENT
+	}
+
+	/**
+	 * There is a problem accepting the server-supplied certificate. What should
+	 * be done?
+	 *
+	 * @param cert The problematic certificate presented by the server
+	 * @param problem The {@link CertificateException} that may indicate the specific
+	 * 	problem with the certificate, e.g. {@link CertificateExpiredException}.
+	 * @return The disposition on the certificate.
+	 */
+	Trust validate(X509Certificate cert, CertificateException problem);
+}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/deprecated/ITrustStoreProvider.class
----------------------------------------------------------------------
diff --git a/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/deprecated/ITrustStoreProvider.class b/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/deprecated/ITrustStoreProvider.class
new file mode 100755
index 0000000..923672e
Binary files /dev/null and b/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/deprecated/ITrustStoreProvider.class differ

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/deprecated/ITrustStoreProvider.java
----------------------------------------------------------------------
diff --git a/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/deprecated/ITrustStoreProvider.java b/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/deprecated/ITrustStoreProvider.java
new file mode 100755
index 0000000..47256a9
--- /dev/null
+++ b/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/deprecated/ITrustStoreProvider.java
@@ -0,0 +1,39 @@
+/*******************************************************************************
+ * Licensed Materials - Property of IBM
+ * (c) Copyright IBM Corporation 2010, 2015. All Rights Reserved.
+ *
+ * The source code for this program is not published or otherwise
+ * divested of its trade secrets, irrespective of what has been
+ * deposited with the U.S. Copyright Office.
+ *******************************************************************************/
+package com.ibm.juno.client.deprecated;
+
+import java.io.*;
+import java.security.*;
+import java.security.cert.*;
+import java.security.cert.Certificate;
+
+/**
+ * Utility class for handling certificate stores.
+ */
+@Deprecated // Use SimpleX509TrustManager
+public interface ITrustStoreProvider {
+
+	/**
+	 * Returns the store of all certificates trusted for the lifetime
+	 * of this trust provider
+	 */
+	CertificateStore getSessionTrustStore();
+
+	/**
+	 * Returns the store of all permanently trusted certificates.
+	 */
+	CertificateStore getRuntimeTrustStore();
+
+    /**
+     * Install a certificate in the user's application-specific on-disk key
+     * store, if possible.
+     */
+    public void installCertificate(Certificate cert) throws NoSuchAlgorithmException, CertificateException, KeyStoreException, IOException;
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/deprecated/LenientCertificateValidator.class
----------------------------------------------------------------------
diff --git a/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/deprecated/LenientCertificateValidator.class b/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/deprecated/LenientCertificateValidator.class
new file mode 100755
index 0000000..ad456a0
Binary files /dev/null and b/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/deprecated/LenientCertificateValidator.class differ

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/deprecated/LenientCertificateValidator.java
----------------------------------------------------------------------
diff --git a/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/deprecated/LenientCertificateValidator.java b/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/deprecated/LenientCertificateValidator.java
new file mode 100755
index 0000000..a12ca43
--- /dev/null
+++ b/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/deprecated/LenientCertificateValidator.java
@@ -0,0 +1,26 @@
+/*******************************************************************************
+ * Licensed Materials - Property of IBM
+ * (c) Copyright IBM Corporation 2010, 2015. All Rights Reserved.
+ *
+ * The source code for this program is not published or otherwise
+ * divested of its trade secrets, irrespective of what has been
+ * deposited with the U.S. Copyright Office.
+ *******************************************************************************/
+package com.ibm.juno.client.deprecated;
+
+import java.security.cert.*;
+
+/**
+ * Lenient certificate validator that always accepts invalid certificates.
+ */
+@Deprecated // Use SimpleX509TrustManager
+public final class LenientCertificateValidator implements ICertificateValidator {
+
+	/** Singleton */
+	public static final ICertificateValidator INSTANCE = new LenientCertificateValidator();
+
+	@Override /* ICertificateValidator */
+	public Trust validate(X509Certificate certificate, CertificateException problem) {
+		return Trust.ACCEPT_CONNECTION;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/deprecated/SharedTrustStoreProvider.class
----------------------------------------------------------------------
diff --git a/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/deprecated/SharedTrustStoreProvider.class b/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/deprecated/SharedTrustStoreProvider.class
new file mode 100755
index 0000000..e68e4c7
Binary files /dev/null and b/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/deprecated/SharedTrustStoreProvider.class differ

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/deprecated/SharedTrustStoreProvider.java
----------------------------------------------------------------------
diff --git a/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/deprecated/SharedTrustStoreProvider.java b/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/deprecated/SharedTrustStoreProvider.java
new file mode 100755
index 0000000..149302d
--- /dev/null
+++ b/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/deprecated/SharedTrustStoreProvider.java
@@ -0,0 +1,104 @@
+/*******************************************************************************
+ * Licensed Materials - Property of IBM
+ * (c) Copyright IBM Corporation 2010, 2015. All Rights Reserved.
+ *
+ * The source code for this program is not published or otherwise
+ * divested of its trade secrets, irrespective of what has been
+ * deposited with the U.S. Copyright Office.
+ *******************************************************************************/
+package com.ibm.juno.client.deprecated;
+
+import java.io.*;
+import java.security.*;
+import java.security.cert.*;
+import java.security.cert.Certificate;
+
+/**
+ * Trust store provider with shared static certificate stores.
+ */
+@Deprecated // Use SimpleX509TrustManager
+public final class SharedTrustStoreProvider implements ITrustStoreProvider {
+
+	// In-memory trust store of all certificates explicitly accepted by the
+	// certificate validator during this session. The validator will not be
+	// called again during this session for any of these certificates. These may
+	// include expired, not yet valid, or otherwise untrusted certificates.
+	// These are kept distinctly, rather than merged into the runtime trust
+	// store, because the base trust manager will never accept expired, etc.
+	// certificates, even if from a trusted source.
+	private static CertificateStore sessionCerts;
+
+	// In-memory trust store of all permanently trusted certificates, assembled
+	// from a number of key store files. These are provided to the base trust
+	// manager as the basis for its decision making.
+	private static CertificateStore runtimeCerts;
+
+	// Location and password of the user's private trust store for this application.
+	private static String userTrustStoreLocation;
+	private static String userTrustStorePassword;
+
+	static {
+		init();
+	}
+
+	private static final void init() {
+		try {
+			String userHome = System.getProperty("user.home");
+			String javaHome = System.getProperty("java.home");
+
+			userTrustStoreLocation = userHome + "/.jazzcerts";
+			userTrustStorePassword = "ibmrationaljazz";
+
+			sessionCerts = new CertificateStore();
+
+			runtimeCerts = new CertificateStore();
+
+			// JRE keystore override
+			String file = System.getProperty("javax.net.ssl.trustStore");
+			String password = System.getProperty("javax.net.ssl.trustStorePassword");
+			addCertificatesFromStore(runtimeCerts, file, password);
+
+			// JRE Signer CA keystore
+			file = javaHome + "/lib/security/cacerts";
+			addCertificatesFromStore(runtimeCerts, file, null);
+
+			// JRE Secure Site CA keystore
+			file =  (javaHome + "/lib/security/jssecacerts");
+			addCertificatesFromStore(runtimeCerts, file, null);
+
+			// Application-specific keystore for the current user
+			addCertificatesFromStore(runtimeCerts, userTrustStoreLocation, userTrustStorePassword);
+
+		} catch (Exception e) {
+			e.printStackTrace();
+		}
+	}
+
+	private static void addCertificatesFromStore(CertificateStore store, String file, String password) {
+		try {
+			File f = new File(file);
+			if (f.canRead())
+				store.loadCertificates(f, password);
+		} catch (Exception e) {
+			// Discard errors
+		}
+	}
+
+	@Override /* ITrustStoreProvider */
+	public CertificateStore getRuntimeTrustStore() {
+		return runtimeCerts;
+	}
+
+	@Override /* ITrustStoreProvider */
+	public CertificateStore getSessionTrustStore() {
+		return sessionCerts;
+	}
+
+	@Override /* ITrustStoreProvider */
+	public void installCertificate(Certificate cert) throws NoSuchAlgorithmException, CertificateException, KeyStoreException, IOException {
+		File  f = new File(userTrustStoreLocation);
+		KeyStore ks = CertificateStore.load(f, userTrustStorePassword);
+		ks.setCertificateEntry(CertificateStore.computeAlias(cert), cert);
+		CertificateStore.store(ks, f, userTrustStorePassword);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/deprecated/ValidatingX509TrustManager$1.class
----------------------------------------------------------------------
diff --git a/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/deprecated/ValidatingX509TrustManager$1.class b/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/deprecated/ValidatingX509TrustManager$1.class
new file mode 100755
index 0000000..1673c74
Binary files /dev/null and b/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/deprecated/ValidatingX509TrustManager$1.class differ

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/deprecated/ValidatingX509TrustManager.class
----------------------------------------------------------------------
diff --git a/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/deprecated/ValidatingX509TrustManager.class b/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/deprecated/ValidatingX509TrustManager.class
new file mode 100755
index 0000000..b06f337
Binary files /dev/null and b/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/deprecated/ValidatingX509TrustManager.class differ

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/deprecated/ValidatingX509TrustManager.java
----------------------------------------------------------------------
diff --git a/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/deprecated/ValidatingX509TrustManager.java b/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/deprecated/ValidatingX509TrustManager.java
new file mode 100755
index 0000000..a7539fe
--- /dev/null
+++ b/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/deprecated/ValidatingX509TrustManager.java
@@ -0,0 +1,113 @@
+/*******************************************************************************
+ * Licensed Materials - Property of IBM
+ * (c) Copyright IBM Corporation 2010, 2015. All Rights Reserved.
+ *
+ * The source code for this program is not published or otherwise
+ * divested of its trade secrets, irrespective of what has been
+ * deposited with the U.S. Copyright Office.
+ *******************************************************************************/
+package com.ibm.juno.client.deprecated;
+
+import java.io.*;
+import java.security.*;
+import java.security.cert.*;
+
+import javax.net.ssl.*;
+
+/**
+ * A trust manager that will call a registered {@link ICertificateValidator} in
+ * the event that a problematic (e.g. expired, not yet valid) or untrusted
+ * certificate is presented by a server, and react appropriately. This trust
+ * manager will rely on multiple key stores, and manage one of its own. The
+ * managed key store and the session-accepted key store are shared by all trust
+ * manager instances.
+ */
+@Deprecated // Use SimpleX509TrustManager
+public final class ValidatingX509TrustManager implements X509TrustManager {
+
+	// The JRE-provided trust manager used to validate certificates presented by a server.
+	private X509TrustManager baseTrustManager;
+
+	// The registered certificate validator, may be null, called when the base
+	// trust manager rejects a certificate presented by a server.
+	private ICertificateValidator validator;
+
+	private ITrustStoreProvider trustStoreProvider;
+
+	/**
+	 * Construct a new ValidatingX509TrustManager.
+	 *
+	 * @param validator Certificate validator to consult regarding problematic
+	 * 	certificates, or <code>null</code> to always reject them.
+	 */
+	public ValidatingX509TrustManager(ICertificateValidator validator) throws KeyStoreException, NoSuchAlgorithmException {
+		this.validator = validator;
+		this.trustStoreProvider = new SharedTrustStoreProvider();
+
+		// Initialize the base X509 trust manager that will be used to evaluate
+		// certificates presented by the server against the runtime trust store.
+		TrustManagerFactory factory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
+		factory.init(trustStoreProvider.getRuntimeTrustStore().getKeyStore());
+		TrustManager[] managers = factory.getTrustManagers();
+		for (TrustManager manager : managers) {
+			if (manager instanceof X509TrustManager) {
+				baseTrustManager = (X509TrustManager) manager; // Take the first X509TrustManager we find
+				return;
+			}
+		}
+		throw new IllegalStateException("Couldn't find JRE's X509TrustManager"); //$NON-NLS-1$
+	}
+
+	@Override /* X509TrustManager */
+	public X509Certificate[] getAcceptedIssuers() {
+		return baseTrustManager.getAcceptedIssuers();
+	}
+
+	@Override /* X509TrustManager */
+	public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
+		baseTrustManager.checkClientTrusted(chain, authType);
+	}
+
+	@Override /* X509TrustManager */
+	public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
+		X509Certificate cert = chain[0];
+
+		// Has the certificate been OK'd for the session?
+		try {
+			if (trustStoreProvider.getSessionTrustStore().containsCertificate(cert))
+				return;
+		} catch (KeyStoreException e) {
+			// Ignore; proceed to try base trust manager
+		}
+
+		try {
+			// Rely on the base trust manager to check the certificate against the assembled runtime key store
+			baseTrustManager.checkServerTrusted(chain, authType);
+		} catch (CertificateException certEx) {
+
+			// Done if there isn't a validator to consult
+			if (validator == null)
+				throw certEx; // Rejected!
+
+			// Ask the registered certificate validator to rule on the certificate
+			ICertificateValidator.Trust disposition = validator.validate(cert, certEx);
+			switch (disposition) {
+				case REJECT:				throw certEx;
+				case ACCEPT_CONNECTION: break;
+				case ACCEPT_SESSION:		enterCertificate(cert, false); break;
+				case ACCEPT_PERMANENT:	enterCertificate(cert, true); break;
+			}
+		}
+	}
+
+	private void enterCertificate(X509Certificate cert, boolean permanent) throws CertificateException {
+		try {
+			trustStoreProvider.getSessionTrustStore().enterCertificate(cert);
+			if (permanent)
+				trustStoreProvider.installCertificate(cert);
+		} catch (KeyStoreException e) {
+		} catch (NoSuchAlgorithmException e) {
+		} catch (IOException e) {
+		}
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/jazz/JazzRestClient.class
----------------------------------------------------------------------
diff --git a/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/jazz/JazzRestClient.class b/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/jazz/JazzRestClient.class
new file mode 100755
index 0000000..5fc0b7e
Binary files /dev/null and b/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/jazz/JazzRestClient.class differ

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/jazz/JazzRestClient.java
----------------------------------------------------------------------
diff --git a/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/jazz/JazzRestClient.java b/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/jazz/JazzRestClient.java
new file mode 100755
index 0000000..58f3017
--- /dev/null
+++ b/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/jazz/JazzRestClient.java
@@ -0,0 +1,390 @@
+/*******************************************************************************
+ * Licensed Materials - Property of IBM
+ * (c) Copyright IBM Corporation 2014, 2015. All Rights Reserved.
+ *
+ * The source code for this program is not published or otherwise
+ * divested of its trade secrets, irrespective of what has been
+ * deposited with the U.S. Copyright Office.
+ *******************************************************************************/
+package com.ibm.juno.client.jazz;
+
+import static org.apache.http.HttpStatus.*;
+
+import java.io.*;
+import java.net.*;
+import java.util.*;
+
+import org.apache.http.*;
+import org.apache.http.auth.*;
+import org.apache.http.client.*;
+import org.apache.http.client.config.*;
+import org.apache.http.client.entity.*;
+import org.apache.http.client.methods.*;
+import org.apache.http.impl.client.*;
+import org.apache.http.message.*;
+import org.apache.http.util.*;
+
+import com.ibm.juno.client.*;
+import com.ibm.juno.core.parser.*;
+import com.ibm.juno.core.serializer.*;
+import com.ibm.juno.core.utils.*;
+
+/**
+ * Specialized {@link RestClient} for working with Jazz servers.
+ * <p>
+ * Provides support for BASIC, FORM, and OIDC authentication against Jazz servers and simple SSL certificate validation.
+ *
+ * <h6 class='topic'>Additional Information</h6>
+ * <ul>
+ * 	<li><a class='doclink' href='package-summary.html#RestClient'>com.ibm.juno.client.jazz &gt; Jazz REST client API</a> for more information and code examples.
+ * </ul>
+ * @author James Bognar (jbognar@us.ibm.com)
+ */
+public class JazzRestClient extends RestClient {
+
+	private String user, pw;
+	private URI jazzUri;
+	private SSLOpts sslOpts;
+	private String cookie = null;
+
+	/**
+	 * Create a new client with no serializer or parser.
+	 *
+	 * @param jazzUrl The URL of the Jazz server being connected to (e.g. <js>"https://localhost:9443/jazz"</js>)
+	 * @param sslOpts SSL options.
+	 * @param user The Jazz username.
+	 * @param pw The Jazz password.
+	 * @throws IOException If a problem occurred trying to authenticate against the Jazz server.
+	 */
+	public JazzRestClient(String jazzUrl, SSLOpts sslOpts, String user, String pw) throws IOException {
+		super();
+		this.user = user;
+		this.pw = pw;
+		if (! jazzUrl.endsWith("/"))
+			jazzUrl = jazzUrl + "/";
+		this.sslOpts = sslOpts;
+		jazzUri = URI.create(jazzUrl);
+	}
+
+	/**
+	 * Create a new client with no serializer or parser, and LAX SSL support.
+	 *
+	 * @param jazzUrl The URL of the Jazz server being connected to (e.g. <js>"https://localhost:9443/jazz"</js>)
+	 * @param user The Jazz username.
+	 * @param pw The Jazz password.
+	 * @throws IOException
+	 */
+	public JazzRestClient(String jazzUrl, String user, String pw) throws IOException {
+		this(jazzUrl, SSLOpts.LAX, user, pw);
+	}
+
+	/**
+	 * Create a new client with the specified serializer and parser instances.
+	 *
+	 * @param jazzUri The URI of the Jazz server being connected to (e.g. <js>"https://localhost:9443/jazz"</js>)
+	 * @param sslOpts SSL options.
+	 * @param user The Jazz username.
+	 * @param pw The Jazz password.
+	 * @param s The serializer for converting POJOs to HTTP request message body text.
+	 * @param p The parser for converting HTTP response message body text to POJOs.
+	 * @throws IOException If a problem occurred trying to authenticate against the Jazz server.
+	 */
+	public JazzRestClient(String jazzUri, SSLOpts sslOpts, String user, String pw, Serializer<?> s, Parser<?> p) throws IOException {
+		this(jazzUri, sslOpts, user, pw);
+		setParser(p);
+		setSerializer(s);
+	}
+
+	/**
+	 * Create a new client with the specified serializer and parser instances and LAX SSL support.
+	 *
+	 * @param jazzUri The URI of the Jazz server being connected to (e.g. <js>"https://localhost:9443/jazz"</js>)
+	 * @param user The Jazz username.
+	 * @param pw The Jazz password.
+	 * @param s The serializer for converting POJOs to HTTP request message body text.
+	 * @param p The parser for converting HTTP response message body text to POJOs.
+	 * @throws IOException If a problem occurred trying to authenticate against the Jazz server.
+	 */
+	public JazzRestClient(String jazzUri, String user, String pw, Serializer<?> s, Parser<?> p) throws IOException {
+		this(jazzUri, SSLOpts.LAX, user, pw);
+		setParser(p);
+		setSerializer(s);
+	}
+
+	/**
+	 * Create a new client with the specified serializer and parser classes.
+	 *
+	 * @param jazzUri The URI of the Jazz server being connected to (e.g. <js>"https://localhost:9443/jazz"</js>)
+	 * @param sslOpts SSL options.
+	 * @param user The Jazz username.
+	 * @param pw The Jazz password.
+	 * @param s The serializer for converting POJOs to HTTP request message body text.
+	 * @param p The parser for converting HTTP response message body text to POJOs.
+	 * @throws IOException If a problem occurred trying to authenticate against the Jazz server.
+	 * @throws InstantiationException If serializer or parser could not be instantiated.
+	 */
+	public JazzRestClient(String jazzUri, SSLOpts sslOpts, String user, String pw, Class<? extends Serializer<?>> s, Class<? extends Parser<?>> p) throws InstantiationException, IOException {
+		this(jazzUri, sslOpts, user, pw);
+		setParser(p);
+		setSerializer(s);
+	}
+
+	/**
+	 * Create a new client with the specified serializer and parser classes and LAX SSL support.
+	 *
+	 * @param jazzUri The URI of the Jazz server being connected to (e.g. <js>"https://localhost:9443/jazz"</js>)
+	 * @param user The Jazz username.
+	 * @param pw The Jazz password.
+	 * @param s The serializer for converting POJOs to HTTP request message body text.
+	 * @param p The parser for converting HTTP response message body text to POJOs.
+	 * @throws IOException If a problem occurred trying to authenticate against the Jazz server.
+	 * @throws InstantiationException If serializer or parser could not be instantiated.
+	 */
+	public JazzRestClient(String jazzUri, String user, String pw, Class<? extends Serializer<?>> s, Class<? extends Parser<?>> p) throws InstantiationException, IOException {
+		this(jazzUri, SSLOpts.LAX, user, pw);
+		setParser(p);
+		setSerializer(s);
+	}
+
+	@Override /* RestClient */
+	protected CloseableHttpClient createHttpClient() throws Exception {
+		try {
+			if (jazzUri.getScheme().equals("https"))
+				enableSSL(sslOpts);
+
+			setRedirectStrategy(new AllowAllRedirects());
+
+			// See wi 368181. The PublicSuffixDomainFilter uses a default PublicSuffixMatcher
+			// that rejects hostnames lacking a dot, such as "ccmserver", so needed
+			// cookies don't get put on outgoing requests.
+			// Here, we create a cookie spec registry with handlers that don't have a PublicSuffixMatcher.
+			if (! Boolean.getBoolean("com.ibm.team.repository.transport.client.useDefaultPublicSuffixMatcher")) { //$NON-NLS-1$
+				// use a lenient PublicSuffixDomainFilter
+				setDefaultCookieSpecRegistry(CookieSpecRegistries.createDefault(null));
+			}
+
+			// We want to use a fresh HttpClientBuilder since the default implementation
+			// uses an unshared PoolingConnectionManager, and if you close the client
+			// and create a new one, can cause a "java.lang.IllegalStateException: Connection pool shut down"
+			CloseableHttpClient client = createHttpClientBuilder().build();
+
+			// Tomcat will respond with SC_BAD_REQUEST (or SC_REQUEST_TIMEOUT?) when the
+			// j_security_check URL is visited before an authenticated URL has been visited.
+			visitAuthenticatedURL(client);
+
+			// Authenticate against the server.
+			String authMethod = determineAuthMethod(client);
+			if (authMethod.equals("FORM")) {
+				formBasedAuthenticate(client);
+				visitAuthenticatedURL(client);
+			} else if (authMethod.equals("BASIC")) {
+				AuthScope scope = new AuthScope(jazzUri.getHost(), jazzUri.getPort());
+				Credentials up = new UsernamePasswordCredentials(user, pw);
+				CredentialsProvider p = new BasicCredentialsProvider();
+				p.setCredentials(scope, up);
+				setDefaultCredentialsProvider(p);
+				client.close();
+				client = getHttpClientBuilder().build();
+			} else if (authMethod.equals("OIDC")) {
+				oidcAuthenticate(client);
+				client.close();
+				client = getHttpClientBuilder().build();
+			}
+
+			return client;
+		} catch (Exception e) {
+			throw e;
+		} catch (Throwable e) {
+			throw new RuntimeException(e);
+		}
+	}
+
+	@Override /* RestClient */
+	protected HttpClientBuilder createHttpClientBuilder() {
+		HttpClientBuilder b = super.createHttpClientBuilder();
+
+		// See wi 368181. The PublicSuffixDomainFilter uses a default PublicSuffixMatcher
+		// that rejects hostnames lacking a dot, such as "ccmserver", so needed
+		// cookies don't get put on outgoing requests.
+		// Here, we create a cookie spec registry with handlers that don't have a PublicSuffixMatcher.
+		if (! Boolean.getBoolean("com.ibm.team.repository.transport.client.useDefaultPublicSuffixMatcher"))
+			b.setDefaultCookieSpecRegistry(CookieSpecRegistries.createDefault(null));
+
+		return b;
+	}
+
+
+	/**
+	 * Performs form-based authentication against the Jazz server.
+	 */
+	private void formBasedAuthenticate(HttpClient client) throws IOException {
+
+		URI uri2 = jazzUri.resolve("j_security_check");
+		HttpPost request = new HttpPost(uri2);
+		request.setConfig(RequestConfig.custom().setRedirectsEnabled(false).build());
+		 // Charset must explicitly be set to UTF-8 to handle user/pw with non-ascii characters.
+		request.addHeader("Content-Type", "application/x-www-form-urlencoded; charset=utf-8");
+
+		NameValuePairs params = new NameValuePairs()
+			.append(new BasicNameValuePair("j_username", user))
+			.append(new BasicNameValuePair("j_password", pw));
+		request.setEntity(new UrlEncodedFormEntity(params));
+
+		HttpResponse response = client.execute(request);
+		try {
+			int rc = response.getStatusLine().getStatusCode();
+
+			Header authMsg = response.getFirstHeader("X-com-ibm-team-repository-web-auth-msg");
+			if (authMsg != null)
+				throw new IOException(authMsg.getValue());
+
+			// The form auth request should always respond with a 200 ok or 302 redirect code
+			if (rc == SC_MOVED_TEMPORARILY) {
+				if (response.getFirstHeader("Location").getValue().matches("^.*/auth/authfailed.*$"))
+					throw new IOException("Invalid credentials.");
+			} else if (rc != SC_OK) {
+				throw new IOException("Unexpected HTTP status: " + rc);
+			}
+		} finally {
+			EntityUtils.consume(response.getEntity());
+		}
+	}
+
+	private void oidcAuthenticate(HttpClient client) throws IOException {
+
+		HttpGet request = new HttpGet(jazzUri);
+		request.setConfig(RequestConfig.custom().setRedirectsEnabled(false).build());
+
+		 // Charset must explicitly be set to UTF-8 to handle user/pw with non-ascii characters.
+		request.addHeader("Content-Type", "application/x-www-form-urlencoded; charset=utf-8");
+
+		HttpResponse response = client.execute(request);
+		try {
+			int code = response.getStatusLine().getStatusCode();
+
+			// Already authenticated
+			if (code == SC_OK)
+				return;
+
+			if (code != SC_UNAUTHORIZED)
+				throw new RestCallException("Unexpected response during OIDC authentication: " + response.getStatusLine());
+
+			//'x-jsa-authorization-redirect'
+			String redirectUri = getHeader(response, "X-JSA-AUTHORIZATION-REDIRECT");
+
+			if (redirectUri == null)
+				throw new RestCallException("Excpected a redirect URI during OIDC authentication: " + response.getStatusLine());
+
+			// Handle Bearer Challenge
+			HttpGet method = new HttpGet(redirectUri + "&prompt=none");
+			addDefaultOidcHeaders(method);
+
+			response = client.execute(method);
+
+			code = response.getStatusLine().getStatusCode();
+
+			if (code != SC_OK)
+				throw new RestCallException("Unexpected response during OIDC authentication phase 2: " + response.getStatusLine());
+
+			String loginRequired = getHeader(response, "X-JSA-LOGIN-REQUIRED");
+
+			if (! "true".equals(loginRequired))
+				throw new RestCallException("X-JSA-LOGIN-REQUIRED header not found on response during OIDC authentication phase 2: " + response.getStatusLine());
+
+			method = new HttpGet(redirectUri + "&prompt=none");
+
+			addDefaultOidcHeaders(method);
+			response = client.execute(method);
+
+			code = response.getStatusLine().getStatusCode();
+
+			if (code != SC_OK)
+				throw new RestCallException("Unexpected response during OIDC authentication phase 3: " + response.getStatusLine());
+
+			// Handle JAS Challenge
+			method = new HttpGet(redirectUri);
+			addDefaultOidcHeaders(method);
+
+			response = client.execute(method);
+
+			code = response.getStatusLine().getStatusCode();
+
+			if (code != SC_OK)
+				throw new RestCallException("Unexpected response during OIDC authentication phase 4: " + response.getStatusLine());
+
+			cookie = getHeader(response, "Set-Cookie");
+
+			Header[] defaultHeaders = new Header[] {
+				new BasicHeader("User-Agent", "Jazz Native Client"),
+				new BasicHeader("X-com-ibm-team-configuration-versions", "com.ibm.team.rtc=6.0.0,com.ibm.team.jazz.foundation=6.0"),
+				new BasicHeader("Accept", "text/json"),
+				new BasicHeader("Authorization", "Basic " + StringUtils.base64EncodeToString(this.user + ":" + this.pw)),
+				new BasicHeader("Cookie", cookie)
+			};
+
+			setDefaultHeaders(Arrays.asList(defaultHeaders));
+
+		} finally {
+			EntityUtils.consume(response.getEntity());
+		}
+	}
+
+	/*
+	 * This is needed for Tomcat because it responds with SC_BAD_REQUEST when the j_security_check URL is visited before an
+	 * authenticated URL has been visited. This same URL must also be visited after authenticating with j_security_check
+	 * otherwise tomcat will not consider the session authenticated
+	 */
+	private int visitAuthenticatedURL(HttpClient httpClient) throws IOException {
+		HttpGet authenticatedURL = new HttpGet(jazzUri.resolve("authenticated/identity"));
+		HttpResponse response = httpClient.execute(authenticatedURL);
+		try {
+			return response.getStatusLine().getStatusCode();
+		} finally {
+			EntityUtils.consume(response.getEntity());
+		}
+	}
+
+	/*
+	 * @return Returns "FORM" for form-based authenication, "BASIC" for basic auth, "OIDC" for OIDC.  Never <code>null</code>.
+	 */
+	private String determineAuthMethod(HttpClient client) throws IOException {
+
+		HttpGet request = new HttpGet(jazzUri.resolve("authenticated/identity"));
+		request.setConfig(RequestConfig.custom().setRedirectsEnabled(false).build());
+
+		// if the FORM_AUTH_URI path exists, then we know we are using FORM auth
+		HttpResponse response = client.execute(request);
+		try {				//'x-jsa-authorization-redirect'
+			Header redirectUri = response.getFirstHeader("X-JSA-AUTHORIZATION-REDIRECT");
+			if (redirectUri != null)
+				return "OIDC";
+
+			int rc = response.getStatusLine().getStatusCode();
+			// Tomcat and Jetty return a status code 200 if the server is using FORM auth
+			if (rc == SC_OK)
+				return "FORM";
+			else if (rc == SC_MOVED_TEMPORARILY && response.getFirstHeader("Location").getValue().matches("^.*(/auth/authrequired|/authenticated/identity).*$"))
+				return "FORM";
+			return "BASIC";
+
+		} finally {
+			EntityUtils.consume(response.getEntity());
+		}
+	}
+
+	private String getHeader(HttpResponse response, String key) {
+		Header h = response.getFirstHeader(key);
+		return (h == null ? null : h.getValue());
+	}
+
+	private void addDefaultOidcHeaders(HttpRequestBase method) {
+		method.addHeader("User-Agent", "Jazz Native Client");
+		method.addHeader("X-com-ibm-team-configuration-versions", "com.ibm.team.rtc=6.0.0,com.ibm.team.jazz.foundation=6.0");
+		method.addHeader("Accept", "text/json");
+
+		if (cookie != null) {
+			method.addHeader("Authorization", "Basic " + StringUtils.base64EncodeToString(user + ":" + pw));
+			method.addHeader("Cookie", cookie);
+		}
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/jazz/package.html
----------------------------------------------------------------------
diff --git a/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/jazz/package.html b/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/jazz/package.html
new file mode 100755
index 0000000..fce9248
--- /dev/null
+++ b/com.ibm.team.juno.releng/bin/client/com/ibm/juno/client/jazz/package.html
@@ -0,0 +1,187 @@
+<!DOCTYPE HTML>
+<!--
+    Licensed Materials - Property of IBM
+    (c) Copyright IBM Corporation 2014. All Rights Reserved.
+   
+    Note to U.S. Government Users Restricted Rights:  
+    Use, duplication or disclosure restricted by GSA ADP Schedule 
+    Contract with IBM Corp. 
+ -->
+<html>
+<head>
+	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+	<style type="text/css">
+		/* For viewing in Page Designer */
+		@IMPORT url("../../../../../../javadoc.css");
+
+		/* For viewing in REST interface */
+		@IMPORT url("../htdocs/javadoc.css");
+		body { 
+			margin: 20px; 
+		}	
+	</style>
+	<script>
+		/* Replace all @code and @link tags. */	
+		window.onload = function() {
+			document.body.innerHTML = document.body.innerHTML.replace(/\{\@code ([^\}]+)\}/g, '<code>$1</code>');
+			document.body.innerHTML = document.body.innerHTML.replace(/\{\@link (([^\}]+)\.)?([^\.\}]+)\}/g, '<code>$3</code>');
+		}
+	</script>
+</head>
+<body>
+<p>Jazz REST client API</p>
+
+<script>
+	function toggle(x) {
+		var div = x.nextSibling;
+		while (div != null && div.nodeType != 1)
+			div = div.nextSibling;
+		if (div != null) {
+			var d = div.style.display;
+			if (d == 'block' || d == '') {
+				div.style.display = 'none';
+				x.className += " closed";
+			} else {
+				div.style.display = 'block';
+				x.className = x.className.replace(/(?:^|\s)closed(?!\S)/g , '' );
+			}
+		}
+	}
+</script>
+
+<a id='TOC'></a><h5 class='toc'>Table of Contents</h5>
+<ol class='toc'>
+	<li><p><a class='doclink' href='#RestClient'>Jazz REST client API</a></p>
+</ol>
+
+<!-- ======================================================================================================== -->
+<a id="RestClient"></a>
+<h2 class='topic' onclick='toggle(this)'>1 - Jazz REST client API</h2>
+<div class='topic'>
+	<p>
+		Juno provides a default REST client implementation for working with Jazz servers. 
+		The client automatically detects and handles BASIC and FORM authentication and basic certificate authentication.
+	</p>
+	<p>
+		The following code shows the Jazz REST client being used for querying and creating server messages on 
+			a Jazz server.  The <code>ServerMessage</code> and <code>CreateServerMessage</code> classes
+			are nothing more than simple beans that get serialized over the connection and reconstituted on 
+			the server.
+	</p>
+	<p class='bcode'>
+	System.<jsf>out</jsf>.println(<js>"Adding sample messages"</js>);
+
+	DateFormat df = <jk>new</jk> SimpleDateFormat(<js>"yyyy-MM-dd'T'HH:mm:ssZ"</js>);
+	String url = <js>"https://localhost:9443/jazz"</js>;
+	String sms = url + <js>"/serverMessages"</js>;
+	CreateServerMessage m;
+	ServerMessage m2;
+	String s1;
+	ServerMessage[] messages;
+	
+	<jc>// Serializer for debug messages.</jc>
+	WriterSerializer serializer = JsonSerializer.<jsf>DEFAULT</jsf>;
+	
+	<jc>// Create clients to handle JSON and XML requests and responses.</jc>
+	RestClient jsonClient = <jk>new</jk> JazzRestClient(url, <js>"ADMIN"</js>, <js>"ADMIN"</js>)
+		.setSerializer(JsonSerializer.<jk>class</jk>)
+		.setParser(<jk>new</jk> JsonParser().addFilters(DateFilter.<jsf>ISO8601DTZ</jsf>.<jk>class</jk>));
+	
+	RestClient xmlClient = <jk>new</jk> JazzRestClient(url, <js>"ADMIN"</js>, <js>"ADMIN"</js>, XmlSerializer.<jk>class</jk>, XmlParser.<jk>class</jk>);
+	
+	<jc>// Delete any existing messages.</jc>
+	messages = jsonClient
+		.doGet(sms)
+		.getResponse(ServerMessage[].<jk>class</jk>);
+	
+	<jk>for</jk> (ServerMessage message : messages) {
+		<jk>int</jk> rc = jsonClient
+			.doDelete(message.getUri())
+			.execute();
+		System.<jsf>out</jsf>.println(rc);	<jc>// Prints 200.</jc>
+	}
+	
+	<jc>// Create an active server message.</jc>
+	m = <jk>new</jk> CreateServerMessage(
+		<jsf>INFO</jsf>,
+		<js>"Test message #1"</js>,
+		<js>"subTypeFoo"</js>,
+		df.parse(<js>"2012-01-01T12:34:56EST"</js>),
+		df.parse(<js>"2013-01-01T12:34:56EST"</js>));
+	
+	<jc>// POST the message, get response as string.</jc>
+	s1 = jsonClient
+		.doPost(sms, m)
+		.getResponseAsString(); 
+	System.<jsf>out</jsf>.println(<js>"TEST1: response="</js> + s1);
+
+	<jc>// POST another message, get response as ServerMessage</jc>
+	m = <jk>new</jk> CreateServerMessage(
+		<jsf>INFO</jsf>,
+		<js>"Test message #2"</js>,
+		<js>"subTypeFoo"</js>,
+		df.parse(<js>"2012-01-01T12:34:56EST"</js>),
+		df.parse(<js>"2013-01-01T12:34:56EST"</js>));
+
+	m2 = jsonClient
+		.doPost(sms, m)
+		.getResponse(ServerMessage.<jk>class</jk>); 
+	System.<jsf>out</jsf>.println(<js>"TEST2: response="</js> + serializer.serialize(m2));
+	
+	<jc>// Create a future server message.</jc>
+	m = <jk>new</jk> CreateServerMessage(
+		<jsf>INFO</jsf>,
+		<js>"Test message #3"</js>,
+		<js>"subTypeFoo"</js>,
+		df.parse(<js>"2013-01-01T12:34:56EST"</js>),
+		df.parse(<js>"2014-01-01T12:34:56EST"</js>));
+	
+	m2 = jsonClient
+		.doPost(sms, m)
+		.getResponse(ServerMessage.<jk>class</jk>); 
+	System.<jsf>out</jsf>.println(<js>"TEST3: response="</js> + serializer.serialize(m2));
+	System.<jsf>out</jsf>.println(<js>"TEST3: id="</js> + m2.getItemId().getUuidValue());
+
+	<jc>// Create a future server message using XML on both request and response.</jc>
+	m = <jk>new</jk> CreateServerMessage(
+		<jsf>INFO</jsf>,
+		<js>"Test message #4"</js>,
+		<js>"subTypeFoo"</js>,                                  
+		df.parse(<js>"2013-01-01T12:34:56EST"</js>),
+		df.parse(<js>"2014-01-01T12:34:56EST"</js>));
+	
+	s1 = xmlClient
+		.doPost(sms, m)
+		.getResponseAsString();
+	System.<jsf>out</jsf>.println(<js>"TEST4: response="</js> + s1);
+
+	<jc>// Get all the messages</jc>
+	messages = jsonClient
+		.doGet(sms)
+		.getResponse(ServerMessage[].<jk>class</jk>);
+	System.<jsf>out</jsf>.println(<js>"TEST5: response="</js> + serializer.serialize(messages)); 
+
+	<jc>// Get the first ID</jc>
+	URI firstMessageUrl = messages[0].getUri();
+	
+	System.<jsf>out</jsf>.println(<js>"firstMessageUrl=["</js>+firstMessageUrl+<js>"]"</js>);
+	
+	<jc>// Get the Date of the first ID.</jc>
+	Date startDate = jsonClient
+		.doGet(firstMessageUrl + <js>"/startDate"</js>)
+		.getResponse(Date.<jk>class</jk>);  
+	System.<jsf>out</jsf>.println(<js>"TEST5: response.startDate="</js>+startDate);
+	
+	<jc>// Change the start and end dates on first message</jc>
+	m = <jk>new</jk> CreateServerMessage(
+		<jsf>INFO</jsf>,
+		<js>"Test message #3 overwritten"</js>,
+		<js>"subTypeFooBar"</js>,
+		df.parse(<js>"2023-01-01T12:34:56EST"</js>),
+		df.parse(<js>"2024-01-01T12:34:56EST"</js>));
+	s1 = jsonClient.doPut(firstMessageUrl, m).getResponseAsString();
+	System.<jsf>out</jsf>.println(<js>"TEST6: response="</js>+s1);
+	</p>
+</div>
+</body>
+</html>
\ No newline at end of file