package com.klopaj.interceptor; import com.klopaj.interceptor.meta.JsonAction; import com.klopaj.interceptor.meta.JsonIn; import flexjson.JSONDeserializer; import flexjson.JSONSerializer; import jodd.bean.BeanUtil; import jodd.madvoc.ActionRequest; import jodd.madvoc.interceptor.ActionInterceptor; import jodd.servlet.ServletUtil; import javax.servlet.http.HttpServletRequest; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.ParameterizedType; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; public class JsonActionInterceptor extends ActionInterceptor { @Override public Object intercept(ActionRequest actionRequest) throws Exception { HttpServletRequest request = actionRequest.getHttpServletRequest(); Object action = actionRequest.getAction(); Method method = actionRequest.getActionConfig().getActionClassMethod(); if (method.isAnnotationPresent(JsonAction.class)) { // if this is a json action, get all @JsonIn fields, and try to parse them from input String jsonStringValue = ServletUtil.readRequestBody(request); Object fieldValues = new JSONDeserializer().deserialize(jsonStringValue); List fields = getAllDeclaredFields(action.getClass()); for (Field field : fields) { if (field != null) { if (field.isAnnotationPresent(JsonIn.class)) { // This field is marked as @JsonIn meaning it should be in the content of the response addFieldValueToObject(action, fieldValues, field); } } } } return actionRequest.invoke(); } private void addFieldValueToObject(Object action, Object fieldValues, Field field) throws IllegalAccessException, InstantiationException { Class fieldType = field.getType(); if (fieldType == String.class || fieldType.isPrimitive() || Number.class.isAssignableFrom(fieldType)) { // it's simple string, just try to get that value from json Object fieldValue = getFieldValue(field.getName(), fieldValues); field.setAccessible(true); field.set(action, fieldValue); } else if (List.class.isAssignableFrom(fieldType)) { // Handle lists and similar System.out.println("It's a collection"); List fieldValue = (List) getFieldValue(field.getName(), fieldValues); if (field.getGenericType() instanceof ParameterizedType) { ParameterizedType itemParameterizedType = (ParameterizedType) field.getGenericType(); Class itemType = (Class) itemParameterizedType.getActualTypeArguments()[0]; List result = new ArrayList(); for (Object object : fieldValue) { if (object != null) { System.out.println("Collection item " + object); Object itemInstance = itemType.newInstance(); // Object is most likely HashMap if (object instanceof Map) { Map objectAsMap = (Map) object; for (Field itemField : getAllDeclaredFields(itemInstance.getClass())) { if (objectAsMap.get(itemField.getName()) != null) { // we have that field's value addFieldValueToObject(itemInstance, objectAsMap, itemField); } } } result.add(itemInstance); } } BeanUtil.setDeclaredPropertyForced(action, field.getName(), result); } } else { // Most likely this is a POJO. Object fieldValue = getFieldValue(field.getName(), fieldValues); // TODO: "Number.class" part is a hack cause current Entity (hibernate) Id is marked as "extends Serializable", but the value is always a number. if (fieldValue.getClass().isPrimitive() || Number.class.isAssignableFrom(fieldValue.getClass())) { field.setAccessible(true); field.set(action, fieldValue); } else { String objectSerialized = new JSONSerializer().exclude("*.class").serialize(fieldValue); Object fieldValueObject = new JSONDeserializer().deserialize(objectSerialized, fieldType); field.setAccessible(true); field.set(action, fieldValueObject); } } } private Object getFieldValue(String fieldName, Object fieldValues) { if (fieldValues instanceof Map) { Map mapFieldValue = (Map) fieldValues; return mapFieldValue.get(fieldName); } return null; } /** * Returns all declared fields (including all supper classes' fields) * * @param type Class object that we need to get it's declared fields * @return List of Fields */ public List getAllDeclaredFields(Class type) { List result = new ArrayList(); return getAllDeclaredFields(result, type); } private List getAllDeclaredFields(List fields, Class type) { Collections.addAll(fields, type.getDeclaredFields()); if (type.getSuperclass() != null) { fields = getAllDeclaredFields(fields, type.getSuperclass()); } return fields; } }