org.xframium.integrations.perfectoMobile.rest.services.RESTInvocationHandler.java Source code

Java tutorial

Introduction

Here is the source code for org.xframium.integrations.perfectoMobile.rest.services.RESTInvocationHandler.java

Source

/*******************************************************************************
 * xFramium
 *
 * Copyright 2016 by Moreland Labs, Ltd. (http://www.morelandlabs.com)
 *
 * Some open source application is free software: you can redistribute 
 * it and/or modify it under the terms of the GNU General Public 
 * License as published by the Free Software Foundation, either 
 * version 3 of the License, or (at your option) any later version.
 *  
 * Some open source application is distributed in the hope that it will 
 * be useful, but WITHOUT ANY WARRANTY; without even the implied warranty 
 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *  
 * You should have received a copy of the GNU General Public License
 * along with xFramium.  If not, see <http://www.gnu.org/licenses/>.
 *
 * @license GPL-3.0+ <http://spdx.org/licenses/GPL-3.0+>
 *******************************************************************************/
package org.xframium.integrations.perfectoMobile.rest.services;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.xframium.device.DeviceManager;
import org.xframium.device.cloud.CloudDescriptor;
import org.xframium.integrations.perfectoMobile.rest.PerfectoMobile;
import org.xframium.integrations.perfectoMobile.rest.services.PerfectoService.NameOverride;
import org.xframium.integrations.perfectoMobile.rest.services.PerfectoService.Operation;
import org.xframium.integrations.perfectoMobile.rest.services.PerfectoService.Parameter;
import org.xframium.integrations.perfectoMobile.rest.services.PerfectoService.ParameterMap;
import org.xframium.integrations.perfectoMobile.rest.services.PerfectoService.PerfectoCommand;
import org.xframium.integrations.perfectoMobile.rest.services.PerfectoService.ResourceID;
import org.xframium.integrations.perfectoMobile.rest.services.PerfectoService.ServiceDescriptor;
import org.xframium.integrations.rest.bean.Bean;
import org.xframium.integrations.rest.bean.factory.BeanManager;

// TODO: Auto-generated Javadoc
/**
 * The Class RESTInvocationHandler.
 */
public class RESTInvocationHandler implements InvocationHandler {

    /** The Constant SLASH. */
    private static final String SLASH = "/";

    /** The Constant TYPE. */
    private static final String TYPE = "TYPE";

    /** The Constant PARAM. */
    private static final String PARAM = "param.";

    /** The log. */
    private Log log = LogFactory.getLog(RESTInvocationHandler.class);

    /* (non-Javadoc)
     * @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object, java.lang.reflect.Method, java.lang.Object[])
     */
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        CloudDescriptor currentCloud = DeviceManager.instance().getCurrentCloud();
        if (currentCloud != null && !currentCloud.getProvider().equals("PERFECTO"))
            return null;

        StringBuilder urlBuilder = new StringBuilder();

        //
        // If the factory specified a cloud descriptor then use that URL
        //
        if (currentCloud != null)
            urlBuilder.append("https://" + currentCloud.getHostName()).append(SLASH);
        else {
            urlBuilder.append(PerfectoMobile.instance().getBaseUrl());

            if (!PerfectoMobile.instance().getBaseUrl().endsWith(SLASH))
                urlBuilder.append(SLASH);
        }

        urlBuilder.append("services/");

        ServiceDescriptor serviceDescriptor = proxy.getClass().getInterfaces()[0]
                .getAnnotation(ServiceDescriptor.class);
        if (serviceDescriptor == null)
            throw new IllegalArgumentException("Service Descriptor was NOT found");

        urlBuilder.append(serviceDescriptor.serviceName());

        String parameterId = "";
        Map parameterMap = null;
        Map<String, String> derivedMap = new HashMap<String, String>(10);

        Annotation[][] parameterAnnotations = method.getParameterAnnotations();

