All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.androidannotations.rest.spring.handler.RestMethodHandler Maven / Gradle / Ivy

There is a newer version: 4.8.0
Show newest version
/**
 * Copyright (C) 2010-2016 eBusiness Information, Excilys Group
 *
 * 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 org.androidannotations.rest.spring.handler;

import static org.androidannotations.rest.spring.helper.RestSpringClasses.HTTP_METHOD;
import static org.androidannotations.rest.spring.helper.RestSpringClasses.NESTED_RUNTIME_EXCEPTION;
import static org.androidannotations.rest.spring.helper.RestSpringClasses.RESPONSE_ENTITY;

import java.util.List;
import java.util.Locale;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;

import javax.lang.model.element.Element;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.TypeKind;

import org.androidannotations.AndroidAnnotationsEnvironment;
import org.androidannotations.ElementValidation;
import org.androidannotations.handler.BaseAnnotationHandler;
import org.androidannotations.rest.spring.helper.RestAnnotationHelper;
import org.androidannotations.rest.spring.helper.RestSpringValidatorHelper;
import org.androidannotations.rest.spring.holder.RestHolder;

import com.helger.jcodemodel.AbstractJClass;
import com.helger.jcodemodel.AbstractJType;
import com.helger.jcodemodel.IJExpression;
import com.helger.jcodemodel.JArray;
import com.helger.jcodemodel.JBlock;
import com.helger.jcodemodel.JCatchBlock;
import com.helger.jcodemodel.JConditional;
import com.helger.jcodemodel.JExpr;
import com.helger.jcodemodel.JForEach;
import com.helger.jcodemodel.JInvocation;
import com.helger.jcodemodel.JMethod;
import com.helger.jcodemodel.JMod;
import com.helger.jcodemodel.JOp;
import com.helger.jcodemodel.JTryBlock;
import com.helger.jcodemodel.JVar;

public abstract class RestMethodHandler extends BaseAnnotationHandler {

	protected final RestAnnotationHelper restAnnotationHelper;
	protected final RestSpringValidatorHelper restSpringValidatorHelper;

	public RestMethodHandler(Class targetClass, AndroidAnnotationsEnvironment environment) {
		super(targetClass, environment);
		restAnnotationHelper = new RestAnnotationHelper(environment, getTarget());
		restSpringValidatorHelper = new RestSpringValidatorHelper(environment, getTarget());
	}

	@Override
	public void validate(Element element, ElementValidation validation) {
		validatorHelper.notAlreadyValidated(element, validation);

		restSpringValidatorHelper.enclosingElementHasRestAnnotation(element, validation);
		restSpringValidatorHelper.throwsOnlyRestClientException((ExecutableElement) element, validation);
		Set variableNames = restAnnotationHelper.extractUrlVariableNames((ExecutableElement) element);
		restSpringValidatorHelper.urlVariableNamesExistInParameters((ExecutableElement) element, variableNames, validation);
		restSpringValidatorHelper.hasAnnotatedAllParameters((ExecutableElement) element, validation);
	}

	@Override
	public void process(Element element, RestHolder holder) {
		ExecutableElement executableElement = (ExecutableElement) element;
		String methodName = element.getSimpleName().toString();
		AbstractJClass methodReturnClass = getMethodReturnClass(element, holder);
		boolean methodReturnVoid = executableElement.getReturnType().getKind() == TypeKind.VOID;

		// Creating method signature
		JMethod method = holder.getGeneratedClass().method(JMod.PUBLIC, methodReturnClass, methodName);
		method.annotate(Override.class);
		SortedMap params = addMethodParams(executableElement, holder, method);
		JBlock methodBody = new JBlock().bracesRequired(false).indentRequired(false);

		// RestTemplate exchange() method call
		JInvocation exchangeCall = JExpr.invoke(holder.getRestTemplateField(), "exchange");
		exchangeCall.arg(getUrl(element, holder));
		exchangeCall.arg(getHttpMethod());
		exchangeCall.arg(getRequestEntity(executableElement, holder, methodBody, params));
		exchangeCall.arg(getResponseClass(element, holder));
		IJExpression urlVariables = getUrlVariables(element, holder, methodBody, params);
		if (urlVariables != null) {
			exchangeCall.arg(urlVariables);
		}

		IJExpression response = setCookies(executableElement, holder, methodBody, exchangeCall);
		if (methodReturnVoid && response.equals(exchangeCall)) {
			methodBody.add(exchangeCall);
		} else if (!methodReturnVoid) {
			methodBody._return(addResultCallMethod(response, methodReturnClass));
		}
		methodBody = surroundWithRestTryCatch(holder, methodBody, methodReturnVoid);
		codeModelHelper.copy(methodBody, method.body());
	}

	protected AbstractJClass getMethodReturnClass(Element element, RestHolder holder) {
		ExecutableElement executableElement = (ExecutableElement) element;
		return codeModelHelper.typeMirrorToJClass(executableElement.getReturnType());
	}

	protected SortedMap addMethodParams(ExecutableElement executableElement, RestHolder restHolder, JMethod method) {
		List params = executableElement.getParameters();
		SortedMap methodParams = new TreeMap<>();
		for (VariableElement parameter : params) {
			String paramName = parameter.getSimpleName().toString();
			String paramType = parameter.asType().toString();

			JVar param;
			if (parameter.asType().getKind().isPrimitive()) {
				param = method.param(getCodeModel().parseType(paramType), paramName);
			} else {
				AbstractJClass parameterClass = codeModelHelper.typeMirrorToJClass(parameter.asType());
				param = method.param(parameterClass, paramName);
			}
			methodParams.put(paramName, param);
		}
		return methodParams;
	}

	protected IJExpression getUrl(Element element, RestHolder restHolder) {
		String urlSuffix = getUrlSuffix(element);
		IJExpression url = JExpr.lit(getUrlSuffix(element));
		if (!(urlSuffix.startsWith("http://") || urlSuffix.startsWith("https://"))) {
			url = JExpr.invoke(restHolder.getRootUrlField(), "concat").arg(url);
		}
		return url;
	}

	protected abstract String getUrlSuffix(Element element);

	protected IJExpression getHttpMethod() {
		AbstractJClass httpMethod = getJClass(HTTP_METHOD);
		String simpleName = getTarget().substring(getTarget().lastIndexOf('.') + 1);
		String restMethodInCapitalLetters = simpleName.toUpperCase(Locale.ENGLISH);
		return httpMethod.staticRef(restMethodInCapitalLetters);
	}

	protected IJExpression getRequestEntity(ExecutableElement element, RestHolder holder, JBlock methodBody, SortedMap params) {
		JVar httpHeaders = restAnnotationHelper.declareHttpHeaders(element, holder, methodBody);
		JVar entitySentToServer = restAnnotationHelper.getEntitySentToServer(element, params);
		return restAnnotationHelper.declareHttpEntity(methodBody, entitySentToServer, httpHeaders);
	}

	protected IJExpression getResponseClass(Element element, RestHolder holder) {
		return restAnnotationHelper.getResponseClass(element, holder);
	}

	protected IJExpression getUrlVariables(Element element, RestHolder holder, JBlock methodBody, SortedMap params) {
		return restAnnotationHelper.declareUrlVariables((ExecutableElement) element, holder, methodBody, params);
	}

	protected IJExpression addResultCallMethod(IJExpression exchangeCall, AbstractJClass methodReturnClass) {
		if (methodReturnClass != null && !methodReturnClass.fullName().startsWith(RESPONSE_ENTITY)) {
			return JExpr.invoke(exchangeCall, "getBody");
		}
		return exchangeCall;
	}

	private IJExpression setCookies(ExecutableElement executableElement, RestHolder restHolder, JBlock methodBody, JInvocation exchangeCall) {
		String[] settingCookies = restAnnotationHelper.settingCookies(executableElement);
		if (settingCookies != null) {
			boolean methodReturnVoid = executableElement.getReturnType().getKind() == TypeKind.VOID;

			AbstractJClass exchangeResponseClass = restAnnotationHelper.retrieveResponseClass(executableElement.getReturnType(), restHolder);
			AbstractJType narrowType = exchangeResponseClass == null || methodReturnVoid ? getCodeModel().VOID : exchangeResponseClass;
			AbstractJClass responseEntityClass = getJClass(RESPONSE_ENTITY).narrow(narrowType);
			JVar responseEntity = methodBody.decl(responseEntityClass, "response", exchangeCall);

			// set cookies
			AbstractJClass stringListClass = getClasses().LIST.narrow(getClasses().STRING);
			AbstractJClass stringArrayClass = getClasses().STRING.array();
			JArray cookiesArray = JExpr.newArray(getClasses().STRING);
			for (String cookie : settingCookies) {
				cookiesArray.add(JExpr.lit(cookie));
			}
			JVar requestedCookiesVar = methodBody.decl(stringArrayClass, "requestedCookies", cookiesArray);

			JInvocation setCookiesList = JExpr.invoke(responseEntity, "getHeaders").invoke("get").arg("Set-Cookie");
			JVar allCookiesList = methodBody.decl(stringListClass, "allCookies", setCookiesList);

			// for loop over list... add if in string array
			JForEach forEach = methodBody._if(allCookiesList.ne(JExpr._null()))._then() //
					.forEach(getClasses().STRING, "rawCookie", allCookiesList);
			JVar rawCookieVar = forEach.var();

			JBlock forLoopBody = forEach.body();

			JForEach innerForEach = forLoopBody.forEach(getClasses().STRING, "thisCookieName", requestedCookiesVar);
			JBlock innerBody = innerForEach.body();
			JBlock thenBlock = innerBody._if(JExpr.invoke(rawCookieVar, "startsWith").arg(innerForEach.var()))._then();

			// where does the cookie VALUE end?
			JInvocation valueEnd = rawCookieVar.invoke("indexOf").arg(JExpr.lit(';'));
			JVar valueEndVar = thenBlock.decl(getCodeModel().INT, "valueEnd", valueEnd);
			JBlock fixValueEndBlock = thenBlock._if(valueEndVar.eq(JExpr.lit(-1)))._then();
			fixValueEndBlock.assign(valueEndVar, rawCookieVar.invoke("length"));

			IJExpression indexOfValue = rawCookieVar.invoke("indexOf").arg("=").plus(JExpr.lit(1));
			JInvocation cookieValue = rawCookieVar.invoke("substring").arg(indexOfValue).arg(valueEndVar);
			thenBlock.invoke(restHolder.getAvailableCookiesField(), "put").arg(innerForEach.var()).arg(cookieValue);
			thenBlock._break();

			return responseEntity;
		}
		return exchangeCall;
	}

	/**
	 * Adds the try/catch around the rest execution code.
	 *
	 * If an exception is caught, it will first check if the handler is set. If
	 * the handler is set, it will call the handler and return null (or nothing
	 * if void). If the handler isn't set, it will re-throw the exception so
	 * that it behaves as it did previous to this feature.
	 */
	private JBlock surroundWithRestTryCatch(RestHolder holder, JBlock block, boolean methodReturnVoid) {
		if (holder.getRestErrorHandlerField() != null) {
			JBlock newBlock = new JBlock().bracesRequired(false).indentRequired(false);

			JTryBlock tryBlock = newBlock._try();
			codeModelHelper.copy(block, tryBlock.body());

			JCatchBlock jCatch = tryBlock._catch(getJClass(NESTED_RUNTIME_EXCEPTION));

			JBlock catchBlock = jCatch.body();
			JConditional conditional = catchBlock._if(JOp.ne(holder.getRestErrorHandlerField(), JExpr._null()));
			JVar exceptionParam = jCatch.param("e");

			JBlock thenBlock = conditional._then();

			// call the handler method if it was set.
			thenBlock.add(holder.getRestErrorHandlerField().invoke("onRestClientExceptionThrown").arg(exceptionParam));

			// return null if exception was caught and handled.
			if (!methodReturnVoid) {
				thenBlock._return(JExpr._null());
			}

			// re-throw the exception if handler wasn't set.
			conditional._else()._throw(exceptionParam);
			return newBlock;
		}
		return block;
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy