diff --git a/yada-api/src/main/java/com/novartis/opensource/yada/adaptor/RESTAdaptor.java b/yada-api/src/main/java/com/novartis/opensource/yada/adaptor/RESTAdaptor.java
index a3e2ed20..64717d45 100644
--- a/yada-api/src/main/java/com/novartis/opensource/yada/adaptor/RESTAdaptor.java
+++ b/yada-api/src/main/java/com/novartis/opensource/yada/adaptor/RESTAdaptor.java
@@ -15,48 +15,49 @@
package com.novartis.opensource.yada.adaptor;
import java.io.BufferedReader;
+import java.io.File;
import java.io.IOException;
+import java.io.InputStream;
import java.io.InputStreamReader;
-import java.io.OutputStream;
-import java.io.OutputStreamWriter;
-import java.io.UnsupportedEncodingException;
import java.net.HttpCookie;
-import java.net.HttpURLConnection;
-import java.net.InetSocketAddress;
-import java.net.MalformedURLException;
-import java.net.Proxy;
import java.net.URL;
import java.net.URLConnection;
-import java.net.URLEncoder;
-import java.security.InvalidKeyException;
-import java.security.KeyFactory;
+import java.security.GeneralSecurityException;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
-import java.security.SecureRandom;
-import java.security.Signature;
-import java.security.SignatureException;
-import java.security.spec.InvalidKeySpecException;
-import java.security.spec.PKCS8EncodedKeySpec;
+import java.security.UnrecoverableEntryException;
+import java.security.cert.CertificateException;
import java.util.Iterator;
-import java.util.LinkedHashMap;
-import java.util.List;
import java.util.Map;
-import java.util.TreeMap;
+import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
-import javax.net.ssl.HttpsURLConnection;
-
-import org.apache.commons.codec.binary.Base64;
import org.apache.log4j.Logger;
+import com.google.api.client.auth.oauth.OAuthParameters;
+import com.google.api.client.auth.oauth.OAuthRsaSigner;
+import com.google.api.client.http.BasicAuthentication;
+import com.google.api.client.http.ByteArrayContent;
+import com.google.api.client.http.GenericUrl;
+import com.google.api.client.http.HttpContent;
+import com.google.api.client.http.HttpHeaders;
+import com.google.api.client.http.HttpRequest;
+import com.google.api.client.http.HttpRequestFactory;
+import com.google.api.client.http.HttpRequestInitializer;
+import com.google.api.client.http.HttpResponse;
+import com.google.api.client.http.javanet.NetHttpTransport;
+
import com.novartis.opensource.yada.ConnectionFactory;
import com.novartis.opensource.yada.YADAQuery;
import com.novartis.opensource.yada.YADAQueryConfigurationException;
import com.novartis.opensource.yada.YADAQueryResult;
import com.novartis.opensource.yada.YADARequest;
+import com.novartis.opensource.yada.YADASecurityException;
-
+import org.json.JSONException;
import org.json.JSONObject;
@@ -76,32 +77,50 @@ public class RESTAdaptor extends Adaptor {
* Constant equal to: {@value}. The character set name.
* @since 8.7.0
*/
- private final static String CHARSET_UTF8 = "UTF-8";
+ private final static String PROP_KEYSTORE = "javax.net.ssl.keyStore";
+
+ /**
+ * Constant equal to: {@value}. The keystore password passed to the jvm.
+ * @since 8.7.0
+ */
+ private final static String PROP_KEYSTORE_PASS = "javax.net.ssl.keyStorePassword";
+
+ /**
+ * Constant equal to: {@value}. The {@code oauth} request parameter property name.
+ * @since 8.7.0
+ */
+ private final static String OAUTH_VERSION = "oauth_version";
+
+ /**
+ * Constant equal to: {@value}. The {@code oauth} request parameter property name.
+ * @since 8.7.0
+ */
+ private final static String OAUTH_SIGNATURE_METHOD = "oauth_signature_method";
+
+ /**
+ * Constant equal to: {@value}. The {@code oauth} request parameter property name.
+ * @since 8.7.0
+ */
+ private final static String OAUTH_VERIFIER = "oauth_verifier";
- private final static String OAUTH_VERIFIER = "oauth_verifier";
- private final static String OAUTH_TIMESTAMP = "oauth_timestamp";
- private final static String OAUTH_NONCE = "oauth_nonce";
- private final static String OAUTH_SIGNATURE = "oauth_signature";
- private final static String OAUTH_REALM = "OAuth realm";
- private final static String SPACE = " ";
- private final static String EQUAL = "=";
- private final static String QUOTE = "\"";
- private final static String COMMA = ",";
- private final static String AMP = "&";
- private final static String CR = "\r";
- private final static String LF = "\n";
- private final static String CRLF = CR+LF;
- private final static char URL_QUERY_MARKER = '?';
- private final static String BODY = "body";
+ /**
+ * Constant equal to: {@value}. The {@code oauth} request parameter property name.
+ * @since 8.7.0
+ */
+ private final static String OAUTH_PRIVATE_KEY = "oauth_private_key";
/**
- * Secure random number generator to sign requests.
- * @see OAuthParameters.java
+ * Constant equal to: {@value}. The {@code oauth} request parameter property name.
* @since 8.7.0
*/
- private final static SecureRandom RANDOM = new SecureRandom();
- private final static String ALGO_RSA = "RSA";
- private final static String ALGO_RSASHA1 = "SHA1withRSA";
+ private final static String OAUTH_CONSUMER_KEY = "oauth_consumer_key";
+
+ /**
+ * Constant equal to: {@value}. The {@code oauth} request parameter property name.
+ * @since 8.7.0
+ */
+ private final static String OAUTH_TOKEN = "oauth_token";
+
/**
* Constant equal to: {@value}
*/
@@ -109,20 +128,24 @@ public class RESTAdaptor extends Adaptor {
/**
* Constant equal to: {@value}
- * {@link JSONParams} key for delivery of {@code HTTP POST, PUT, PATCH} body content
+ * {@link com.novartis.opensource.yada.JSONParams} key for delivery of {@code HTTP POST, PUT, PATCH} body content
* @since 8.5.0
*/
private final static String YADA_PAYLOAD = "YADA_PAYLOAD";
/**
- * Constant equal to: {@value}
+ * Constant equal to: {@value}.
* Workaround for requests using {@code HTTP PATCH}
* @since 8.5.0
*/
- private final static String X_HTTP_METHOD_OVERRIDE = "X-HTTP-Method-Override";
- private final static String X_AUTHORIZATION = "X-Authorization";
- private final static String CONTENT_LENGTH = "Content-length";
- /**
+ private final static String H_X_HTTP_METHOD_OVERRIDE = "X-HTTP-Method-Override";
+
+ /**
+ * Constant equal to: {@value}. For retrieving the default keystore.
+ */
+ private static final String KEYSTORE_TYPE_JKS = "jks";
+
+ /**
* Variable to hold the proxy server string if necessary
*/
private String proxy = null;
@@ -138,7 +161,6 @@ public class RESTAdaptor extends Adaptor {
*/
private String method = YADARequest.METHOD_GET;
-
/**
* Default constructor
*/
@@ -216,234 +238,72 @@ private String setPositionalParameterValues(YADAQuery yq, int row) {
return urlStr;
}
- private void setAuthentication(YADAQuery yq, int row, HttpURLConnection hConn) throws YADAQueryConfigurationException, InvalidKeyException, NoSuchAlgorithmException, InvalidKeySpecException, SignatureException, IOException
- {
+ /**
+ * Determines if OAuth or Basic Authentication should be applied given the {@link YADARequest}
+ * configuration, and proceeds accordingly.
+ * @param yq The query object defined in the {@link YADARequest}
+ * @param url The REST endpoint
+ * @return The {@link HttpRequestInitializer} to pass to the {@link HttpRequestFactory}
+ * @throws YADAQueryConfigurationException if both OAuth and Basic are defined in the request
+ * @throws YADASecurityException if authentication fails
+ * @since 8.7.0
+ */
+ private HttpRequestInitializer setAuthentication(YADAQuery yq, GenericUrl url) throws YADAQueryConfigurationException, YADASecurityException
+ {
if(yq.hasParam(YADARequest.PS_OAUTH) || yq.hasParam(YADARequest.PL_OAUTH))
{
- if(hConn.getURL().getUserInfo() != null)
+ if(url.getUserInfo() != null)
{
String msg = "A query cannot contain both basic and oauth configurations. "
+ "When using oauth, make sure the REST data source is not also configured with "
+ "basic auth values.";
throw new YADAQueryConfigurationException(msg);
}
- String body = yq.getDataRow(row).get(YADA_PAYLOAD)[0];
- setAuthenticationOAuth(body,hConn);
+ return setAuthenticationOAuth(url);
}
- else if(hConn.getURL().getUserInfo() != null)
+ else if(url.getUserInfo() != null)
{
- setAuthenticatonBasic(hConn.getURL(), hConn);
+ return setAuthenticatonBasic(url);
}
+ return null;
}
- /**
- * Generates a random nonce. This method originated at
- * developer.pearson.com
- * Oauth 1.0a Sample Code
- *
- * @return A unique identifier for the request
- * @since 8.7.0
- */
- private static String getNonce()
- {
- return Long.toHexString(Math.abs(RANDOM.nextLong()));
- }
-
/**
- * Generates an integer representing the number of seconds since the unix epoch using the
- * date/time the request is issued. This method originated at
- * developer.pearson.com
- * Oauth 1.0a Sample Code
- *
- *
- * @return A timestamp for the request
- * @since 8.7.0
+ * Generates a {@link PrivateKey} object from the encoded {@code privateKey}string obtained from the
+ * {@code JKS} keystore. The provided {@link KeyStore} is designated at application boot time by the JVM's
+ * {@code javax.net.ssl.keyStore} and {@code javax.net.ssl.keyStorePassword} properties.s
+ * under the provided {@code alias}
+ * @param alias the alias to the private key stored in the YADA keystore
+ * @return the private key object
+ * @throws NoSuchAlgorithmException when the JKS keystore can't be loaded
+ * @throws KeyStoreException when there is no {@code JKS} keystore available
+ * @throws IOException when the JKS keystore can't be loaded due to a filesystem issue
+ * @throws CertificateException when the JKS keystore can't be loaded
+ * @throws UnrecoverableEntryException when the desired entry can't be extracted
+ * @since 8.7.0
*/
- private static String getTimestamp()
- {
- return Long.toString((System.currentTimeMillis() / 1000));
- }
-
- /**
- * Generates an OAuth 1.0 signature. This method originated at
- * developer.pearson.com
- * Oauth 1.0a Sample Code
- *
- *
- * @param httpMethod The HTTP method of the request
- * @param URL The request URL
- * @param oauthParams The associative set of signable oAuth parameters
- * @param requestBody The serialized POST/PUT message body
- * @param secret Alphanumeric string used to validate the identity of the education partner (Private Key)
- *
- * @return A string containing the Base64-encoded signature digest
- *
- * @throws UnsupportedEncodingException
- * @throws SignatureException
- * @throws InvalidKeySpecException
- * @throws NoSuchAlgorithmException
- * @throws InvalidKeyException
- * @since 8.7.0
- */
- private static String generateSignature(
- String httpMethod,
- URL url,
- Map oauthParams,
- byte[] requestBody,
- String secret
- ) throws UnsupportedEncodingException, InvalidKeyException, NoSuchAlgorithmException, InvalidKeySpecException, SignatureException
+ private PrivateKey getPrivateKey(String alias) throws NoSuchAlgorithmException, KeyStoreException, CertificateException, IOException, UnrecoverableEntryException
{
- // Ensure the HTTP Method is upper-cased
- httpMethod = httpMethod.toUpperCase();
-
- // Construct the URL-encoded OAuth parameter portion of the signature base string
- String encodedParams = normalizeParams(httpMethod, url, oauthParams, requestBody);
-
- // URL-encode the relative URL
- String encodedUri = URLEncoder.encode(url.getPath(), CHARSET_UTF8);
-
- // Build the signature base string to be signed with the Consumer Secret
- String baseString = String.format("%s&%s&%s", httpMethod, encodedUri, encodedParams);
-
- return generateRSA(secret, baseString);
-
- }
-
- /**
- * Normalizes all OAuth signable parameters and url query parameters according to OAuth 1.0. This method originated at
- * developer.pearson.com
- * Oauth 1.0a Sample Code
- *
- *
- * @param httpMethod The upper-cased HTTP method
- * @param URL The request URL
- * @param oauthParams The associative set of signable oAuth parameters
- * @param requstBody The serialized POST/PUT message body
- *
- * @return A string containing normalized and encoded oAuth parameters
- *
- * @throws UnsupportedEncodingException
- * @since 8.7.0
- */
- private static String normalizeParams(
- String httpMethod,
- URL url,
- Map oauthParams,
- byte[] requestBody
- ) throws UnsupportedEncodingException
- {
-
- // Sort the parameters in lexicographical order, 1st by Key then by Value
- Map kvpParams = new TreeMap(String.CASE_INSENSITIVE_ORDER);
- kvpParams.putAll(oauthParams);
-
- // Place any query string parameters into a key value pair using equals ("=") to mark
- // the key/value relationship and join each parameter with an ampersand ("&")
- if (url.getQuery() != null)
- {
- for(String keyValue : url.getQuery().split(AMP))
- {
- String[] p = keyValue.split(EQUAL);
- kvpParams.put(p[0],p[1]);
- }
-
- }
-
- // Include the body parameter if dealing with a POST or PUT request
- if (YADARequest.METHOD_POST.equals(httpMethod) || YADARequest.METHOD_PUT.equals(httpMethod))
- {
- String body = Base64.encodeBase64String(requestBody).replaceAll(CRLF, "");
- // url encode the body 2 times now before combining other params
- body = URLEncoder.encode(body, CHARSET_UTF8);
- body = URLEncoder.encode(body, CHARSET_UTF8);
- kvpParams.put(BODY, body);
- }
-
- // separate the key and values with a "="
- // separate the kvp with a "&"
- StringBuilder combinedParams = new StringBuilder();
- String delimiter="";
- for(String key : kvpParams.keySet()) {
- combinedParams.append(delimiter);
- combinedParams.append(key);
- combinedParams.append(EQUAL);
- combinedParams.append(kvpParams.get(key));
- delimiter=AMP;
- }
-
- // url encode the entire string again before returning
- return URLEncoder.encode(combinedParams.toString(), CHARSET_UTF8);
+ String ksPath = System.getProperty(PROP_KEYSTORE);
+ char[] ksPass = System.getProperty(PROP_KEYSTORE_PASS).toCharArray();
+ KeyStore.ProtectionParameter ksProtParam = new KeyStore.PasswordProtection(ksPass);
+ File ksFile = new File(ksPath);
+ URL ksURL = ksFile.toURI().toURL();
+ KeyStore keyStore = KeyStore.getInstance(KEYSTORE_TYPE_JKS);
+ try(InputStream is = ksURL.openStream())
+ {
+ keyStore.load(is, null == ksPass ? null : ksPass);
+ }
+ KeyStore.PrivateKeyEntry ksEntry = (KeyStore.PrivateKeyEntry) keyStore.getEntry(alias, ksProtParam);
+ PrivateKey pk = ksEntry.getPrivateKey();
+
+ return pk;
}
-//TODO implement MAC support for Oauth 1.0a
-// Leave the commented out MAC stuff in here for now. It's not pressing for the current requirement
-// but should be rectified soon. It will require finding or building a provider in order to test,
-// which could take some time.
-//
-// /**
-// * Generates a Base64-encoded CMAC-AES digest
-// *
-// * @param key The secret key used to sign the data
-// * @param msg The data to be signed
-// *
-// * @return A CMAC-AES hash
-// *
-// * @throws UnsupportedEncodingException
-// */
-// private static String generateCmac(String key, String msg) throws UnsupportedEncodingException
-// {
-// byte[] keyBytes = key.getBytes(CHARSET_UTF8);
-// byte[] data = msg.getBytes(CHARSET_UTF8);
-
-//// Person code:
-//// CMac macProvider = new CMac(new AESFastEngine());
-//// macProvider.init(new KeyParameter(keyBytes));
-//// macProvider.reset();
-////
-//// macProvider.update(data, 0, data.length);
-//// byte[] output = new byte[macProvider.getMacSize()];
-//// macProvider.doFinal(output, 0);
-
-// YADA stubby:
-// Mac mac = Mac.getInstance("HmacSHA1");
-// mac.init(secretKey);
-//
-// // Convert the CMAC to a Base64 string and remove the new line the Base64 library adds
-// String cmac = Base64.encodeBase64String(output).replaceAll("\r\n", "");
-//
-// return cmac;
-//
-// }
-
- /**
- * Generates a Base64-encoded RSA-SHA1 digest
- *
- * @param key The secret key used to sign the data
- * @param msg The data to be signed
- *
- * @return An RSA-SHA1 hash
- *
- * @throws UnsupportedEncodingException
- * @throws NoSuchAlgorithmException
- * @throws InvalidKeySpecException
- * @throws InvalidKeyException
- * @throws SignatureException
- */
- private static String generateRSA(String key, String msg) throws UnsupportedEncodingException, NoSuchAlgorithmException, InvalidKeySpecException, InvalidKeyException, SignatureException
- {
- byte[] keyBytes = key.getBytes(CHARSET_UTF8);
- byte[] data = msg.getBytes(CHARSET_UTF8);
- Signature signer = Signature.getInstance(ALGO_RSASHA1);
- KeyFactory kf = KeyFactory.getInstance(ALGO_RSA);
- PrivateKey privateKey = kf.generatePrivate(new PKCS8EncodedKeySpec(keyBytes));
- signer.initSign(privateKey);
- signer.update(data);
- byte[] signed = signer.sign();
- return Base64.encodeBase64String(signed);
- }
/**
- * This method relies on passage of {@link YADAQuery#getOAuth()} which returns
+ * Creates the {@link OAuthParameters} {@link HttpRequestInitializer}.
+ * This method relies on inclusion of {@link YADARequest#getOAuth()} which returns
* a {@link JSONObject} containing:
*
*
@@ -452,105 +312,82 @@ private static String generateRSA(String key, String msg) throws UnsupportedEnco
* oauth_token
* oauth_verifier (secret)
* oauth_version
. For now, should always equal 1.0a
+ * oauth_private_key
which is the alias
+ * under which the desired KeyStore.PrivateKeyEntry
is stored
*
- *
- * The logic of this method is derived from
- * developer.pearson.com
- * Oauth 1.0a Sample Code
*
- * @param body the value associated to the YADA_PAYLOAD
key in the current "row"
- * @param hConn the {@link HttpURLConnection} used to engage with the web service
- * @throws SignatureException
- * @throws InvalidKeySpecException
- * @throws NoSuchAlgorithmException
- * @throws InvalidKeyException
- * @throws IOException
+ * @param url the transformed (conformed) REST endpoint
+ * @return the initializer to pass to the request factory
+ * @throws YADASecurityException if there is an issue with the keystore or key entry
+ * @throws YADAQueryConfigurationException if the 'oauth' request param is misconfigured
* @since 8.7.0
*/
- private void setAuthenticationOAuth(String body, HttpURLConnection hConn) throws InvalidKeyException, NoSuchAlgorithmException, InvalidKeySpecException, SignatureException, IOException
+ private HttpRequestInitializer setAuthenticationOAuth(GenericUrl url) throws YADASecurityException, YADAQueryConfigurationException
{
-
- String httpMethod = hConn.getRequestMethod();
- URL url = hConn.getURL();
- byte[] requestBody = null;
- BufferedReader in = null;
+ OAuthRsaSigner signer = new OAuthRsaSigner();
- // Set the request body if making a POST or PUT request
- //TODO handle body content--this is just YADA_PAYLOAD now which is probs totes wrong
- if (YADARequest.METHOD_POST.equals(httpMethod) || YADARequest.METHOD_PUT.equals(httpMethod))
+ try
{
- requestBody = body.getBytes(CHARSET_UTF8);
- }
-
- // Create the OAuth parameter map from the oauth yada param
- Map oauthParams = new LinkedHashMap();
- for(Object key : oauth.keySet())
+ signer.privateKey = getPrivateKey(oauth.getString(OAUTH_PRIVATE_KEY));
+ }
+ catch (NoSuchAlgorithmException|KeyStoreException|CertificateException|UnrecoverableEntryException|IOException e)
+ {
+ String msg = "There was a problem loading the KeyStore or obtaining the designated key.";
+ throw new YADASecurityException(msg,e);
+ }
+ catch (JSONException e)
{
- oauthParams.put((String)key, oauth.getString((String)key));
- }
- oauthParams.put(OAUTH_TIMESTAMP, getTimestamp());
- oauthParams.put(OAUTH_NONCE, getNonce());
-
+ String msg = "There was a problem obtaining the private key entry alias from the request. Check the 'oauth' request parameter for the 'oauth_private_key' property.";
+ throw new YADAQueryConfigurationException(msg,e);
+ }
+
+ OAuthParameters oauthParams = new OAuthParameters();
+ oauthParams.consumerKey = oauth.getString(OAUTH_CONSUMER_KEY);
+ oauthParams.signer = signer;
+ oauthParams.signatureMethod = oauth.getString(OAUTH_SIGNATURE_METHOD);
+ oauthParams.token = oauth.getString(OAUTH_TOKEN);
+ oauthParams.verifier = oauth.getString(OAUTH_VERIFIER);
+ oauthParams.version = oauth.getString(OAUTH_VERSION);
+ oauthParams.computeNonce();
+ oauthParams.computeTimestamp();
+ try
- // Get the OAuth 1.0 Signature
- String secret = oauthParams.get(OAUTH_VERIFIER);
- String signature = generateSignature(httpMethod, url, oauthParams, requestBody, secret);
+ {
+ oauthParams.computeSignature(this.method, url);
+ }
+ catch (GeneralSecurityException e)
+ {
+ String msg = "Unable to compute signature with the information provided. Check 'oauth' request parameter and key pair.";
+ throw new YADASecurityException(msg, e);
+ }
- l.debug(String.format("OAuth 1.0 Signature = %s", signature));
-
- // Add the oauth_signature parameter to the set of OAuth Parameters
- oauthParams.put(OAUTH_SIGNATURE, signature);
-
- // Generate a string of comma delimited: keyName="URL-encoded(value)" pairs
- StringBuilder paramStringBldr = new StringBuilder();
- String delimiter = "";
- for (String keyName : oauthParams.keySet()) {
- paramStringBldr.append(delimiter);
- String value = oauthParams.get((String) keyName);
- paramStringBldr.append(keyName).append(EQUAL+QUOTE).append(URLEncoder.encode(value, CHARSET_UTF8)).append(QUOTE);
- delimiter=COMMA;
- }
-
- String urlString = url.toString();
- // omit the queryString from the url
- int startOfQueryString = urlString.indexOf(URL_QUERY_MARKER);
- if(startOfQueryString != -1) {
- urlString = urlString.substring(0,startOfQueryString);
- }
-
- // Build the X-Authorization request header
- String xauth = String.format(OAUTH_REALM+EQUAL+QUOTE+"%s"+QUOTE+",%s", urlString, paramStringBldr.toString());
- l.debug(String.format("X-Authorization request header = %s", xauth));
-
- // Add the header
- hConn.addRequestProperty(X_AUTHORIZATION, xauth);
+ return oauthParams;
}
/**
- * Sets the Authorization
header for the request with Base64-encoded key/value pair
- * @param url the URL object
- * @param conn the URLConnection object
+ * Creates the {@link BasicAuthentication} {@link HttpRequestInitializer}
+ * @param url the conformed REST endpoint
+ * @return the initializer to pass to the request factory
* @since 8.7.0
*/
- private void setAuthenticatonBasic(URL url, URLConnection conn)
+ private HttpRequestInitializer setAuthenticatonBasic(GenericUrl url)
{
- //TODO basic auth and other auth methods should be mutually exclusive
- if (url.getUserInfo() != null)
- {
- //TODO issue with '@' sign in pw, must decode first
- String basicAuth = "Basic " + new String(new Base64().encode(url.getUserInfo().getBytes()));
- conn.setRequestProperty("Authorization", basicAuth);
- }
+ //TODO issue with '@' sign in pw, must decode first (is this still an issue?)
+ String[] userinfo = url.getUserInfo().split(":");
+ String username = userinfo[0];
+ String password = userinfo[1];
+ BasicAuthentication ba = new BasicAuthentication(username, password);
+ return ba;
}
/**
* Looks for cookie strings passed in the request or stored in the {@link YADAQuery}
- * and adds them to the {@link URLConnection}
+ * and adds them to the {@link HttpRequest}
* @param yq The YADAQuery object
- * @param conn The URLConnection object
+ * @param request The HttpRequest object
* @since 8.7.0
*/
- private void setCookies(YADAQuery yq, URLConnection conn)
+ private void setCookies(YADAQuery yq, HttpRequest request)
{
if(yq.getCookies() != null && yq.getCookies().size() > 0)
{
@@ -559,21 +396,24 @@ private void setCookies(YADAQuery yq, URLConnection conn)
{
cookieStr += cookie.getName()+"="+cookie.getValue()+";";
}
- conn.setRequestProperty("Cookie", cookieStr);
+ HttpHeaders headers = new HttpHeaders();
+ headers.setCookie(cookieStr);
+ request.setHeaders(headers);
}
}
/**
* Looks for http header strings passed in the request or stored in the {@link YADAQuery}
- * and adds them to the {@link URLConnection}
- * @param yq the YADAQuery object
- * @param conn the URLConnection object
+ * and adds them to the {@link HttpRequest}
+ * @param yq the {@link YADAQuery} object
+ * @param request the HttpRequest object
* @since 8.7.0
*/
- private void setHeaders(YADAQuery yq, URLConnection conn)
+ private void setHeaders(YADAQuery yq, HttpRequest request)
{
if(yq.getHttpHeaders() != null && yq.getHttpHeaders().length() > 0)
{
+ HttpHeaders headers = new HttpHeaders();
l.debug("Processing custom headers...");
@SuppressWarnings("unchecked")
Iterator keys = yq.getHttpHeaders().keys();
@@ -582,67 +422,46 @@ private void setHeaders(YADAQuery yq, URLConnection conn)
String name = keys.next();
String value = yq.getHttpHeaders().getString(name);
l.debug("Custom header: "+name+" : "+value);
- conn.setRequestProperty(name, value);
- if(name.equals(X_HTTP_METHOD_OVERRIDE) && value.equals(YADARequest.METHOD_PATCH))
- {
- l.debug("Resetting method to ["+YADARequest.METHOD_POST+"]");
- this.method = YADARequest.METHOD_POST;
- }
+ headers.set(name, value);
}
}
}
/**
- * Injects the payload into the request's {@link OutputStream} if {@link YADARequest#getMethod()} returns
- * {@link YADARequest#METHOD_POST}, {@link YADARequest#METHOD_PUT}, or {@link YADARequest#METHOD_PATCH}
- * @param yq The current YADAQuery object
- * @param row The current data "row"
- * @param hConn The HTTPUrlConnection into which the data will be put
- * @throws YADAAdaptorExecutionException in the event of an issue writing to the output stream.
+ * Returns true if method
matches POST
, PUT
, or PATCH
+ * @param method the http method of the request to check
+ * @return true if method
matches POST
, PUT
, or PATCH
* @since 8.7.0
*/
- private void handlePostPutPatch(YADAQuery yq, int row, HttpURLConnection hConn) throws YADAAdaptorExecutionException {
- boolean isPostPutPatch = this.method.equals(YADARequest.METHOD_POST)
- || this.method.equals(YADARequest.METHOD_PUT)
- || this.method.equals(YADARequest.METHOD_PATCH);
-
- if(!this.method.equals(YADARequest.METHOD_GET))
- {
- try
- {
- hConn.setRequestMethod(this.method);
- if(isPostPutPatch)
- {
- //TODO make YADA_PAYLOAD case-insensitive and create an alias for it, e.g., ypl
- // NOTE: YADA_PAYLOAD is a COLUMN NAME found in a JSONParams DATA object. It
- // is not a YADA param
- String payload = yq.getDataRow(row).get(YADA_PAYLOAD)[0];
- hConn.setDoOutput(true);
- OutputStreamWriter writer;
- writer = new OutputStreamWriter(hConn.getOutputStream());
- writer.write(payload.toString());
- writer.flush();
- }
- }
- catch (IOException e)
- {
- String msg = "There was a problem injecting the payload into the request";
- throw new YADAAdaptorExecutionException(msg, e);
- }
- }
+ protected static boolean isPostPutPatch(String method)
+ {
+ return method.equals(YADARequest.METHOD_POST)
+ || method.equals(YADARequest.METHOD_PUT)
+ || method.equals(YADARequest.METHOD_PATCH);
}
/**
* Reads the response {@link InputStream} and writes it to a {@link String}
- * @param hConn the connection object
+ * @param request the request object
* @return a String containing the response data
* @throws YADAAdaptorExecutionException when reading the input stream fails
* @since 8.7.0
*/
- private String processResponse(HttpURLConnection hConn) throws YADAAdaptorExecutionException
+ private String processResponse(HttpRequest request) throws YADAAdaptorExecutionException
{
String result = "";
- try(BufferedReader in = new BufferedReader(new InputStreamReader(hConn.getInputStream())))
+ HttpResponse response;
+ try
+ {
+ response = request.execute();
+ }
+ catch (IOException e)
+ {
+ String msg = "Unable to execute request.";
+ throw new YADAAdaptorExecutionException(msg,e);
+ }
+
+ try(BufferedReader in = new BufferedReader(new InputStreamReader(response.getContent())))
{
String inputLine;
while ((inputLine = in.readLine()) != null)
@@ -659,114 +478,149 @@ private String processResponse(HttpURLConnection hConn) throws YADAAdaptorExecut
}
/**
- * Writes the header fields to the console or log
- * @param conn the current connection object
+ * Writes the header fields to the console or log while in DEBUG mode
+ * @param request the current request object
* @since 8.7.0
*/
- private void logRequest(URLConnection conn) {
- Map> map = conn.getHeaderFields();
- for (Map.Entry> entry : map.entrySet()) {
+ private void logRequest(HttpRequest request) {
+ Map map = request.getHeaders();
+ for (Entry entry : map.entrySet()) {
l.debug("Key : " + entry.getKey() + " ,Value : " + entry.getValue());
}
}
+ /**
+ * Wrapper for {@link HttpRequestFactory#buildRequest(String, GenericUrl, HttpContent)}
+ * @param yq The {@link YADAQuery} defined by the {@link YADARequest}
+ * @param payload the request body or null
+ * @param url the REST endpoint
+ * @return the initialized {@link HttpRequest}
+ * @throws YADAQueryConfigurationException when there is an issue with request construction
+ * @throws YADASecurityException when authentication fails
+ */
+ private HttpRequest buildRequest(YADAQuery yq, String payload, GenericUrl url) throws YADAQueryConfigurationException, YADASecurityException
+ {
+ HttpContent content = null;
+ HttpRequestInitializer initializer = setAuthentication(yq, url);
+ HttpRequestFactory requestFactory = new NetHttpTransport().createRequestFactory(initializer);
+ if(isPostPutPatch(this.method))
+ {
+ content = ByteArrayContent.fromString(null, payload);
+ if(this.method.equals(YADARequest.METHOD_PATCH))
+ this.method = YADARequest.METHOD_PUT;
+ }
+
+ try
+ {
+ return requestFactory.buildRequest(this.method, url, content);
+ }
+ catch (IOException e)
+ {
+ String msg = "Unable to initialize the "+this.method+" request for ["+url+"]";
+ throw new YADAQueryConfigurationException(msg,e);
+ }
+ }
+
/**
* Gets the input stream from the {@link URLConnection} and stores it in
* the {@link YADAQueryResult} in {@code yq}
+ * @throws YADASecurityException if authentication fails
* @see com.novartis.opensource.yada.adaptor.Adaptor#execute(com.novartis.opensource.yada.YADAQuery)
*/
@Override
- public void execute(YADAQuery yq) throws YADAAdaptorExecutionException
+ public void execute(YADAQuery yq) throws YADAAdaptorExecutionException, YADASecurityException
{
+ //
+ // THINGS ORGANIZED IN HERE:
+ //
+ // Type of request
+ // This will determine the 'build' method in the HttpRequestFactory (i.e., buildGet, buildPost, etc)
+ //
+ // Type of authentication
+ // Basic, Oauth, None
+ //
+ // Post content
+ // Cookies
+ // Headers
+ // Proxy (config'd in JVM for now)
+ //
+
+ // 1) Override method type if necessary
+ if(yq.getHttpHeaders().has(H_X_HTTP_METHOD_OVERRIDE))
+ {
+ this.method = yq.getHttpHeaders().getString(H_X_HTTP_METHOD_OVERRIDE);
+ l.debug("Resetting method to ["+this.method+"]");
+ }
+
+ // 2) set 'count' to false until the algo for counting rest response rows is impl.
resetCountParameter(yq);
+
+ // 3) get the data array from the YADAQuery
+ //
+ // Remember:
+ // A row is a set of YADA URL parameter values, e.g.,
+ //
+ // x,y,z in this:
+ // ...yada/q/queryname/p/x,y,z
+ // so 1 row
+ //
+ // or each of {col1:x,col2:y,col3:z} and {col1:a,col2:b,col3:c} in this:
+ // ...j=[{qname:queryname,DATA:[{col1:x,col2:y,col3:z},{col1:a,col2:b,col3:c}]}]
+ // so 2 rows
+ //
int rows = yq.getData().size() > 0 ? yq.getData().size() : 1;
- /*
- * Remember:
- * A row is an set of YADA URL parameter values, e.g.,
- *
- * x,y,z in this:
- * ...yada/q/queryname/p/x,y,z
- * so 1 row
- *
- * or each of {col1:x,col2:y,col3:z} and {col1:a,col2:b,col3:c} in this:
- * ...j=[{qname:queryname,DATA:[{col1:x,col2:y,col3:z},{col1:a,col2:b,col3:c}]}]
- * so 2 rows
- */
- URL url;
- URLConnection conn;
- HttpURLConnection hConn = null;
+
+
+ // 4) process the url
+ String urlString;
+ HttpRequest request = null;
+
for(int row=0;row