        for (int i = 0; i < method.getParameterTypes().length; i++) {
            if (getAnnotation(parameterAnnotations[i], ResourceID.class) != null)
                parameterId = parameterId + SLASH + args[i] + "";
            else if (getAnnotation(parameterAnnotations[i], ParameterMap.class) != null)
                parameterMap = (Map) parameterMap;
            else if (getAnnotation(parameterAnnotations[i], Parameter.class) != null) {
                if (args[i] != null) {
                    Parameter paramAnnotation = (Parameter) getAnnotation(parameterAnnotations[i], Parameter.class);
                    if (paramAnnotation.name().isEmpty())
                        derivedMap.put(PARAM + method.getParameterTypes()[i].getName(), args[i] + "");
                    else
                        derivedMap.put(PARAM + paramAnnotation.name(), args[i] + "");
                }
            } else {
                if (args[i] != null) {
                    String parameterName = method.getParameterTypes()[i].getName();
                    NameOverride namedParameter = (NameOverride) getAnnotation(parameterAnnotations[i],
                            NameOverride.class);
                    if (namedParameter != null && !namedParameter.name().isEmpty())
                        parameterName = namedParameter.name();

                    derivedMap.put(parameterName, args[i] + "");
                }
            }
        }

        if (!parameterId.isEmpty())
            urlBuilder.append(parameterId);

        Method actualMethod = findMethod(proxy.getClass().getInterfaces()[0], method.getName(), args);

        Operation op = actualMethod.getAnnotation(Operation.class);
        if (op == null)
            throw new IllegalArgumentException("Operation was NOT found");

        urlBuilder.append("?operation=").append(op.operationName());

        //
        // If the factory specified a cloud descriptor then use those credentials
        //
        if (currentCloud != null) {
            urlBuilder.append("&user=").append(currentCloud.getUserName());
            urlBuilder.append("&password=").append(currentCloud.getPassword());
        } else {
            urlBuilder.append("&user=").append(PerfectoMobile.instance().getUserName());
            urlBuilder.append("&password=").append(PerfectoMobile.instance().getPassword());
        }

        PerfectoCommand command = actualMethod.getAnnotation(PerfectoCommand.class);
        if (command != null) {
            urlBuilder.append("&command=").append(command.commandName());
            if (command.subCommandName() != null && !command.subCommandName().isEmpty())
                urlBuilder.append("&subcommand=").append(command.subCommandName());
        }

        urlBuilder.append("&responseFormat=").append(PerfectoMobile.instance().getResponseMethod());

        for (String name : derivedMap.keySet())
            urlBuilder.append("&").append(name).append("=")
                    .append(URLEncoder.encode(derivedMap.get(name), "UTF-8"));

        if (parameterMap != null) {
            for (Object name : parameterMap.keySet())
                urlBuilder.append("&").append(name).append("=")
                        .append(URLEncoder.encode(parameterMap.get(name) + "", "UTF-8"));
        }

        URL currentUrl = new URL(urlBuilder.toString());
        if (log.isInfoEnabled())
            log.info("Submitting REST call as " + urlBuilder.toString());

        if (method.getReturnType().isAssignableFrom(byte[].class)) {
            //
            // Byte Array is a special case that is not wrapped in XML
            //
            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
            byte[] buffer = new byte[512];
            int bytesRead = 0;
            InputStream inputStream = currentUrl.openStream();

            while ((bytesRead = inputStream.read(buffer)) > 0) {
                outputStream.write(buffer, 0, bytesRead);
            }

            return outputStream.toByteArray();
        }

