package org.jboss.resteasy.client.jaxrs.internal.proxy.processors; import org.jboss.resteasy.client.jaxrs.internal.ClientConfiguration; import org.jboss.resteasy.client.jaxrs.internal.ClientInvocation; import org.jboss.resteasy.spi.LoggableFailure; import javax.ws.rs.client.WebTarget; import java.io.ByteArrayOutputStream; import java.io.DataOutputStream; import java.lang.annotation.Annotation; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.lang.reflect.Type; import java.security.DigestOutputStream; import java.security.MessageDigest; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a> * @version $Revision: 1 $ */ public class FormProcessor implements InvocationProcessor, WebTargetProcessor { protected HashMap<Field, Object> fieldMap = new HashMap<Field, Object>(); private static class GetterMethod { private GetterMethod(Method method, Object processor) { this.method = method; this.processor = processor; } public Method method; public Object processor; } protected List<GetterMethod> getters = new ArrayList<GetterMethod>(); protected HashMap<Long, Method> getterHashes = new HashMap<Long, Method>(); protected Class clazz; public FormProcessor(Class clazz, ClientConfiguration configuration) { this.clazz = clazz; populateMap(clazz, configuration); } public static long methodHash(Method method) throws Exception { Class[] parameterTypes = method.getParameterTypes(); StringBuilder methodDesc = new StringBuilder(method.getName()).append("("); for (int j = 0; j < parameterTypes.length; j++) { methodDesc.append(getTypeString(parameterTypes[j])); } methodDesc.append(")").append(getTypeString(method.getReturnType())); return createHash(methodDesc.toString()); } public static long createHash(String methodDesc) throws Exception { long hash = 0; ByteArrayOutputStream bytearrayoutputstream = new ByteArrayOutputStream(512); MessageDigest messagedigest = MessageDigest.getInstance("SHA"); DataOutputStream dataoutputstream = new DataOutputStream(new DigestOutputStream(bytearrayoutputstream, messagedigest)); dataoutputstream.writeUTF(methodDesc); dataoutputstream.flush(); byte abyte0[] = messagedigest.digest(); for (int j = 0; j < Math.min(8, abyte0.length); j++) hash += (long) (abyte0[j] & 0xff) << j * 8; return hash; } static String getTypeString(Class cl) { if (cl == Byte.TYPE) { return "B"; } else if (cl == Character.TYPE) { return "C"; } else if (cl == Double.TYPE) { return "D"; } else if (cl == Float.TYPE) { return "F"; } else if (cl == Integer.TYPE) { return "I"; } else if (cl == Long.TYPE) { return "J"; } else if (cl == Short.TYPE) { return "S"; } else if (cl == Boolean.TYPE) { return "Z"; } else if (cl == Void.TYPE) { return "V"; } else if (cl.isArray()) { return "[" + getTypeString(cl.getComponentType()); } else { return "L" + cl.getName().replace('.', '/') + ";"; } } protected void populateMap(Class clazz, ClientConfiguration configuration) { for (Field field : clazz.getDeclaredFields()) { Annotation[] annotations = field.getAnnotations(); if (annotations == null || annotations.length == 0) continue; Class type = field.getType(); Type genericType = field.getGenericType(); Object processor = ProcessorFactory.createProcessor( clazz, configuration, type, annotations, genericType, field, true); if (processor != null) { if (!Modifier.isPublic(field.getModifiers())) field.setAccessible(true); fieldMap.put(field, processor); } } for (Method method : clazz.getDeclaredMethods()) { if (!method.getName().startsWith("get")) continue; if (method.getParameterTypes().length > 0) continue; Annotation[] annotations = method.getAnnotations(); if (annotations == null || annotations.length == 0) continue; Class type = method.getReturnType(); Type genericType = method.getGenericReturnType(); Object processor = ProcessorFactory .createProcessor(clazz, configuration, type, annotations, genericType, method, true); if (processor != null) { long hash = 0; try { hash = methodHash(method); } catch (Exception e) { throw new RuntimeException(e); } if (!Modifier.isPrivate(method.getModifiers())) { Method older = getterHashes.get(hash); if (older != null) continue; } if (!Modifier.isPublic(method.getModifiers())) method.setAccessible(true); getters.add(new GetterMethod(method, processor)); getterHashes.put(hash, method); } } if (clazz.getSuperclass() != null && !clazz.getSuperclass().equals(Object.class)) populateMap(clazz.getSuperclass(), configuration); } interface Process { Object process(Object target, Object value, Object processor); } @Override public WebTarget build(WebTarget target, Object param) { if (param == null) return target; return (WebTarget) process(new Process() { @Override public Object process(Object target, Object value, Object processor) { return build((WebTarget)target, value, processor); } }, target, param); } @Override public void process(ClientInvocation invocation, Object param) { process(new Process() { @Override public Object process(Object target, Object value, Object processor) { processParam((ClientInvocation)target, value, processor); return target; } }, invocation, param); } protected Object process(Process process, Object target, Object param) { if (param == null) return target; for (Map.Entry<Field, Object> entry : fieldMap.entrySet()) { try { Object val = entry.getKey().get(param); Object proc = entry.getValue(); target = process.process(target, val, proc); } catch (IllegalAccessException e) { throw new LoggableFailure(e); } } for (GetterMethod getter : getters) { Object val = null; try { val = getter.method.invoke(param); } catch (IllegalAccessException e) { throw new RuntimeException(e); } catch (InvocationTargetException e) { throw new RuntimeException(e); } Object proc = getter.processor; target = process.process(target, val, proc); } return target; } private WebTarget build(WebTarget target, Object val, Object proc) { if (proc instanceof WebTargetProcessor) { WebTargetProcessor processor = (WebTargetProcessor) proc; target = processor.build(target, val); } return target; } private void processParam(ClientInvocation invocation, Object val, Object proc) { if (proc instanceof InvocationProcessor) { InvocationProcessor processor = (InvocationProcessor) proc; processor.process(invocation, val); } } }