package io.github.ibuildthecloud.gdapi.request.handler; import io.github.ibuildthecloud.gdapi.exception.ClientVisibleException; import io.github.ibuildthecloud.gdapi.json.JsonMapper; import io.github.ibuildthecloud.gdapi.request.ApiRequest; import io.github.ibuildthecloud.gdapi.util.RequestUtils; import io.github.ibuildthecloud.gdapi.util.ResponseCodes; import io.github.ibuildthecloud.gdapi.validation.ValidationErrorCodes; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import javax.annotation.PostConstruct; import javax.inject.Inject; import org.apache.commons.io.IOUtils; public class BodyParserRequestHandler extends AbstractApiRequestHandler implements ApiRequestHandler { JsonMapper jsonMarshaller; Set<Class<?>> allowedTypes; @Override public void handle(ApiRequest request) throws IOException { Object obj = getObject(request); request.setRequestObject(merge(obj, request)); } protected Object getObject(ApiRequest request) throws IOException { if (!RequestUtils.mayHaveBody(request.getMethod())) return null; if (!shouldBeParsed(request)) { return null; } InputStream is = request.getInputStream(); if (is == null) { return null; } byte[] content = IOUtils.toByteArray(is); if (content.length == 0) return null; try { Object body = jsonMarshaller.readValue(content); if (isAllowedType(body)) { return body; } } catch (IOException e) { throw new ClientVisibleException(ResponseCodes.BAD_REQUEST, ValidationErrorCodes.INVALID_BODY_CONTENT); } return null; } protected boolean isAllowedType(Object obj) { if (obj == null) { return false; } boolean accepted = false; for (Class<?> type : allowedTypes) { if (type.isAssignableFrom(obj.getClass())) { accepted = true; break; } } return accepted; } protected boolean shouldBeParsed(ApiRequest request) { return true; } protected Object merge(Object body, ApiRequest request) { if (body instanceof Map) { @SuppressWarnings("unchecked") Map<String, Object> map = (Map<String, Object>)body; return mergeMap(map, request); } else if (body instanceof List) { @SuppressWarnings("unchecked") List<Object> list = (List<Object>)body; List<Object> result = new ArrayList<Object>(list.size()); for (Object object : list) { if (isAllowedType(object)) { result.add(merge(object, request)); } } return result; } else { return mergeMap(null, request); } } protected Map<String, Object> mergeMap(Map<String, Object> overlay, ApiRequest request) { Map<String, Object> result = new HashMap<String, Object>(); /* * Notice that this loop makes the value singular if it can. This is because the request params are always a String[] from the * HttpServletRequest.getParametersMap() */ for (Map.Entry<String, Object> entry : request.getRequestParams().entrySet()) { result.put(entry.getKey(), RequestUtils.makeSingularIfCan(entry.getValue())); } if (overlay != null) { for (Map.Entry<String, Object> entry : overlay.entrySet()) { result.put(entry.getKey(), entry.getValue()); } } return result; } @PostConstruct public void init() { if (allowedTypes == null) { allowedTypes = new HashSet<Class<?>>(); allowedTypes.add(Map.class); allowedTypes.add(List.class); } } public JsonMapper getJsonMarshaller() { return jsonMarshaller; } @Inject public void setJsonMarshaller(JsonMapper jsonMarshaller) { this.jsonMarshaller = jsonMarshaller; } public Set<Class<?>> getAllowedTypes() { return allowedTypes; } public void setAllowedTypes(Set<Class<?>> allowedTypes) { this.allowedTypes = allowedTypes; } }