Package com.thetransactioncompany.jsonrpc2.client

Source Code of com.thetransactioncompany.jsonrpc2.client.JSONRPC2Session

package com.thetransactioncompany.jsonrpc2.client;


import java.io.IOException;
import java.io.OutputStreamWriter;

import java.net.CookieManager;
import java.net.CookiePolicy;
import java.net.HttpCookie;
import java.net.HttpURLConnection;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLConnection;

import java.security.SecureRandom;

import java.security.cert.X509Certificate;

import java.util.Collections;
import java.util.List;

import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

import com.thetransactioncompany.jsonrpc2.JSONRPC2Notification;
import com.thetransactioncompany.jsonrpc2.JSONRPC2ParseException;
import com.thetransactioncompany.jsonrpc2.JSONRPC2Request;
import com.thetransactioncompany.jsonrpc2.JSONRPC2Response;


/**
* Sends requests and / or notifications to a specified JSON-RPC 2.0 server
* URL. The JSON-RPC 2.0 messages are dispatched by means of HTTP(S) POST.
* This class is thread-safe.
*
* <p>The client-session class has a number of {@link JSONRPC2SessionOptions
* optional settings}. To change them pass a modified options instance to the
* {@link #setOptions setOptions()} method.
*
* <p>Example JSON-RPC 2.0 client session:
*
* <pre>
* // First, import the required packages:
*
* // The Client sessions package
* import com.thetransactioncompany.jsonrpc2.client.*;
*
* // The Base package for representing JSON-RPC 2.0 messages
* import com.thetransactioncompany.jsonrpc2.*;
*
* // The JSON Smart package for JSON encoding/decoding (optional)
* import net.minidev.json.*;
*
* // For creating URLs
* import java.net.*;
*
* // ...
*
*
* // Creating a new session to a JSON-RPC 2.0 web service at a specified URL
*
* // The JSON-RPC 2.0 server URL
* URL serverURL = null;
*
* try {
*   serverURL = new URL("http://jsonrpc.example.com:8080");
*  
* } catch (MalformedURLException e) {
*   // handle exception...
* }
*
* // Create new JSON-RPC 2.0 client session
*  JSONRPC2Session mySession = new JSONRPC2Session(serverURL);
*
*
* // Once the client session object is created, you can use to send a series
* // of JSON-RPC 2.0 requests and notifications to it.
*
* // Sending an example "getServerTime" request:
*
*  // Construct new request
*  String method = "getServerTime";
*  int requestID = 0;
*  JSONRPC2Request request = new JSONRPC2Request(method, requestID);
*
*  // Send request
*  JSONRPC2Response response = null;
*
*  try {
*          response = mySession.send(request);
*
*  } catch (JSONRPC2SessionException e) {
*
*          System.err.println(e.getMessage());
*          // handle exception...
*  }
*
*  // Print response result / error
*  if (response.indicatesSuccess())
*   System.out.println(response.getResult());
*  else
*   System.out.println(response.getError().getMessage());
*
* </pre>
*
* @author Vladimir Dzhuvinov
*/
public class JSONRPC2Session {


  /**
   * The server URL, which protocol must be HTTP or HTTPS.
   *
   * <p>Example URL: "http://jsonrpc.example.com:8080"
   */
  private URL url;


  /**
   * The client-session options.
   */
  private JSONRPC2SessionOptions options;


  /**
   * Custom HTTP URL connection configurator.
   */
  private ConnectionConfigurator connectionConfigurator;
 
 
  /**
   * Optional HTTP raw response inspector.
   */
  private RawResponseInspector responseInspector;
 
 
  /**
   * Optional HTTP cookie manager.
   */
  private CookieManager cookieManager;


  /**
   * Trust-all-certs (including self-signed) SSL socket factory.
   */
  private static SSLSocketFactory trustAllSocketFactory = createTrustAllSocketFactory();


