All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.novartis.opensource.yada.adaptor.RESTAdaptor Maven / Gradle / Ivy

The newest version!
/**
 * Copyright 2016 Novartis Institutes for BioMedical Research Inc.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.novartis.opensource.yada.adaptor;

import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.HttpCookie;
import java.net.URL;
import java.net.URLConnection;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.UnrecoverableEntryException;
import java.security.cert.CertificateException;
import java.util.Base64;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

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.auth.oauth2.BearerToken;
import com.google.api.client.auth.oauth2.Credential;
import com.google.api.client.auth.oauth2.TokenResponse;
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.HttpExecuteInterceptor;
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.HttpResponseException;
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 com.novartis.opensource.yada.plugin.Authorization;

import org.json.JSONException;
import org.json.JSONObject;

/**
 * For connecting to REST endpoints with YADA.
 * 
 * @author David Varon
 *
 */
public class RESTAdaptor extends Adaptor implements Authorization {

  /**
   * Local logger handle
   */
  private static Logger l = Logger.getLogger(RESTAdaptor.class);

  /**
   * Constant equal to: {@value}. The character set name.
   * 
   * @since 8.7.0
   */
  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";

  /**
   * 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";

  /**
   * Constant equal to: {@value}. The {@code oauth} request parameter property
   * name.
   * 
   * @since 8.7.0
   */
  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}. The {@code oauth2} request parameter property
   * name.
   * 
   * @since 9.2.0
   */
  private final static String OAUTH2_CLIENTID     = "client_id";
  /**
   * Constant equal to: {@value}. The {@code oauth2} request parameter property
   * name.
   * 
   * @since 9.2.0
   */
  private final static String OAUTH2_SCOPE        = "scope";
  /**
   * Constant equal to: {@value}. The {@code oauth2} request parameter property
   * name.
   * 
   * @since 9.2.0
   */
  private final static String OAUTH2_CLIENTSECRET = "client_secret";
  /**
   * Constant equal to: {@value}. The {@code oauth2} request parameter property
   * name.
   * 
   * @since 9.2.0
   */
  private final static String OAUTH2_GRANTTYPE    = "grant_type";
  /**
   * Constant equal to: {@value}. The {@code oauth2} request parameter property
   * name.
   * 
   * @since 9.2.0
   */
  private final static String OAUTH2_TOKENURL     = "token_url";
  /**
   * Constant equal to: {@value}. The {@code oauth2} response parameter property
   * name.
   * 
   * @since 9.2.0
   */
  private final static String OAUTH2_ACCESSTOKEN  = "access_token";
  /**
   * Constant equal to: {@value}. The {@code oauth2} response parameter property
   * name.
   * 
   * @since 9.2.0
   */
  private final static String OAUTH2_EXPIRESIN    = "expires_in";
  /**
   * Constant equal to: {@value}. The {@code oauth2} response parameter property
   * name.
   * 
   * @since 9.2.0
   */
  private final static String OAUTH2_TOKENTYPE    = "token_type";

  /**
   * Constant equal to: {@value}
   */
  protected final static String PARAM_SYMBOL_RX = "([\\/=:~]|%(?:2F|3D|3A|7E))(\\?[idvnt])";

  /**
   * Constant equal to: {@value} {@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}. Workaround for requests using {@code HTTP PATCH}
   * 
   * @since 8.5.0
   */
  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";

  /**
   * Constant equal to: {@value}.
   * 
   * @since 8.7.4
   */
  private static final int HTTP_STATUS_401 = 401;

  /**
   * Constant equal to: {@value}.
   * 
   * @since 8.7.4
   */
  private static final int HTTP_STATUS_403 = 403;

  /**
   * Constant equal to: {@value}.
   * 
   * @since 9.3.4
   */
  private static final int HTTP_STATUS_404 = 404;

  /**
   * Variable to hold the proxy server string if necessary
   */
  private String proxy = null;

  /**
   * Variable to hold the oauth parameters
   */
  private JSONObject oauth = null;

  /**
   * Variable to hold the oauth2 parameters
   * 
   * @since 9.2.0
   */
  private JSONObject oauth2 = null;

  /**
   * Variable denoting the HTTP method
   * 
   * @since 8.5.0
   */
  private String method = YADARequest.METHOD_GET;

  /**
   * Variable denoting the mimetype of the requested resources, where applicable
   * 
   * @since 9.0.0
   */
  private String mimeType = "";

  /**
   * Storage for query properties stored in application configuration
   * 
   * @since 9.3.0
   */
  private Properties props = null;

  /**
   * Default constructor
   */
  public RESTAdaptor() {
    super();
    l.debug("Initializing RESTAdaptor");
  }

  /**
   * The yadaReq constructor called by
   * {@link com.novartis.opensource.yada.QueryManager#endowQuery}
   * 
   * @param yadaReq YADA request configuration
   */
  public RESTAdaptor(YADARequest yadaReq) {
    super(yadaReq);
    if (yadaReq.getMethod() != null && !yadaReq.getMethod().equals(YADARequest.METHOD_GET))
    {
      this.method = yadaReq.getMethod();
    }

    if (yadaReq.getProxy() != null && !yadaReq.getProxy().equals(""))
    {
      this.proxy = yadaReq.getProxy();
    }

    if (yadaReq.getOAuth() != null)
    {
      setOAuth(yadaReq.getOAuth());
    }
  }

  /**
   * Standard mutator for variable
   * 
   * @param oauth the JSONObject store in the {@link YADARequest}
   * @since 8.7.1
   */
  private void setOAuth(JSONObject oauth) {
    this.oauth = oauth;
  }

  /**
   * Standard mutator for variable which converts {@link String} to
   * {@link JSONObject}
   * 
   * @param yq The {@link YADAQuery} containing the {@code oauth} or {@code o}
   *           parameter
   * @since 8.7.1
   */
  private void setOAuth(YADAQuery yq) {
    this.oauth = new JSONObject(yq.getYADAQueryParamValue(YADARequest.PS_OAUTH)[0]);
  }

  /**
   * Standard mutator for variable
   * 
   * @param oauth2 the JSONObject store in the {@link YADARequest}
   * @since 9.2.0
   */
  private void setOAuth2(JSONObject oauth2) {
    this.oauth2 = oauth2;
  }

  /**
   * Standard mutator for variable which converts {@link String} to
   * {@link JSONObject}. Looks in query configuration first, if not there, looks
   * in app config for o2 config data.
   * 
   * @param yq The {@link YADAQuery} containing the {@code oauth} or {@code o}
   *           parameter
   * @since 8.7.1
   */
  private void setOAuth2(YADAQuery yq) {
    String yqParamValue = yq.getYADAQueryParamValue(YADARequest.PS_OAUTH2)[0];
    this.setOAuth2(new JSONObject(yqParamValue));
  }

  /**
   * Tests if proxy has been set in {@link YADARequest#setProxy(String[])}
   * 
   * @return {@code true} if {@link #proxy} is not null or an empty {@link String}
   */
  protected boolean hasProxy() {
    if (this.proxy != null && !this.proxy.equals(""))
      return true;
    return false;
  }

  /**
   * Creates the {@link YADAQueryResult} and adds it to the {@link YADAQuery}
   * 
   * @param yq The {@link YADAQuery} to process
   * @since 8.7.0
   */
  private void setYADAQueryResultForYADAQuery(YADAQuery yq) {
    yq.setResult();
    YADAQueryResult yqr = yq.getResult();
    yqr.setApp(yq.getApp());
  }

  /**
   * Replaces YADA markup in the url query string with values from the request
   * 
   * @param yq  The {@link YADAQuery} to process
   * @param row The current collection of query parameter values
   * @return the fully fleshed-out query string
   * @since 8.7.0
   */
  private String setPositionalParameterValues(YADAQuery yq, int row) {
    String       urlStr = yq.getUrl(row);
    Matcher      m      = Pattern.compile(PARAM_SYMBOL_RX).matcher(urlStr);
    StringBuffer sb     = new StringBuffer();
    int          i      = 0;
    while (m.find())
    {
      String param = yq.getVals(row).get(i++);
      String repl  = m.group(1) + param;
      m.appendReplacement(sb, repl);
    }
    m.appendTail(sb);
    urlStr = sb.toString();
    l.debug("REST url w/params: [" + urlStr + "]");
    return urlStr;
  }

  /**
   * 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_OAUTH2) || yq.hasParam(YADARequest.PL_OAUTH2) || yq.hasParam(YADARequest.PS_OAUTH)
        || yq.hasParam(YADARequest.PL_OAUTH))
    {
      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);
      }

      if (yq.hasParam(YADARequest.PS_OAUTH2) || yq.hasParam(YADARequest.PL_OAUTH2))
      {
        if (null == this.oauth2) // not in the request
          setOAuth2(yq);
        return setAuthenticationOAuth2(url);
      }
      else if (yq.hasParam(YADARequest.PS_OAUTH) || yq.hasParam(YADARequest.PL_OAUTH))
      {

        if (null == this.oauth) // not in the request
          setOAuth(yq);
        return setAuthenticationOAuth(url);
      }
    }
    else if (yq.hasParam(YADARequest.PS_HTTPHEADERS) || yq.hasParam(YADARequest.PL_HTTPHEADERS))
    {
      String paramVal = yq.getParam(YADARequest.PS_HTTPHEADERS).get(0).getValue();
      if (null == paramVal || "".contentEquals(paramVal))
      {
        paramVal = yq.getParam(YADARequest.PL_HTTPHEADERS).get(0).getValue();
      }
      JSONObject auth = new JSONObject(paramVal);
      if (auth.has("Authorization"))
      {
        return setAuthenticationToken(url, auth.getString("Authorization"));
      }
      else if (url.getUserInfo() != null)
      {
        return setAuthenticatonBasic(url);
      }

    }
    else if (url.getUserInfo() != null)
    {
      return setAuthenticatonBasic(url);
    }
    return null;
  }

  /**
   * 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 PrivateKey getPrivateKey(String alias) throws NoSuchAlgorithmException, KeyStoreException,
      CertificateException, IOException, UnrecoverableEntryException {
    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;
  }

  /**
   * Creates the {@link Credential} {@link HttpRequestInitializer}. This method
   * relies on inclusion of {@link YADARequest#getOAuth2()} which returns a
   * {@link JSONObject} containing:
   * 
   * 
    *
  • token_url
  • *
  • client_id
  • *
  • scope
  • *
  • client_secret
  • *
  • grant_type
  • *
* * After retrieval of the request token using the above parameters, the access * token will be stored in the {@link Authorization#YADA_CREDENTIAL_CACHE} using * the {@code client_secret} as a key, using the token expiry as the cache entry * ttl. * * The cache is checked before requesting a new token. * * @param url the desired REST endpoint * @return the initializer to pass to the requestFactory * @throws YADASecurityException if the credentials settings fail to result in * obtaining a valid token * @since 9.2.0 */ private HttpRequestInitializer setAuthenticationOAuth2(GenericUrl url) throws YADASecurityException { Credential credential = (Credential) getCacheEntry(YADA_CREDENTIAL_CACHE, oauth2.getString(OAUTH2_CLIENTID)); if (credential == null) { HttpContent content = null; HttpRequestFactory requestFactory = new NetHttpTransport().createRequestFactory(); GenericUrl tokenUrl = new GenericUrl(oauth2.getString(OAUTH2_TOKENURL)); StringBuilder payload = new StringBuilder(); payload.append(String.format("%s=%s", OAUTH2_CLIENTID, oauth2.getString(OAUTH2_CLIENTID))); payload.append(String.format("&%s=%s", OAUTH2_SCOPE, oauth2.getString(OAUTH2_SCOPE))); payload.append(String.format("&%s=%s", OAUTH2_CLIENTSECRET, oauth2.getString(OAUTH2_CLIENTSECRET))); payload.append(String.format("&%s=%s", OAUTH2_GRANTTYPE, oauth2.getString(OAUTH2_GRANTTYPE))); content = ByteArrayContent.fromString(null, payload.toString()); try { HttpRequest tokenRequest = requestFactory.buildRequest(YADARequest.METHOD_POST, tokenUrl, content); JSONObject tokenResponse = new JSONObject(processResponse(tokenRequest)); TokenResponse tr = new TokenResponse(); tr.setAccessToken(tokenResponse.getString(OAUTH2_ACCESSTOKEN)); tr.setExpiresInSeconds(tokenResponse.getLong(OAUTH2_EXPIRESIN)); tr.setScope(oauth2.getString(OAUTH2_SCOPE)); tr.setTokenType(tokenResponse.getString(OAUTH2_TOKENTYPE)); credential = new Credential(BearerToken.authorizationHeaderAccessMethod()).setFromTokenResponse(tr); setCacheEntry(YADA_CREDENTIAL_CACHE, oauth2.getString(OAUTH2_CLIENTID), credential, tokenResponse.getInt(OAUTH2_EXPIRESIN)); } catch (IOException | JSONException | YADAAdaptorExecutionException e) { String msg = "Unable to initialize the POST request for [" + tokenUrl + "]"; throw new YADASecurityException(msg, e); } } return credential; } /** * Creates the {@link OAuthParameters} {@link HttpRequestInitializer}. This * method relies on inclusion of {@link YADARequest#getOAuth()} which returns a * {@link JSONObject} containing: * *
    *
  • oauth_consumer_key
  • *
  • oauth_signature_method. For now, should always equal * RSA
  • *
  • 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
  • *
* * @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 HttpRequestInitializer setAuthenticationOAuth(GenericUrl url) throws YADASecurityException, YADAQueryConfigurationException { OAuthRsaSigner signer = new OAuthRsaSigner(); try { 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) { 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 { 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); } return oauthParams; } /** * 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 HttpRequestInitializer setAuthenticatonBasic(GenericUrl url) { // 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; } /** * @param url the REST url to process * @param token the auth token stored with the query * @return the interceptor to set the auth header */ private HttpRequestInitializer setAuthenticationToken(GenericUrl url, String token) { class TokenAuthentication implements HttpRequestInitializer, HttpExecuteInterceptor { @Override public void initialize(HttpRequest request) throws IOException { request.setInterceptor((HttpExecuteInterceptor) this); } @Override public void intercept(HttpRequest request) throws IOException { if (token.startsWith("Basic ")) { request.getHeaders().setAuthorization(token); } else { request.getHeaders().setAuthorization("Bearer " + token); } } } ; return new TokenAuthentication(); } /** * Looks for cookie strings passed in the request or stored in the * {@link YADAQuery} and adds them to the {@link HttpRequest} * * @param yq The YADAQuery object * @param request The HttpRequest object * @since 8.7.0 */ private void setCookies(YADAQuery yq, HttpRequest request) { if (yq.getCookies() != null && yq.getCookies().size() > 0) { String cookieStr = ""; for (HttpCookie cookie: yq.getCookies()) { cookieStr += cookie.getName() + "=" + cookie.getValue() + ";"; } HttpHeaders headers = request.getHeaders(); 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 HttpRequest} * * @param yq the {@link YADAQuery} object * @param request the HttpRequest object * @throws YADAQueryConfigurationException when a header can not be set due to * encapsulation, bad arguments or other * invocation target error * @since 8.7.0 */ private void setHeaders(YADAQuery yq, HttpRequest request) throws YADAQueryConfigurationException { if (yq.getHttpHeaders() != null && yq.getHttpHeaders().length() > 0) { HttpHeaders headers = request.getHeaders(); l.debug("Processing custom headers..."); @SuppressWarnings("unchecked") Iterator keys = yq.getHttpHeaders().keys(); while (keys.hasNext()) { String name = keys.next(); String value = yq.getHttpHeaders().getString(name); l.debug("Custom header: " + name + " : " + value); String method = "set" + name.replace("-", ""); try { Class clazz = HttpHeaders.class; Method meth = clazz.getMethod(method, java.lang.String.class); meth.invoke(headers, value); } catch (NoSuchMethodException | SecurityException e) { String msg = "There is no method named [" + method + "]. It could be a case issue." + "Method names are camel-cased, and header names should have initial " + " caps, and hyphens instead of spaces."; l.warn(msg); l.warn("Trying to use generic setter"); headers.set(name, value); } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { String msg = "The [" + name + "] header could not be set."; throw new YADAQueryConfigurationException(msg, e); } request.setHeaders(headers); } } } /** * Standard accessor for ivar * * @return the mimetype {@link String} * @since 9.0.0 */ public String getMimeType() { return mimeType; } /** * Standard mutator for ivar * * @param mimeType the mimeType {@link String} to store * @since 9.0.0 */ public void setMimeType(String mimeType) { this.mimeType = mimeType; } /** * 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 */ 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 request the request object * @return a String containing the response data * @throws YADAAdaptorExecutionException when reading the input stream fails * @throws YADASecurityException when the service returns a 401 or 403 * @since 8.7.0 */ private String processResponse(HttpRequest request) throws YADAAdaptorExecutionException, YADASecurityException { String result = ""; HttpResponse response = null; try { response = request.execute(); } catch (HttpResponseException e) { l.error(e.getStatusCode() + ": " + e.getMessage()); int code = e.getStatusCode(); if (code == HTTP_STATUS_401 || code == HTTP_STATUS_403) { String msg = "Unauthorized: " + e.getStatusMessage(); throw new YADASecurityException(msg, e); } else if (code == HTTP_STATUS_404) { throw new YADAAdaptorExecutionException(e.getMessage(), e); } } catch (IOException e) { String msg = "Unable to execute request."; throw new YADAAdaptorExecutionException(msg, e); } if (null != response && (null == this.getMimeType() || this.getMimeType().contentEquals(""))) { this.setMimeType(response.getContentType()); } if (this.getMimeType().startsWith("text") || this.getMimeType().matches("application/(?:json|ld\\+json|x-httpd-php|rtf|x-sh|xml).*")) { try (BufferedReader in = new BufferedReader(new InputStreamReader(response.getContent()))) { String inputLine; while ((inputLine = in.readLine()) != null) { result += String.format("%1s%n", inputLine); } } catch (IOException e) { String msg = "There was a problem reading from the response's input stream"; throw new YADAAdaptorExecutionException(msg, e); } } else if (null != this.getMimeType() && !this.getMimeType().contentEquals("")) { this.getServiceParameters().setFormat(new String[] { YADARequest.FORMAT_BINARY }); try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) { response.download(baos); result = String.format("data:%s;base64, %s", this.getMimeType(), Base64.getEncoder().encodeToString(baos.toByteArray())); } catch (IOException e) { String msg = "There was a problem reading from the response's input stream or encoding to Base64"; throw new YADAAdaptorExecutionException(msg, e); } } l.debug(result); return result; } /** * 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(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, 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; // 4) process the url String urlString; HttpRequest request = null; for (int row = 0; row < rows; row++) { // creates result array and assigns it setYADAQueryResultForYADAQuery(yq); try { // replace yada markup urlString = setPositionalParameterValues(yq, row); // get body content String payload = null; // check for payload in params if (yq.getParam(YADA_PAYLOAD) != null) { payload = yq.getParam(YADA_PAYLOAD).get(0).getValue(); } if (yq.getData().size() > 0 && yq.getDataRow(row).containsKey(YADA_PAYLOAD)) { if (null == payload) { payload = yq.getDataRow(row).get(YADA_PAYLOAD)[0]; } else { payload += yq.getDataRow(row).get(YADA_PAYLOAD)[0]; } } // init url GenericUrl url = new GenericUrl(urlString); // build request, handling auth if necessary request = buildRequest(yq, payload, url); // cookies setCookies(yq, request); // headers setHeaders(yq, request); // inject body content if necessary // handlePostPutPatch(request, yq, row); // debug logRequest(request); // process the response String result = processResponse(request); // store the response in the YADAQueryResult yq.getResult().addResult(row, result); } catch (YADAQueryConfigurationException e) { String msg = "The YADAQuery was configured improperly. Check YADA parameters, especially 'oauth'."; throw new YADAAdaptorExecutionException(msg, e); } } } /** * Constructs a single url string containing the REST endpoint and query string * * @param yq the {@link YADAQuery} containing the source code and metadata * required to construct an executable query * @return a string containing the entire REST url */ @Override public String build(YADAQuery yq) { this.setProps(ConnectionFactory.getConnectionFactory().getWsSourceMap().get(yq.getApp())); // List yqParams = new ArrayList<>(); // String paramStr = this.getProps().getProperty("params"); // if (paramStr != null) // { // JSONArray yqp = new JSONArray(paramStr); // for (int i = 0; i < yqp.length(); i++) // { // JSONObject jo = yqp.getJSONObject(i); // YADAParam yp = new YADAParam(); // yp.setName(jo.getString("name")); // yp.setValue(jo.getString("value")); // yp.setRule(jo.getInt("rule")); // yqParams.add(yp); // // override method if param implies such // if(jo.getString("name").contentEquals(YADARequest.PL_METHOD) // || jo.getString("name").contentEquals(YADARequest.PS_METHOD)) // { // this.method = jo.getString("value"); // } // } // yq.setYADAQueryParams(yqParams); // } String conf = this.getProps().getProperty(ConnectionFactory.YADA_CONF_SOURCE); String uri = yq.getYADACode(); return conf + uri; } @Override public void authorize(String payload) throws YADASecurityException { } @Override public void authorize() throws YADASecurityException { } @Override public void authorizeYADARequest(YADARequest yadaReq, String result) throws YADASecurityException { } /** * Standard accessor for variable * * @return query properties stored in application conf * @since 9.3.0 */ public Properties getProps() { return props; } /** * standard mutator for variable * * @param props query properties stored in application conf * @since 9.3.0 */ public void setProps(Properties props) { this.props = props; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy