com.ibm.mil.cafejava.CafeJava.java Source code

Java tutorial

Introduction

Here is the source code for com.ibm.mil.cafejava.CafeJava.java

Source

/*
 * Licensed Materials - Property of IBM
 *  Copyright IBM Corporation 2015. All Rights Reserved.
 */

package com.ibm.mil.cafejava;

import android.content.Context;
import android.support.annotation.NonNull;

import com.google.gson.Gson;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.reflect.TypeToken;
import com.worklight.wlclient.api.WLClient;
import com.worklight.wlclient.api.WLFailResponse;
import com.worklight.wlclient.api.WLResponse;
import com.worklight.wlclient.api.WLResponseListener;

import rx.Observable;
import rx.Subscriber;
import rx.functions.Func1;

import static rx.Observable.Transformer;

/**
 * Collection of methods for dealing with MFP procedure invocations in a reactive manner. For a
 * detailed guide on using the methods in this class, visit
 * <a href="https://github.com/t-preiss/CafeJava" target="_blank">the project's GitHub page</a> and
 * view the README.
 *
 * @author John Petitto  (github @jpetitto)
 * @author Tanner Preiss (github @t-preiss)
 */
public final class CafeJava {

    private CafeJava() {
        throw new AssertionError(CafeJava.class.getName() + " is non-instantiable");
    }

    /**
     * Creates an {@code Observable} that emits a {@code WLResponse} after attempting connection
     * to the MFP server instance defined in the {@code wlclient.properties} file. This
     * connection is only performed when there is a new {@code Subscriber} to the {@code
     * Observable}. The {@code Observable} will automatically perform its work on a dedicated
     * background thread, so there is usually no need to use the {@code subscribeOn} method of
     * RxJava.
     *
     * @param context
     * @return {@code Observable} that emits a {@code WLResponse} for an MFP connection.
     */
    @NonNull
    public static Observable<WLResponse> connect(@NonNull final Context context) {
        return Observable.create(new Observable.OnSubscribe<WLResponse>() {
            @Override
            public void call(Subscriber<? super WLResponse> subscriber) {
                WLClient client = WLClient.createInstance(context);
                client.connect(new RxResponseListener(subscriber));
            }
        });
    }

    /**
     * Creates an {@code Observable} that emits a {@code WLResponse} after attempting to invoke
     * the {@code ProcedureInvoker} parameter that is passed in. This invocation is only
     * performed when there is a new {@code Subscriber} to the {@code Observable}. The {@code
     * Observable} will automatically perform its work on a dedicated background thread, so there
     * is usually no need to use the {@code subscribeOn} method of RxJava.
     *
     * @param invoker Implementation for a procedure invocation, most commonly
     *                {@link JavaProcedureInvoker} or {@link JSProcedureInvoker}.
     * @return {@code Observable} that emits a {@code WLResponse} for an MFP procedure invocation.
     */
    @NonNull
    public static Observable<WLResponse> invokeProcedure(@NonNull final ProcedureInvoker invoker) {
        return Observable.create(new Observable.OnSubscribe<WLResponse>() {
            @Override
            public void call(Subscriber<? super WLResponse> subscriber) {
                invoker.invoke(new RxResponseListener(subscriber));
            }
        });
    }

    /**
     * Transforms an {@code Observable} that emits a {@code WLResponse} with a valid JSON payload
     * into a new {@code Observable} with the targeted {@code Class} literal. This can be done by
     * passing the result of this method to the {@code compose} operator of RxJava. A variable
     * number of member names can be provided for accessing JSON data that is nested arbitrarily
     * deep inside the response payload.
     *
     * @param clazz       Targeted {@code Class} for the JSON payload to be serialized into.
     * @param memberNames Variable number of member names for accessing JSON data that is nested
     *                    arbitrarily deep inside the response payload.
     * @return {@code Transformer} that can be supplied to the {@code compose} operator of RxJava
     * . The input {@code Observable} must emit a {@code WLResponse} with a valid JSON payload.
     */
    @NonNull
    public static <T> Transformer<WLResponse, T> serializeTo(@NonNull final Class<T> clazz,
            @NonNull final String... memberNames) {
        return transformJson(new Func1<WLResponse, T>() {
            @Override
            public T call(WLResponse wlResponse) {
                JsonElement element = parseNestedJson(wlResponse, memberNames);
                return new Gson().fromJson(element, clazz);
            }
        });
    }

    /**
     * Transforms an {@code Observable} that emits a {@code WLResponse} with a valid JSON payload
     * into a new Observable for the targeted {@code TypeToken}. This can be done by passing the
     * result of this method to the {@code compose} operator of RxJava. A {@code TypeToken} is
     * necessary when the targeted type is a parameterized type, such as {@code List}. A variable
     * number of member names can be provided for accessing JSON data that is nested arbitrarily
     * deep inside the response payload.
     *
     * @param typeToken   Captures the necessary type information for the targeted parameterized
     *                    type, such as {@code List}.
     * @param memberNames Variable number of member names for accessing JSON data that is nested
     *                    arbitrarily deep inside the response payload.
     * @return {@code Transformer} that can be supplied to the {@code compose} operator of RxJava
     * . The input {@code Observable} must emit a {@code WLResponse} with a valid JSON payload.
     */
    @NonNull
    public static <T> Transformer<WLResponse, T> serializeTo(@NonNull final TypeToken<T> typeToken,
            @NonNull final String... memberNames) {
        return transformJson(new Func1<WLResponse, T>() {
            @Override
            public T call(WLResponse wlResponse) {
                JsonElement element = parseNestedJson(wlResponse, memberNames);
                return new Gson().fromJson(element, typeToken.getType());
            }
        });
    }

    private static <T> Transformer<WLResponse, T> transformJson(final Func1<WLResponse, T> func) {
        return new Transformer<WLResponse, T>() {
            @Override
            public Observable<T> call(Observable<WLResponse> wlResponseObservable) {
                return wlResponseObservable.map(func);
            }
        };
    }

    private static JsonElement parseNestedJson(WLResponse wlResponse, String... memberNames) {
        String json = wlResponse.getResponseJSON().toString();
        JsonObject jsonObject = new JsonParser().parse(json).getAsJsonObject();

        // For each member name, fetch the object it maps to until you reach the final member name.
        // Once the final member name is reached, return its corresponding value.
        for (int i = 0, size = memberNames.length; i < size; i++) {
            String member = memberNames[i];

            if (i == size - 1) {
                // last member name reached; return its value
                return jsonObject.get(member);
            } else {
                // more member names remain, therefore current member must map to an object
                jsonObject = jsonObject.getAsJsonObject(member);
            }
        }

        // no nesting required; return top-level object
        return jsonObject;
    }

    private static class RxResponseListener implements WLResponseListener {
        private Subscriber<? super WLResponse> subscriber;

        RxResponseListener(Subscriber<? super WLResponse> subscriber) {
            this.subscriber = subscriber;
        }

        @Override
        public void onSuccess(WLResponse wlResponse) {
            subscriber.onNext(wlResponse);
            subscriber.onCompleted();
        }

        @Override
        public void onFailure(WLFailResponse wlFailResponse) {
            subscriber.onError(new Throwable(wlFailResponse.getErrorCode() + ": " + wlFailResponse.getErrorMsg()));
        }
    }

}