  /**
   * Creates a new client session to a JSON-RPC 2.0 server at the
   * specified URL. Uses a default {@link JSONRPC2SessionOptions}
   * instance.
   *
   * @param url The server URL, e.g. "http://jsonrpc.example.com:8080".
   *            Must not be {@code null}.
   */
  public JSONRPC2Session(final URL url) {

    if (! url.getProtocol().equalsIgnoreCase("http") &&
        ! url.getProtocol().equalsIgnoreCase("https")   )
      throw new IllegalArgumentException("The URL protocol must be HTTP or HTTPS");

    this.url = url;

    // Default session options
    options = new JSONRPC2SessionOptions();

    // No initial connection configurator
    connectionConfigurator = null;
  }
 
 
  /**
   * Creates a trust-all-certificates SSL socket factory. Encountered
   * exceptions are not rethrown.
   *
   * @return The SSL socket factory.
   */
  public static SSLSocketFactory createTrustAllSocketFactory() {
 
    TrustManager[] trustAllCerts = new TrustManager[] {
     
      new X509TrustManager() {

        public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[]{}; }

        public void checkClientTrusted(X509Certificate[] certs, String authType) { }

        public void checkServerTrusted(X509Certificate[] certs, String authType) { }
      }
    };

    try {
      SSLContext sc = SSLContext.getInstance("SSL");
      sc.init(null, trustAllCerts, new SecureRandom());
      return sc.getSocketFactory();

    } catch (Exception e) {
     
      // Ignore
      return null;
    }
  }


  /**
   * Gets the JSON-RPC 2.0 server URL.
   *
   * @return The server URL.
   */
  public URL getURL() {

    return url;
  }


  /**
   * Sets the JSON-RPC 2.0 server URL.
   *
   * @param url The server URL. Must not be {@code null}.
   */
  public void setURL(final URL url) {

    if (url == null)
      throw new IllegalArgumentException("The server URL must not be null");
   
    this.url = url;
  }


  /**
   * Gets the JSON-RPC 2.0 client session options.
   *
   * @return The client session options.
   */
  public JSONRPC2SessionOptions getOptions() {

    return options;
  }


  /**
   * Sets the JSON-RPC 2.0 client session options.
   *
   * @param options The client session options, must not be {@code null}.
   */
  public void setOptions(final JSONRPC2SessionOptions options) {

    if (options == null)
      throw new IllegalArgumentException("The client session options must not be null");

    this.options = options;
  }


  /**
   * Gets the custom HTTP URL connection configurator.
   *
   * @since 1.5
   *
   * @return The connection configurator, {@code null} if none is set.
   */
  public ConnectionConfigurator getConnectionConfigurator() {

    return connectionConfigurator;
  }


  /**
   * Specifies a custom HTTP URL connection configurator. It will be
   * {@link ConnectionConfigurator#configure applied} to each new HTTP
   * connection after the {@link JSONRPC2SessionOptions session options}
   * are applied and before the connection is established.
   *
   * <p>This method may be used to set custom HTTP request headers,
   * timeouts or other properties.
   *
   * @since 1.5
   *
   * @param connectionConfigurator A custom HTTP URL connection
   *                               configurator, {@code null} to remove
   *                               a previously set one.
   */
  public void setConnectionConfigurator(final ConnectionConfigurator connectionConfigurator) {

    this.connectionConfigurator = connectionConfigurator;
  }
 
 
  /**
   * Gets the optional inspector for the raw HTTP responses.
   *
   * @since 1.6
   *
   * @return The optional inspector for the raw HTTP responses,
   *         {@code null} if none is set.
   */
  public RawResponseInspector getRawResponseInspector() {
   
    return responseInspector;
  }
 
 
  /**
   * Specifies an optional inspector for the raw HTTP responses to
   * JSON-RPC 2.0 requests and notifications. Its
   * {@link RawResponseInspector#inspect inspect} method will be called
   * upon reception of a HTTP response.
   *
   * <p>You can use the {@link RawResponseInspector} interface to
   * retrieve the unparsed response content and headers.
   *
   * @since 1.6
   *
   * @param responseInspector An optional inspector for the raw HTTP
   *                          responses, {@code null} to remove a
   *                          previously set one.
   */
  public void setRawResponseInspector(final RawResponseInspector responseInspector) {
   
    this.responseInspector = responseInspector;
  }
 
 
  /**
   * Gets all non-expired HTTP cookies currently stored in the client.
   *
   * @return The HTTP cookies, or empty list if none were set by the
   *         server or cookies are not
   *         {@link JSONRPC2SessionOptions#acceptCookies accepted}.
   */
  public List<HttpCookie> getCookies() {
   
    if (cookieManager == null) {

      List<HttpCookie> emptyList = Collections.emptyList();
      return emptyList;
    }

    return cookieManager.getCookieStore().getCookies();
 


  /**
   * Applies the required headers to the specified URL connection.
   *
   * @param con The URL connection which must be open.
   *
   * @throws JSONRPC2SessionException If an exception is encountered.
   */
  private void applyHeaders(final URLConnection con)
    throws JSONRPC2SessionException {

    // Expect UTF-8 for JSON
    con.setRequestProperty("Accept-Charset", "UTF-8");

    // Add "Content-Type" header?
    if (options.getRequestContentType() != null)
      con.setRequestProperty("Content-Type", options.getRequestContentType());

    // Add "Origin" header?
    if (options.getOrigin() != null)
      con.setRequestProperty("Origin", options.getOrigin());

    // Add "Accept-Encoding: gzip, deflate" header?
    if (options.enableCompression())
      con.setRequestProperty("Accept-Encoding", "gzip, deflate");
   
    // Add "Cookie" headers?
    if (options.acceptCookies()) {

      StringBuilder buf = new StringBuilder();
     
      for (HttpCookie cookie: getCookies()) {

        if (buf.length() > 0)
          buf.append("; ");

        buf.append(cookie.toString());
      }

      con.setRequestProperty("Cookie", buf.toString());
    }
  }
 
 
  /**
   * Creates and configures a new URL connection to the JSON-RPC 2.0
   * server endpoint according to the session settings.
   *
   * @return The URL connection, configured and ready for output (HTTP
   *         POST).
   *
   * @throws JSONRPC2SessionException If the URL connection couldn't be
   *                                  created or configured.
   */
  private URLConnection createURLConnection()
    throws JSONRPC2SessionException {
   
    // Open HTTP connection
    URLConnection con = null;

    try {
      // Use proxy?
      if (options.getProxy() != null)
        con = url.openConnection(options.getProxy());
      else
        con = url.openConnection();

    } catch (IOException e) {

      throw new JSONRPC2SessionException(
          "Network exception: " + e.getMessage(),
          JSONRPC2SessionException.NETWORK_EXCEPTION,
          e);
    }
   
    con.setConnectTimeout(options.getConnectTimeout());
    con.setReadTimeout(options.getReadTimeout());

    applyHeaders(con);

    // Set POST mode
    con.setDoOutput(true);

    // Set trust all certs SSL factory?
    if (con instanceof HttpsURLConnection && options.trustsAllCerts()) {
   
      if (trustAllSocketFactory == null)
        throw new JSONRPC2SessionException("Couldn't obtain trust-all SSL socket factory");
   
      ((HttpsURLConnection)con).setSSLSocketFactory(trustAllSocketFactory);
    }

    // Apply connection configurator?
    if (connectionConfigurator != null)
      connectionConfigurator.configure((HttpURLConnection)con);
   
    return con;
  }
 
 
  /**
   * Posts string data (i.e. JSON string) to the specified URL
   * connection.
   *
   * @param con  The URL connection. Must be in HTTP POST mode. Must not
   *             be {@code null}.
   * @param data The string data to post. Must not be {@code null}.
   *
   * @throws JSONRPC2SessionException If an I/O exception is encountered.
   */
  private static void postString(final URLConnection con, final String data)
    throws JSONRPC2SessionException {
   
    try {
      OutputStreamWriter wr = new OutputStreamWriter(con.getOutputStream(), "UTF-8");
      wr.write(data);
      wr.flush();
      wr.close();

    } catch (IOException e) {

      throw new JSONRPC2SessionException(
          "Network exception: " + e.getMessage(),
          JSONRPC2SessionException.NETWORK_EXCEPTION,
          e);
    }
  }
 
 
  /**
   * Reads the raw response from an URL connection (after HTTP POST).
   * Invokes the {@link RawResponseInspector} if configured and stores
   * any cookies {@link JSONRPC2SessionOptions#storeCookies if required}.
   *
   * @param con The URL connection. It should contain ready data for
   *            retrieval. Must not be {@code null}.
   *
   * @return The raw response.
   *
   * @throws JSONRPC2SessionException If an exception is encountered.
   */
  private RawResponse readRawResponse(final URLConnection con)
    throws JSONRPC2SessionException {
 
    RawResponse rawResponse = null;
   
    try {
      rawResponse = RawResponse.parse((HttpURLConnection)con);

    } catch (IOException e) {

      throw new JSONRPC2SessionException(
          "Network exception: " + e.getMessage(),
          JSONRPC2SessionException.NETWORK_EXCEPTION,
          e);
    }
   
    if (responseInspector != null)
      responseInspector.inspect(rawResponse);
   
    if (options.acceptCookies()) {

      // Init cookie manager?
      if (cookieManager == null)
        cookieManager = new CookieManager(null, CookiePolicy.ACCEPT_ALL);

      try {
        cookieManager.put(con.getURL().toURI(), rawResponse.getHeaderFields());

      } catch (URISyntaxException e) {

        throw new JSONRPC2SessionException(
          "Network exception: " + e.getMessage(),
          JSONRPC2SessionException.NETWORK_EXCEPTION,
          e);

      } catch (IOException e) {

        throw new JSONRPC2SessionException(
          "I/O exception: " + e.getMessage(),
          JSONRPC2SessionException.NETWORK_EXCEPTION,
          e);
      }
    }
   
    return rawResponse;
  }


  /**
   * Sends a JSON-RPC 2.0 request using HTTP POST and returns the server
   * response.
   *
   * @param request The JSON-RPC 2.0 request to send. Must not be
   *                {@code null}.
   *
   * @return The JSON-RPC 2.0 response returned by the server.
   *
   * @throws JSONRPC2SessionException On a network error, unexpected HTTP
   *                                  response content type or invalid
   *                                  JSON-RPC 2.0 response.
   */
  public JSONRPC2Response send(final JSONRPC2Request request)
    throws JSONRPC2SessionException {

    // Create and configure URL connection to server endpoint
    URLConnection con = createURLConnection();

    // Send request encoded as JSON
    postString(con, request.toString());

    // Get the response
    RawResponse rawResponse = readRawResponse(con);

    // Check response content type
    String contentType = rawResponse.getContentType();

    if (! options.isAllowedResponseContentType(contentType)) {

      String msg = null;

      if (contentType == null)
        msg = "Missing Content-Type header in the HTTP response";
      else
        msg = "Unexpected \"" + contentType + "\" content type of the HTTP response";

      throw new JSONRPC2SessionException(msg, JSONRPC2SessionException.UNEXPECTED_CONTENT_TYPE);
    }

    // Parse and return the response
    JSONRPC2Response response = null;

    try {
      response = JSONRPC2Response.parse(rawResponse.getContent(),
                            options.preservesParseOrder(),
                options.ignoresVersion(),
                options.parsesNonStdAttributes());

    } catch (JSONRPC2ParseException e) {

      throw new JSONRPC2SessionException(
          "Invalid JSON-RPC 2.0 response",
          JSONRPC2SessionException.BAD_RESPONSE,
          e);
    }

    // Response ID must match the request ID, except for
    // -32700 (parse error), -32600 (invalid request) and
    // -32603 (internal error)

    Object reqID = request.getID();
    Object resID = response.getID();

    if (reqID != null && resID != null && reqID.toString().equals(resID.toString()) ) {
      // ok
    } else if (reqID == null && resID == null) {
      // ok
    } else if (! response.indicatesSuccess() && ( response.getError().getCode() == -32700 ||
           response.getError().getCode() == -32600 ||
           response.getError().getCode() == -32603    )) {
      // ok
    } else {
      throw new JSONRPC2SessionException(
          "Invalid JSON-RPC 2.0 response: ID mismatch: Returned " +
          resID + ", expected " + reqID,
          JSONRPC2SessionException.BAD_RESPONSE);
    }

    return response;
  }


  /**
   * Sends a JSON-RPC 2.0 notification using HTTP POST. Note that
   * contrary to requests, notifications produce no server response.
   *
   * @param notification The JSON-RPC 2.0 notification to send. Must not
   *                     be {@code null}.
   *
   * @throws JSONRPC2SessionException On a network error.
   */
  public void send(final JSONRPC2Notification notification)
    throws JSONRPC2SessionException {

    // Create and configure URL connection to server endpoint
    URLConnection con = createURLConnection();

    // Send notification encoded as JSON
    postString(con, notification.toString());

    // Get the response /for the inspector only/
    RawResponse rawResponse = readRawResponse(con);
  }
}
TOP

Related Classes of com.thetransactioncompany.jsonrpc2.client.JSONRPC2Session

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.