        try {
            Bean newBean = BeanManager.instance().createBean(method.getReturnType(), currentUrl.openStream());
            return newBean;
        } catch (IOException e) {
            log.error("Could not connect to the cloud instance - Verify your user name, password and Cloud URL", e);
            return null;
        }

    }

    /**
     * Gets the annotation.
     *
     * @param annotationArray the annotation array
     * @param annotationType the annotation type
     * @return the annotation
     */
    private Annotation getAnnotation(Annotation[] annotationArray, Class annotationType) {
        for (Annotation ant : annotationArray) {
            if (annotationType.isAssignableFrom(ant.getClass()))
                return ant;
        }

        return null;
    }

    /**
     * Gets the url.
     *
     * @param currentUrl the current url
     * @return the url
     */
    public byte[] getUrl(URL currentUrl) {
        if (log.isDebugEnabled())
            log.debug("Executing " + currentUrl.toString());
        InputStream inputStream = null;
        try {
            ByteArrayOutputStream resultBuilder = new ByteArrayOutputStream();
            inputStream = currentUrl.openStream();
            byte[] buffer = new byte[1024];
            int bytesRead = 0;

            while ((bytesRead = inputStream.read(buffer)) > 0)
                resultBuilder.write(buffer, 0, bytesRead);

            return resultBuilder.toByteArray();
        } catch (Exception e) {
            log.error("Error performing GET request", e);
            return null;
        } finally {
            try {
                inputStream.close();
            } catch (Exception e) {
            }
        }

    }

    /**
     * Find method.
     *
     * @param rootClass the root class
     * @param methodName the method name
     * @param args the args
     * @return the method
     */
    private Method findMethod(Class rootClass, String methodName, Object[] args) {
        Method[] methodArray = rootClass.getMethods();

        for (Method currentMethod : methodArray) {
            if (isCorrectMethod(currentMethod, methodName, args)) {
                if (log.isDebugEnabled()) {
                    log.debug("Found [" + methodName + "] on " + rootClass.getName());

                    StringBuilder pBuilder = new StringBuilder();

                    pBuilder.append(args.length).append(" paramters supplied as: \r\n");

                    for (Object arg : args) {
                        pBuilder.append("\t");
                        if (arg == null)
                            pBuilder.append("NULL");
                        else
                            pBuilder.append("[" + arg.toString() + "] of type " + arg.getClass().getName());
                        pBuilder.append("\r\n");

                    }
                    log.debug(pBuilder.toString());
                }

                return currentMethod;
            }
        }

        if (log.isWarnEnabled()) {
            StringBuilder pBuilder = new StringBuilder();
            pBuilder.append("Could not locate ").append(methodName).append(" with ");
            pBuilder.append(args.length).append(" paramters supplied as: \r\n");

            for (Object arg : args) {
                pBuilder.append("\t");
                if (arg == null)
                    pBuilder.append("NULL");
                else
                    pBuilder.append("[" + arg.toString() + "] of type " + arg.getClass().getName());
                pBuilder.append("\r\n");

            }
            log.warn(pBuilder.toString());
        }
        return null;

    }

    /**
     * Checks if is correct method.
     *
     * @param compareMethod the compare method
     * @param methodName the method name
     * @param parameterArray the parameter array
     * @return true, if is correct method
     */
    private boolean isCorrectMethod(Method compareMethod, String methodName, Object[] parameterArray) {
        if (!methodName.equals(compareMethod.getName()))
            return false;

        if ((parameterArray == null || parameterArray.length == 0)
                && (compareMethod.getParameterTypes() == null || compareMethod.getParameterTypes().length == 0))
            return true;

        if (parameterArray == null || compareMethod.getParameterTypes() == null)
            return false;

        Class[] parameterTypes = compareMethod.getParameterTypes();

        if (parameterTypes.length != parameterArray.length) {
            if (log.isDebugEnabled())
                log.debug("Paramter Count Mismatch " + parameterTypes.length + " - " + parameterArray.length);
            return false;
        }

        for (int i = 0; i < parameterArray.length; i++) {
            if (log.isDebugEnabled())
                log.debug(parameterTypes[i] + " - " + parameterArray[i]);
            if (!isInstance(parameterTypes[i], parameterArray[i]))
                return false;
        }

        return true;
    }

    /**
     * Checks if is instance.
     *
     * @param classType the class type
     * @param value the value
     * @return true, if is instance
     */
    private boolean isInstance(Class classType, Object value) {
        try {
            if (classType.isPrimitive()) {
                if (value == null) {
                    if (log.isDebugEnabled())
                        log.debug("Primative value null");
                    return false;
                } else {
                    Field typeField = value.getClass().getField(TYPE);
                    return classType.isAssignableFrom((Class) typeField.get(value));
                }
            } else
                return (value == null || classType.isInstance(value));
        } catch (Exception e) {
            log.error("Error getting instance", e);
            return false;
        }
    }

}