com.haulmont.restapi.service.RestServiceInvoker.java Source code

Java tutorial

Introduction

Here is the source code for com.haulmont.restapi.service.RestServiceInvoker.java

Source

/*
 * Copyright (c) 2008-2016 Haulmont.
 *
 * 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.haulmont.restapi.service;

import com.google.common.base.Strings;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.haulmont.chile.core.datatypes.Datatype;
import com.haulmont.chile.core.datatypes.Datatypes;
import com.haulmont.cuba.core.app.serialization.EntitySerializationAPI;
import com.haulmont.cuba.core.entity.Entity;
import com.haulmont.cuba.core.global.AppBeans;
import com.haulmont.restapi.common.ParseUtils;
import com.haulmont.restapi.exception.RestAPIException;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.util.ClassUtils;

import javax.inject.Inject;
import java.lang.reflect.Method;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;

/**
 * Class is used for invoking middleware services from REST API service controller
 */
@Component
public class RestServiceInvoker {

    @Inject
    protected ParseUtils parseUtils;

    @Inject
    protected EntitySerializationAPI entitySerializationAPI;

    public String invokeServiceMethod(String serviceName, String methodName, String paramsJson) {
        List<String> paramValuesStr = new ArrayList<>();
        List<Class> paramTypes = new ArrayList<>();

        parseParamsJson(paramsJson, paramValuesStr, paramTypes);

        return _invokeServiceMethod(serviceName, methodName, paramValuesStr, paramTypes);
    }

    public String invokeServiceMethod(String serviceName, String methodName, Map<String, String> paramsMap) {
        List<String> paramValuesStr = new ArrayList<>();
        List<Class> paramTypes = new ArrayList<>();

        parseParamsJson(paramsMap, paramValuesStr, paramTypes);

        return _invokeServiceMethod(serviceName, methodName, paramValuesStr, paramTypes);
    }

    protected String _invokeServiceMethod(String serviceName, String methodName, List<String> paramValuesStr,
            List<Class> paramTypes) {
        if (!paramTypes.isEmpty() && paramValuesStr.size() != paramTypes.size()) {
            throw new RestAPIException("Wrong request parameters",
                    "The number of parameters that define method argument values should be equal to the number of "
                            + "parameters that define method argument types",
                    HttpStatus.BAD_REQUEST);
        }

        Object service = AppBeans.get(serviceName);
        Method serviceMethod = findMethod(service, methodName, paramValuesStr, paramTypes);
        List<Object> paramValues = new ArrayList<>();
        Class<?>[] types = serviceMethod.getParameterTypes();
        for (int i = 0; i < types.length; i++) {
            Class<?> aClass = types[i];
            try {
                paramValues.add(parseUtils.toObject(aClass, paramValuesStr.get(i)));
            } catch (ParseException e) {
                throw new RestAPIException("Invalid parameter value", e.getMessage(), HttpStatus.BAD_REQUEST, e);
            }
        }

        Object methodResult;
        try {
            methodResult = serviceMethod.invoke(service, paramValues.toArray());
        } catch (Exception e) {
            throw new RestAPIException("Error on service method invoke", e.getMessage(),
                    HttpStatus.INTERNAL_SERVER_ERROR, e);
        }

        if (methodResult == null)
            return null;

        Class<?> methodReturnType = serviceMethod.getReturnType();
        if (Entity.class.isAssignableFrom(methodReturnType)) {
            return entitySerializationAPI.toJson((Entity) methodResult);
        } else if (Collection.class.isAssignableFrom(methodReturnType)) {
            return entitySerializationAPI.toJson((Collection<? extends Entity>) methodResult);
        } else {
            Datatype<?> datatype = Datatypes.get(methodReturnType);
            if (datatype != null) {
                return datatype.format(methodResult);
            } else {
                return methodResult.toString();
            }
        }
    }

    private void parseParamsJson(String paramsJson, List<String> paramValuesStr, List<Class> paramTypes) {
        if (Strings.isNullOrEmpty(paramsJson))
            return;
        JsonParser jsonParser = new JsonParser();
        JsonObject jsonObject = jsonParser.parse(paramsJson).getAsJsonObject();
        int idx = 0;
        while (true) {
            JsonElement nthParam = jsonObject.get("param" + idx);
            if (nthParam == null)
                break;
            if (nthParam.isJsonPrimitive()) {
                paramValuesStr.add(nthParam.getAsString());
            } else {
                paramValuesStr.add(nthParam.toString());
            }

            JsonElement nthParamType = jsonObject.get("param" + idx + "_type");
            if (nthParamType != null) {
                try {
                    paramTypes.add(ClassUtils.forName(nthParamType.getAsString(), null));
                } catch (ClassNotFoundException e) {
                    throw new RestAPIException("Error on evaluating parameter type", e.getMessage(),
                            HttpStatus.BAD_REQUEST, e);
                }
            }
            idx++;
        }
    }

    private void parseParamsJson(Map<String, String> paramsMap, List<String> paramValuesStr,
            List<Class> paramTypes) {
        int idx = 0;
        while (true) {
            String paramValueStr = paramsMap.get("param" + idx);
            if (paramValueStr == null)
                break;
            paramValuesStr.add(paramValueStr);

            String paramType = paramsMap.get("param" + idx + "_type");
            if (paramType != null) {
                try {
                    paramTypes.add(ClassUtils.forName(paramType, null));
                } catch (ClassNotFoundException e) {
                    throw new RestAPIException("Error on evaluating parameter type", e.getMessage(),
                            HttpStatus.BAD_REQUEST, e);
                }
            }
            idx++;
        }
    }

    protected Method findMethod(Object service, String methodName, List<String> paramValues, List<Class> paramTypes)
            throws RestAPIException {
        Method serviceMethod;
        if (paramTypes.isEmpty()) {
            //trying to guess which method to invoke
            Method[] methods = service.getClass().getMethods();
            List<Method> appropriateMethods = new ArrayList<>();
            for (Method method : methods) {
                if (methodName.equals(method.getName())
                        && method.getParameterTypes().length == paramValues.size()) {
                    appropriateMethods.add(method);
                }
            }
            if (appropriateMethods.size() == 1) {
                serviceMethod = appropriateMethods.get(0);
            } else if (appropriateMethods.size() > 1) {
                throw new RestAPIException("Multiple methods found",
                        "There are multiple methods with given argument numbers. Please define parameter types in request",
                        HttpStatus.BAD_REQUEST);
            } else {
                throw new RestAPIException("Method not found", "", HttpStatus.NOT_FOUND);
            }
        } else {
            try {
                serviceMethod = service.getClass().getMethod(methodName,
                        paramTypes.toArray(new Class[paramTypes.size()]));
            } catch (NoSuchMethodException e) {
                throw new RestAPIException("Method not found", "", HttpStatus.NOT_FOUND);
            }
        }
        return serviceMethod;
    }

}