/** * */ package org.minnal.instrument.resource; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.lang.annotation.Annotation; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Type; import java.util.List; import java.util.Map; import java.util.Map.Entry; import javax.ws.rs.core.HttpHeaders; import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.ext.MessageBodyReader; import javax.ws.rs.ext.Providers; import org.activejpa.entity.Condition.Operator; import org.activejpa.entity.Filter; import org.apache.commons.beanutils.ConvertUtils; import org.apache.commons.lang3.reflect.MethodUtils; import org.javalite.common.Inflector; import org.minnal.instrument.MinnalInstrumentationException; import org.minnal.instrument.entity.metadata.ParameterMetaData; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.base.Function; import com.google.common.collect.Lists; import com.google.common.reflect.TypeParameter; import com.google.common.reflect.TypeToken; /** * @author ganeshs * */ public class ResourceUtil { public static final String PER_PAGE = "per_page"; public static final String PAGE_NO = "page"; public static final String TOTAL = "total"; public static final String COUNT = "count"; public static final String DATA = "data"; private static final Logger logger = LoggerFactory.getLogger(ResourceUtil.class); public static Filter getFilter(MultivaluedMap<String, String> queryParams) { Integer perPage = (Integer) ConvertUtils.convert(queryParams.getFirst(PER_PAGE), Integer.class); Integer pageNo = (Integer) ConvertUtils.convert(queryParams.getFirst(PAGE_NO), Integer.class); return new Filter(perPage, pageNo); } public static Filter getFilter(MultivaluedMap<String, String> queryParams, final List<String> paramNames) { Filter filter = getFilter(queryParams); for (Entry<String, List<String>> entry : queryParams.entrySet()) { if (! paramNames.contains(entry.getKey())) { continue; } if (entry.getValue().size() == 1) { filter.addCondition(Inflector.camelize(entry.getKey(), false), entry.getValue().get(0)); } else { filter.addCondition(Inflector.camelize(entry.getKey(), false), Operator.in, entry.getValue()); } } return filter; } public static boolean isCommaSeparated(String value) { return value.contains(","); } public static String[] getCommaSeparatedValues(String value) { return value.split(","); } /** * @param inputStream * @param providers * @param httpHeaders * @param type * @param genericType * @param annotations * @return */ public static <T> T getContent(InputStream inputStream, Providers providers, HttpHeaders httpHeaders, Class<T> type, Type genericType, Annotation[] annotations) { MessageBodyReader<T> reader = providers.getMessageBodyReader(type, genericType, annotations, httpHeaders.getMediaType()); try { return reader.readFrom(type, genericType, annotations, httpHeaders.getMediaType(), httpHeaders.getRequestHeaders(), inputStream); } catch (Exception e) {e.printStackTrace(); throw new MinnalInstrumentationException("Failed while getting the content from the request stream", e); } } static <V> TypeToken<List<V>> listType(Class<V> clazz) { return new TypeToken<List<V>>() {}.where(new TypeParameter<V>() {}, TypeToken.of(clazz)); } /** * Returns the request content as the given type. IF the content is a collection, returns a list of elements of the given type * * @param request * @param type * @return */ public static Object getContent(byte[] raw, Providers providers, HttpHeaders httpHeaders, Class<?> type) { try { return getContent(new ByteArrayInputStream(raw), providers, httpHeaders, type, type, new Annotation[] {}); } catch (MinnalInstrumentationException e) { logger.trace("Failed while getting the content from the request stream", e); Throwable ex = e; while (ex.getCause() != null) { if (ex.getCause() instanceof IOException) { return getContent(new ByteArrayInputStream(raw), providers, httpHeaders, List.class, listType(type).getType(), new Annotation[]{}); } ex = ex.getCause(); } throw e; } } /** * @param parameters * @param request * @param values * @return */ private static Object[] getActionParameterValues(List<ParameterMetaData> parameters, Map<String, Object> body, Map<String, Object> values) { if (body != null) { values.putAll(body); } Object[] params = new Object[parameters.size()]; for (int i = 0; i < parameters.size(); i++) { ParameterMetaData param = parameters.get(i); Object value = values.get(param.getName()); if (value != null && ! value.getClass().equals(param.getType())) { value = ConvertUtils.convert(value, param.getType()); } params[i] = value; } return params; } /** * TODO: Handle bulk actions * * @param model * @param actionName * @param parameters * @param rawContent * @param providers * @param httpHeaders * @param values * @return * @throws Throwable */ public static Object invokeAction(Object model, String actionName, List<ParameterMetaData> parameters, byte[] rawContent, Providers providers, HttpHeaders httpHeaders, Map<String, Object> values) throws Throwable { List<Class<?>> types = Lists.transform(parameters, new Function<ParameterMetaData, Class<?>>() { @Override public Class<?> apply(ParameterMetaData input) { return input == null ? null : input.getType(); } }); Method method = MethodUtils.getAccessibleMethod(model.getClass(), actionName, types.toArray(new Class[0])); if (method == null) { logger.error("Unable to get the action - {} for the model - {}", actionName, model.getClass()); throw new MinnalInstrumentationException("Unable to get the action - " + actionName + " for the model - " + model.getClass()); } Map<String, Object> payload = getContent(new ByteArrayInputStream(rawContent), providers, httpHeaders, Map.class, Map.class, null); Object[] arguments = getActionParameterValues(parameters, payload, values); try { return method.invoke(model, arguments); } catch (InvocationTargetException e) { logger.error("Failed while invoking the action - " + actionName + " for the model - " + model.getClass(), e); throw e.getCause(); } catch (Exception e) { logger.error("Failed while invoking the action - " + actionName + " for the model - " + model.getClass(), e); throw new MinnalInstrumentationException("Failed while invoking the action - " + actionName + " for the model - " + model.getClass(), e); } } }