com.mollie.api.resource.BaseResource.java Source code

Java tutorial

Introduction

Here is the source code for com.mollie.api.resource.BaseResource.java

Source

/**
 * Copyright (c) 2015, Impending
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * 
 * 1. Redistributions of source code must retain the above copyright notice, this
 *    list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * @license     Berkeley Software Distribution License (BSD-License 2) http://www.opensource.org/licenses/bsd-license.php
 * @author      Freddie Tilley <freddie.tilley@impending.nl>
 * @copyright   Impending
 * @link        http://www.impending.nl
 */
package com.mollie.api.resource;

import java.net.URISyntaxException;
import org.apache.http.client.utils.URIBuilder;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.util.LinkedHashMap;

import com.google.gson.Gson;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.mollie.api.MollieClient;
import com.mollie.api.MollieException;

abstract public class BaseResource<T> {
    public static final String REST_CREATE = MollieClient.HTTP_POST;
    public static final String REST_UPDATE = MollieClient.HTTP_POST;
    public static final String REST_READ = MollieClient.HTTP_GET;
    public static final String REST_LIST = MollieClient.HTTP_GET;
    public static final String REST_DELETE = MollieClient.HTTP_DELETE;

    /**
     * Default number of objects to retrieve when listing all objects.
     */
    public static final int DEFAULT_LIMIT = 10;

    protected MollieClient _api;

    public BaseResource(MollieClient api) {
        _api = api;
    }

    /**
     * @return Default resource name used by this resource when performing api calls.
     */
    protected String getResourceName() {
        String className = getClass().getName();
        String resourceName = className;
        String[] elements = className.split("\\.");

        if (elements.length > 0)
            resourceName = elements[elements.length - 1];
        return resourceName.toLowerCase();
    }

    @SuppressWarnings("unchecked")
    protected Class<T> returnedClass() {
        ParameterizedType parameterizedType = (ParameterizedType) getClass().getGenericSuperclass();
        return (Class<T>) parameterizedType.getActualTypeArguments()[0];
    }

