/**
* Copyright 2010-2016 Ralph Schaer <ralphschaer@gmail.com>
*
* 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 ch.ralscha.extdirectspring.annotation;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Controller;
import org.springframework.util.StringUtils;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;
import ch.ralscha.extdirectspring.bean.EdFormPostResult;
import ch.ralscha.extdirectspring.bean.ExtDirectFormPostResult;
/**
* Enumeration of all possible remote method types.
*/
public enum ExtDirectMethodType {
/**
* Specifies a simple remote method. This type of method can have any parameter and
* any return type but must not contain a parameter with @RequestParam annotated.
*/
SIMPLE {
@Override
public boolean isValid(String beanAndMethodName, Class<?> clazz, Method method) {
ExtDirectMethod extDirectMethodAnnotation = AnnotationUtils
.findAnnotation(method, ExtDirectMethod.class);
if (StringUtils.hasText(extDirectMethodAnnotation.event())) {
log.warn("SIMPLE method '" + beanAndMethodName
+ "' does not support event attribute of @ExtDirectMethod");
}
if (extDirectMethodAnnotation.entryClass() != Object.class) {
log.warn("SIMPLE method '" + beanAndMethodName
+ "' does not support entryClass attribute of @ExtDirectMethod");
}
Annotation[][] allParameterAnnotations = method.getParameterAnnotations();
for (Annotation[] paramAnnotations : allParameterAnnotations) {
for (Annotation paramAnnotation : paramAnnotations) {
if (RequestParam.class.isInstance(paramAnnotation)) {
log.error("SIMPLE method '" + beanAndMethodName
+ "' contains a non supported parameter annotation @RequestParam");
return false;
}
}
}
return true;
}
},
/**
* Specifies a simple remote method with named parameters. This type of method can
* have any parameter and any return type.
*/
SIMPLE_NAMED {
@Override
public boolean isValid(String beanAndMethodName, Class<?> clazz, Method method) {
ExtDirectMethod extDirectMethodAnnotation = AnnotationUtils
.findAnnotation(method, ExtDirectMethod.class);
if (StringUtils.hasText(extDirectMethodAnnotation.event())) {
log.warn("SIMPLE_NAMED method '" + beanAndMethodName
+ "' does not support event attribute of @ExtDirectMethod");
}
if (extDirectMethodAnnotation.entryClass() != Object.class) {
log.warn("SIMPLE_NAMED method '" + beanAndMethodName
+ "' does not support entryClass attribute of @ExtDirectMethod");
}
return true;
}
},
/**
* Specifies a method that handles a form load.
*/
FORM_LOAD {
@Override
public boolean isValid(String beanAndMethodName, Class<?> clazz, Method method) {
ExtDirectMethod extDirectMethodAnnotation = AnnotationUtils
.findAnnotation(method, ExtDirectMethod.class);
if (StringUtils.hasText(extDirectMethodAnnotation.event())) {
log.warn("FORM_LOAD method '" + beanAndMethodName
+ "' does not support event attribute of @ExtDirectMethod");
}
if (extDirectMethodAnnotation.entryClass() != Object.class) {
log.warn("FORM_LOAD method '" + beanAndMethodName
+ "' does not support entryClass attribute of @ExtDirectMethod");
}
return true;
}
},
/**
* Specifies a method that handles read calls from DirectStore.
*/
STORE_READ {
@Override
public boolean isValid(String beanAndMethodName, Class<?> clazz, Method method) {
ExtDirectMethod extDirectMethodAnnotation = AnnotationUtils
.findAnnotation(method, ExtDirectMethod.class);
if (StringUtils.hasText(extDirectMethodAnnotation.event())) {
log.warn("STORE_READ method '" + beanAndMethodName
+ "' does not support event attribute of @ExtDirectMethod");
}
if (extDirectMethodAnnotation.entryClass() != Object.class) {
log.warn("STORE_READ method '" + beanAndMethodName
+ "' does not support entryClass attribute of @ExtDirectMethod");
}
return true;
}
},
/**
* Specifies a method that handles create, update or destroy calls from DirectStore.
*/
STORE_MODIFY {
@Override
public boolean isValid(String beanAndMethodName, Class<?> clazz, Method method) {
ExtDirectMethod extDirectMethodAnnotation = AnnotationUtils
.findAnnotation(method, ExtDirectMethod.class);
if (StringUtils.hasText(extDirectMethodAnnotation.event())) {
log.warn("STORE_MODIFY method '" + beanAndMethodName
+ "' does not support event attribute of @ExtDirectMethod");
}
return true;
}
},
/**
* Specifies a method that handles a form post (with or without upload). A FORM_POST
* method must not return anything. This type of method must be annotated
* with @RequestMapping. @RequestMapping must contain a value and a method of type
* RequestMethod.POST. This kind of method must be member of a bean annotated
* with @Controller.
*/
FORM_POST {
@Override
public boolean isValid(String beanAndMethodName, Class<?> clazz, Method method) {
boolean isValid = true;
if (method.getReturnType().equals(ExtDirectFormPostResult.class)
|| method.getReturnType().equals(EdFormPostResult.class)) {
ExtDirectMethod extDirectMethodAnnotation = AnnotationUtils
.findAnnotation(method, ExtDirectMethod.class);
if (StringUtils.hasText(extDirectMethodAnnotation.event())) {
log.warn("FORM_POST method '" + beanAndMethodName
+ "' does not support event attribute of @ExtDirectMethod");
}
if (extDirectMethodAnnotation.entryClass() != Object.class) {
log.warn("FORM_POST method '" + beanAndMethodName
+ "' does not support entryClass attribute of @ExtDirectMethod");
}
if (extDirectMethodAnnotation.batched() == false) {
log.warn("FORM_POST method '" + beanAndMethodName
+ "' does not support batched attribute of @ExtDirectMethod");
}
isValid = true;
}
else if (method.getReturnType().equals(Void.TYPE)) {
if (AnnotationUtils.findAnnotation(method, ResponseBody.class) != null) {
log.warn("FORM_POST method '" + beanAndMethodName
+ "' should not have a @ResponseBody annotation");
}
if (AnnotationUtils.findAnnotation(clazz, Controller.class) == null) {
log.error("FORM_POST method '" + beanAndMethodName
+ "' must be a member of a @Controller bean");
isValid = false;
}
final RequestMapping methodAnnotation = AnnotationUtils
.findAnnotation(method, RequestMapping.class);
if (methodAnnotation == null) {
log.error("FORM_POST method '" + beanAndMethodName
+ "' must be annotated with @RequestMapping");
isValid = false;
}
RequestMapping classAnnotation = AnnotationUtils.findAnnotation(clazz,
RequestMapping.class);
boolean hasValue = false;
if (classAnnotation != null) {
hasValue = classAnnotation.value() != null
&& classAnnotation.value().length > 0;
}
if (methodAnnotation != null && !hasValue) {
hasValue = methodAnnotation.value() != null
&& methodAnnotation.value().length > 0;
}
if (!hasValue) {
log.error("FORM_POST method '" + beanAndMethodName
+ "' must have a @RequestMapping annotation with a value");
isValid = false;
}
if (methodAnnotation != null) {
boolean hasPostRequestMethod = false;
for (RequestMethod requestMethod : methodAnnotation.method()) {
if (requestMethod.equals(RequestMethod.POST)) {
hasPostRequestMethod = true;
break;
}
}
if (!hasPostRequestMethod) {
log.error("FORM_POST method '" + beanAndMethodName
+ "' must have a @RequestMapping annotation with method = RequestMethod.POST");
isValid = false;
}
}
ExtDirectMethod extDirectMethodAnnotation = AnnotationUtils
.findAnnotation(method, ExtDirectMethod.class);
if (extDirectMethodAnnotation.batched() == false) {
log.warn("FORM_POST method '" + beanAndMethodName
+ "' does not support batched attribute of @ExtDirectMethod");
}
if (StringUtils.hasText(extDirectMethodAnnotation.event())) {
log.warn("FORM_POST method '" + beanAndMethodName
+ "' does not support event attribute of @ExtDirectMethod");
}
if (extDirectMethodAnnotation.entryClass() != Object.class) {
log.warn("FORM_POST method '" + beanAndMethodName
+ "' does not support entryClass attribute of @ExtDirectMethod");
}
if (extDirectMethodAnnotation.synchronizeOnSession()) {
log.warn("FORM_POST method '" + beanAndMethodName
+ "' does not support synchronizeOnSession attribute of @ExtDirectMethod");
}
if (extDirectMethodAnnotation.streamResponse()) {
log.warn("FORM_POST method '" + beanAndMethodName
+ "' does not support streamResponse attribute of @ExtDirectMethod");
}
}
else {
log.error("FORM_POST method '" + beanAndMethodName
+ "' must return void or an instance of ExtDirectFormPostResult or EdFormPostResult");
isValid = false;
}
return isValid;
}
},
/**
* Specifies a method that handles read calls from TreeLoader or TreeStore
*/
TREE_LOAD {
@Override
public boolean isValid(String beanAndMethodName, Class<?> clazz, Method method) {
ExtDirectMethod extDirectMethodAnnotation = AnnotationUtils
.findAnnotation(method, ExtDirectMethod.class);
if (StringUtils.hasText(extDirectMethodAnnotation.event())) {
log.warn("TREE_LOAD method '" + beanAndMethodName
+ "' does not support event attribute of @ExtDirectMethod");
}
if (extDirectMethodAnnotation.entryClass() != Object.class) {
log.warn("TREE_LOAD method '" + beanAndMethodName
+ "' does not support entryClass attribute of @ExtDirectMethod");
}
return true;
}
},
/**
* Specifies a method that handles polling.
*/
POLL {
@Override
public boolean isValid(String beanAndMethodName, Class<?> clazz, Method method) {
ExtDirectMethod extDirectMethodAnnotation = AnnotationUtils
.findAnnotation(method, ExtDirectMethod.class);
if (extDirectMethodAnnotation.entryClass() != Object.class) {
log.warn("POLL method '" + beanAndMethodName
+ "' does not support entryClass attribute of @ExtDirectMethod");
}
if (extDirectMethodAnnotation.batched() == false) {
log.warn("POLL method '" + beanAndMethodName
+ "' does not support batched attribute of @ExtDirectMethod");
}
return true;
}
},
/**
* Specifies a method that handles a form post with a Json payload
*/
FORM_POST_JSON {
@Override
public boolean isValid(String beanAndMethodName, Class<?> clazz, Method method) {
ExtDirectMethod extDirectMethodAnnotation = AnnotationUtils
.findAnnotation(method, ExtDirectMethod.class);
if (StringUtils.hasText(extDirectMethodAnnotation.event())) {
log.warn("FORM_POST_JSON method '" + beanAndMethodName
+ "' does not support event attribute of @ExtDirectMethod");
}
if (extDirectMethodAnnotation.batched() == false) {
log.warn("FORM_POST_JSON method '" + beanAndMethodName
+ "' does not support batched attribute of @ExtDirectMethod");
}
for (Class<?> clazzz : method.getParameterTypes()) {
if (clazzz.isAssignableFrom(BindingResult.class)) {
log.error("FORM_POST_JSON method '" + beanAndMethodName
+ "' must not have a BindingResult parameter");
return false;
}
else if (clazzz.isAssignableFrom(MultipartFile.class)) {
log.error("FORM_POST_JSON method '" + beanAndMethodName
+ "' must not have a MultipartFile parameter");
return false;
}
}
return true;
}
};
static final Log log = LogFactory.getLog(ExtDirectMethodType.class);
/**
* Checks if the annotated method contains non supported annotation properties and
* contains non supported parameters and/or parameter annotations. Method logs
* warnings and errors. Check is running during startup of the application. If return
* value is false the method is not registered and cannot be called from the client.
*
* @param beanAndMethodName Name of the bean and method for logging purpose. e.g.
* 'bean.methodname'
* @param clazz The class where the method is member of
* @param method The annotated method
*
* @return true if the method does not contains any errors.
*/
public abstract boolean isValid(String beanAndMethodName, Class<?> clazz,
Method method);
}