com.ibm.watson.developer_cloud.service.WatsonService.java Source code

Java tutorial

Introduction

Here is the source code for com.ibm.watson.developer_cloud.service.WatsonService.java

Source

/**
 * Copyright 2015 IBM Corp. All Rights Reserved.
 * 
 * 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.ibm.watson.developer_cloud.service;

import com.google.gson.JsonIOException;
import com.google.gson.JsonObject;
import com.google.gson.JsonSyntaxException;
import com.ibm.watson.developer_cloud.http.HttpHeaders;
import com.ibm.watson.developer_cloud.http.HttpStatus;
import com.ibm.watson.developer_cloud.http.RequestBuilder;
import com.ibm.watson.developer_cloud.service.model.GenericModel;
import com.ibm.watson.developer_cloud.util.CredentialUtils;
import com.ibm.watson.developer_cloud.util.RequestUtil;
import com.ibm.watson.developer_cloud.util.ResponseUtil;
import com.squareup.okhttp.Credentials;
import com.squareup.okhttp.Headers;
import com.squareup.okhttp.HttpUrl;
import com.squareup.okhttp.OkHttpClient;
import com.squareup.okhttp.Request;
import com.squareup.okhttp.*;
import com.squareup.okhttp.Request.Builder;

import java.io.IOException;
import java.net.CookieManager;
import java.net.CookiePolicy;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * Watson service abstract common functionality of various Watson Services. It handle authentication
 * and default url
 * 
 * @see <a href="http://www.ibm.com/smarterplanet/us/en/ibmwatson/developercloud/"> IBM Watson
 *      Developer Cloud</a>
 */
public abstract class WatsonService {

    private static final String BASIC = "Basic ";
    private static final Logger log = Logger.getLogger(WatsonService.class.getName());
    private String apiKey;
    private final OkHttpClient client;
    private String endPoint;
    private final String name;
    private Headers defaultHeaders = null;

    protected static final String VERSION = "version";

    /**
     * Instantiates a new Watson service.
     * 
     * @param name the service name
     */
    public WatsonService(String name) {
        this.name = name;
        this.apiKey = CredentialUtils.getAPIKey(name);
        this.client = configureHttpClient();
    }

    /**
     * Configures the HTTP client.
     * 
     * @return the HTTP client
     */
    protected OkHttpClient configureHttpClient() {
        final OkHttpClient client = new OkHttpClient();
        final CookieManager cookieManager = new CookieManager();
        cookieManager.setCookiePolicy(CookiePolicy.ACCEPT_ALL);
        client.setCookieHandler(cookieManager);

        client.setConnectTimeout(60, TimeUnit.SECONDS);
        client.setWriteTimeout(60, TimeUnit.SECONDS);
        client.setReadTimeout(90, TimeUnit.SECONDS);
        return client;
    }

    /**
     * Execute the HTTP request.
     * 
     * @param request the HTTP request
     * 
     * @return the HTTP response
     */
    protected Response execute(Request request) {
        final Builder builder = request.newBuilder();

        // Set service endpoint for relative paths
        if (RequestUtil.isRelative(request)) {
            builder.url(RequestUtil.replaceEndPoint(request.urlString(), getEndPoint()));
        }

        // Set default headers
        if (defaultHeaders != null) {
            for (String key : defaultHeaders.names())
                builder.header(key, defaultHeaders.get(key));
        }

        // Set User-Agent
        builder.header(HttpHeaders.USER_AGENT, getUserAgent());

        // Set Authentication
        setAuthentication(builder);

        final Request newRequest = builder.build();
        Response response;
        log.log(Level.FINEST, "Request to: " + newRequest.urlString());
        try {
            response = client.newCall(newRequest).execute();
        } catch (final IOException e) {
            log.log(Level.SEVERE, "IOException", e);
            throw new RuntimeException(e);
        }

        if (response.isSuccessful()) {
            return response;
        }

        final int status = response.code();

        // There was a Client Error 4xx or a Server Error 5xx
        // Get the error message and create the exception
        final String error = getErrorMessage(response);
        log.log(Level.SEVERE, newRequest.urlString() + ", status: " + status + ", error: " + error);

        switch (status) {
        case HttpStatus.BAD_REQUEST: // HTTP 400
            throw new BadRequestException(error != null ? error : "Bad Request", response);
        case HttpStatus.UNAUTHORIZED: // HTTP 401
            throw new UnauthorizedException("Unauthorized: Access is denied due to invalid credentials", response);
        case HttpStatus.FORBIDDEN: // HTTP 403
            throw new ForbiddenException(error != null ? error : "Forbidden: Service refuse the request", response);
        case HttpStatus.NOT_FOUND: // HTTP 404
            throw new NotFoundException(error != null ? error : "Not found", response);
        case HttpStatus.NOT_ACCEPTABLE: // HTTP 406
            throw new ForbiddenException(error != null ? error : "Forbidden: Service refuse the request", response);
        case HttpStatus.CONFLICT: // HTTP 409
            throw new ConflictException(error != null ? error : "", response);
        case HttpStatus.REQUEST_TOO_LONG: // HTTP 413
            throw new RequestTooLargeException(
                    error != null ? error
                            : "Request too large: The request entity is larger than the server is able to process",
                    response);
        case HttpStatus.UNSUPPORTED_MEDIA_TYPE: // HTTP 415
            throw new UnsupportedException(error != null ? error : "Unsupported Media Type", response);
        case HttpStatus.TOO_MANY_REQUESTS: // HTTP 429
            throw new TooManyRequestsException(error != null ? error : "Too many requests", response);
        case HttpStatus.INTERNAL_SERVER_ERROR: // HTTP 500
            throw new InternalServerErrorException(error != null ? error : "Internal Server Error", response);
        case HttpStatus.SERVICE_UNAVAILABLE: // HTTP 503
            throw new ServiceUnavailableException(error != null ? error : "Service Unavailable", response);
        default: // other errors
            throw new ServiceResponseException(status, error, response);
        }
    }