    /**
     * Convenience method to copy all public properties from a src object into
     * a dst object of the same type.
     *
     * @param src Source object to copy properties from
     * @param dst Target object
     */
    protected void copyInto(T src, T dst) {
        Field[] fromFields = returnedClass().getDeclaredFields();
        Object value = null;

        try {
            for (Field field : fromFields) {
                int modifiers = field.getModifiers();

                if ((modifiers & Modifier.PUBLIC) == Modifier.PUBLIC
                        && (modifiers & Modifier.FINAL) != Modifier.FINAL
                        && (modifiers & Modifier.STATIC) != Modifier.STATIC) {
                    value = field.get(src);
                    field.set(dst, value);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * Retrieve all objects of a certain resource.
     *
     * @return list of fetched objects
     * @throws MollieException when the client is unable to fetch the objects
     * from the server
     * 
     * @see #all(Map options)
     * @see #all(int offset, int limit)
     * @see #all(int offset, int limit, Map options)
     */
    public List<T> all() throws MollieException {
        return this.all(0, 0);
    }

    /**
     * Retrieve all objects of a certain resource.
     *
     * @param options additional options to include when fetching objects.
     * @return list of fetched objects
     * @throws MollieException when the client is unable to fetch the objects
     * from the server
     * 
     * @see #all()
     * @see #all(int offset, int limit)
     * @see #all(int offset, int limit, Map options)
     */
    public List<T> all(Map<String, String> options) throws MollieException {
        return this.all(0, 0, options);
    }

    /**
     * Retrieve all objects of a certain resource.
     *
     * @param offset page offset of the objects to retrieve
     * @param limit maximimum number of objects to retrieve
     * @return list of fetched objects
     * @throws MollieException when the client is unable to fetch the objects
     * from the server
     * 
     * @see #all()
     * @see #all(Map options)
     * @see #all(int offset, int limit, Map options)
     */
    public List<T> all(int offset, int limit) throws MollieException {
        return this.all(offset, limit, null);
    }

    /**
     * Retrieve all objects of a certain resource.
     *
     * @param offset page offset of the objects to retrieve
     * @param limit maximimum number of objects to retrieve
     * @param options additional options to include when fetching objects.
     * @return list of fetched objects
     * @throws MollieException when the client is unable to fetch the objects
     * from the server
     * 
     * @see #all()
     * @see #all(Map options)
     * @see #all(int offset, int limit)
     */
    public List<T> all(int offset, int limit, Map<String, String> options) throws MollieException {
        return this.rest_list(this.getResourceName(), offset, limit, options);
    }

    /**
     * Retrieve information on a single resource from Mollie.
     * 
     * Will throw a MollieException if the resource cannot be found.
     *
     * @param resourceId Id of the object to retrieve.
     * @return object
     * @throws MollieException if the client is unable to get a resource from
     * the server.
     */
    public T get(String resourceId) throws MollieException {
        return this.rest_read(this.getResourceName(), resourceId);
    }

    /**
     * Create a resource with the remote API.
     * 
     * @param data an object containing details on the resource. Fields supported
     * depend on the resource created.
     * @return object on success or null on failure.
     * @throws MollieException when the client is unable to create a resource
     * on the server.
     */
    public T create(Object data) throws MollieException {
        Gson gson = new Gson();
        String encoded = gson.toJson(data);

        if (encoded != null)
            return this.rest_create(this.getResourceName(), encoded);
        else
            return null;
    }

    /**
     * Creates a resource with the REST API.
     *
     * @param restResource resource name
     * @param body contents used for creation of object
     * @return object on success or null on failure.
     * @throws MollieException when the client is unable to create a resource
     * on the server.
     */
    private T rest_create(String restResource, String body) throws MollieException {
        JsonObject result = this.performApiCall(REST_CREATE, restResource, body);

        if (result != null) {
            Gson gson = new Gson();
            return gson.fromJson(result, returnedClass());
        }

        return null;
    }

    /**
     * Retrieves a single object from the REST API.
     *
     * @param restResource resource name
     * @param id Id of the object to retrieve
     * @return object
     * @throws MollieException when the client is unable to read the results
     * from the server.
     */
    private T rest_read(String restResource, String id) throws MollieException {
        String method = restResource + "/" + id;
        JsonObject result = this.performApiCall(REST_READ, method);

        if (result != null) {
            Gson gson = new Gson();
            return gson.fromJson(result, returnedClass());
        }

        return null;
    }

    /**
     * Creates a valid query string from the supplied options that can be used
     * as url parameters. The method returns null if there was an error building
     * the query string.
     * 
     * @param options options to build a query string from
     * @return a valid query string or null.
     */
    private String buildQueryFromMap(Map<String, String> options) {
        URIBuilder ub = null;
        String queryString = null;

        try {
            ub = new URIBuilder();

            for (Map.Entry<String, String> entry : options.entrySet()) {
                ub.addParameter(entry.getKey(), entry.getValue());
            }

            queryString = ub.build().getQuery();
        } catch (URISyntaxException e) {
            e.printStackTrace();
        }

        return queryString;
    }

    /**
     * Get a collection of objects from the REST API.
     *
     * @param restResource resource name
     * @param offset page offset of the objects to retrieve
     * @param limit maximimum number of objects to retrieve
     * @param options additional options
     * @return
     * @throws MollieException if there was a problem fetching the objects
     */
    private List<T> rest_list(String restResource, int offset, int limit, Map<String, String> options)
            throws MollieException {
        String query = null;

        if (options == null) {
            options = new LinkedHashMap<String, String>();
        }

        if (!options.containsKey("offset")) {
            options.put("offset", Integer.toString(offset));
        }

        if (!options.containsKey("count")) {
            options.put("count", Integer.toString(limit));
        }

        query = buildQueryFromMap(options);
        String apiPath = restResource + (query != null ? "?" + query : "");

        JsonObject result = this.performApiCall(REST_LIST, apiPath);
        ArrayList<T> arraylist = new ArrayList<T>();

        if (result != null) {
            Gson gson = new Gson();

            for (JsonElement object : result.get("data").getAsJsonArray())
                arraylist.add(gson.fromJson(object, returnedClass()));
        }

        return arraylist;
    }

    /**
     * Perform an API call with an empty body contents, interpret the results
     * and convert them to the correct object type.
     *
     * @param httpMethod the http method to use
     * @param apiMethod the api method to call
     *
     * @return object {@link JsonObject} or null if there was a problem with
     * the api call.
     * @throws MollieException if there was an error performing the call or if
     * the results could not be decoded into a {@link JsonObject}
     * 
     * @see #performApiCall(String httpMethod, String apiMethod)
     */
    protected JsonObject performApiCall(String httpMethod, String apiMethod) throws MollieException {
        return performApiCall(httpMethod, apiMethod, null);
    }

    /**
     * Perform an API call, interpret the results and convert them to the
     * correct object type. 
     *
     * @param httpMethod the http method to use
     * @param apiMethod the api method to call
     * @param httpBody the contents to send to the server.
     *
     * @return object {@link JsonObject} or null if there was a problem with
     * the api call
     * @throws MollieException if there was an error performing the call or if
     * the results could not be decoded into a {@link JsonObject}
     * 
     * @see #performApiCall(String httpMethod, String apiMethod)
     */
    protected JsonObject performApiCall(String httpMethod, String apiMethod, String httpBody)
            throws MollieException {
        String result = _api.performHttpCall(httpMethod, apiMethod, httpBody);
        JsonParser parser = new JsonParser();
        JsonElement element = null;
        JsonObject object = null;

        try {
            if ((element = parser.parse(result)) != null) {
                if (element.isJsonObject()) {
                    object = element.getAsJsonObject();
                }
            }
        } catch (com.google.gson.JsonParseException e) {
        }

        if (object == null) {
            throw new MollieException("Unable to decode Mollie response: \"" + result + "\"");
        } else {
            if (object.get("error") != null) {
                MollieException exception = null;
                JsonObject error = object.get("error").getAsJsonObject();
                String type = error.get("type").getAsString();
                String message = error.get("message").getAsString();

                exception = new MollieException("Error executing API call (" + type + "): " + message + ".");

                if (error.has("field")) {
                    exception.setField(error.get("field").getAsString());
                }

                throw exception;
            }
        }

        return object;
    }
}