    /**
     * Executes the HTTP Request, reads and parses the HTTP Response.
     * 
     * @param <T> the POJO class that represents the response
     * @param request the request
     * @param returnType the return type
     * @return the POJO object
     */
    protected <T extends GenericModel> T executeRequest(Request request, Class<T> returnType) {
        final Response response = execute(request);
        //    System.out.println("response = " + response);
        //    System.out.println("response headers = " + response.headers());
        //    System.out.println("response body = " + response.body());
        // don't read body here, you can do that only once
        //    try {
        //      System.out.println("response body source = " + response.body().source().readUtf8());
        //      System.out.println("response body source = " + response.body().source().readUtf8());
        //      System.out.println("response body source = " + response.body().source().readUtf8());
        //    } catch (IOException e) {
        //      System.err.println("body source fetching failed dude");
        //      e.printStackTrace();
        //    }
        //    try {
        //      System.out.println("response body = " + response.body().string());
        //    } catch (IOException e) {
        //      System.err.println("body fetching failed dude");
        //      e.printStackTrace();
        //    }
        return ResponseUtil.getObject(response, returnType);
    }

    /**
     * Execute the HTTP request and discard the response. Use this when you don't want to get the
     * response but you want to make sure we read it so that the underline connection is released
     * 
     * @param request the request
     */
    protected void executeWithoutResponse(Request request) {
        final Response response = execute(request);

        // close the response
        try {
            response.body().close();
        } catch (final IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * Gets the API key.
     * 
     * 
     * @return the API key
     */
    protected String getApiKey() {
        return apiKey;
    }

    /**
     * Gets the API end point.
     * 
     * 
     * @return the API end point
     */
    public String getEndPoint() {
        return endPoint;
    }

    /**
     * Gets an authorization token that can be use to authorize API calls.
     * 
     * 
     * @return the token
     */
    public String getToken() {
        HttpUrl url = HttpUrl.parse(getEndPoint()).newBuilder().setPathSegment(0, "authorization").build();
        Request request = RequestBuilder.get(url + "/v1/token").withQuery("url", getEndPoint()).build();
        Response response = execute(request);
        return ResponseUtil.getJsonObject(response).get("token").getAsString();
    }

    /**
     * Gets the error message from a JSON response
     * 
     * <pre>
     * {
     *   code: 400
     *   error: 'bad request'
     * }
     * </pre>
     * 
     * @param response the HTTP response
     * @return the error message from the JSON object
     */
    private String getErrorMessage(Response response) {
        String error = ResponseUtil.getString(response);
        try {

            final JsonObject jsonObject = ResponseUtil.getJsonObject(error);
            if (jsonObject.has("error")) {
                error = jsonObject.get("error").getAsString();
            } else if (jsonObject.has("error_message")) {
                error = jsonObject.get("error_message").getAsString();
            } else if (jsonObject.has("message")) {
                error = jsonObject.get("message").getAsString();
            }
        } catch (final JsonIOException e) {
            // Ignore JsonIOException and use fallback String version of response
        } catch (final JsonSyntaxException e) {
            // Ignore JsonSyntaxException and use fallback String version of response
        }

        return error;
    }

    /**
     * Gets the name.
     * 
     * @return the name
     */
    public String getName() {
        return name;
    }

    /**
     * Gets the user agent.
     * 
     * 
     * @return the user agent
     */
    private final String getUserAgent() {
        return "watson-developer-cloud-java-sdk-2.8.0";
    }

    /**
     * Sets the API key.
     * 
     * @param apiKey the new API key
     */
    public void setApiKey(String apiKey) {
        this.apiKey = apiKey;
    }

    /**
     * Sets the authentication.
     * 
     * @param builder the new authentication
     */
    protected void setAuthentication(Builder builder) {
        if (getApiKey() == null) {
            throw new IllegalArgumentException("apiKey or username and password were not specified");
        }
        builder.addHeader(HttpHeaders.AUTHORIZATION, apiKey.startsWith(BASIC) ? apiKey : BASIC + apiKey);
    }

    /**
     * Sets the end point.
     * 
     * @param endPoint the new end point
     */
    public void setEndPoint(String endPoint) {
        this.endPoint = endPoint;
    }

    /**
     * Sets the username and password.
     * 
     * @param username the username
     * @param password the password
     */
    public void setUsernameAndPassword(String username, String password) {
        apiKey = Credentials.basic(username, password);
    }

    /**
     * Set the default headers to be used on every HTTP request.
     * 
     * @param headers name value pairs of headers
     */
    public void setDefaultHeaders(Map<String, String> headers) {
        defaultHeaders = Headers.of(headers);
    }

    /*
     * (non-Javadoc)
     * 
     * @see java.lang.Object#toString()
     */
    @Override
    public String toString() {
        final StringBuilder builder = new StringBuilder();
        builder.append(name);
        builder.append(" [");
        if (endPoint != null) {
            builder.append("endPoint=");
            builder.append(endPoint);
        }
        builder.append("]");
        return builder.toString();
    }
}