/* * Copyright 2016 the original author or authors. * * 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 org.gradle.internal.remote.internal.hub; import org.gradle.internal.serialize.*; import org.gradle.internal.dispatch.MethodInvocation; import java.io.IOException; import java.lang.reflect.Method; import java.util.HashMap; import java.util.Map; public class MethodInvocationSerializer implements StatefulSerializer<MethodInvocation> { private final ClassLoader classLoader; private final MethodArgsSerializer methodArgsSerializer; public MethodInvocationSerializer(ClassLoader classLoader, MethodArgsSerializer methodArgsSerializer) { this.classLoader = classLoader; this.methodArgsSerializer = methodArgsSerializer; } public ObjectReader<MethodInvocation> newReader(Decoder decoder) { return new MethodInvocationReader(decoder, classLoader, methodArgsSerializer); } public ObjectWriter<MethodInvocation> newWriter(Encoder encoder) { return new MethodInvocationWriter(encoder, methodArgsSerializer); } private static class MethodDetails { final int methodId; final Method method; final Serializer<Object[]> argsSerializer; MethodDetails(int methodId, Method method, Serializer<Object[]> argsSerializer) { this.methodId = methodId; this.method = method; this.argsSerializer = argsSerializer; } } private static class MethodInvocationWriter implements ObjectWriter<MethodInvocation> { private final Encoder encoder; private final MethodArgsSerializer methodArgsSerializer; private final Map<Method, MethodDetails> methods = new HashMap<Method, MethodDetails>(); MethodInvocationWriter(Encoder encoder, MethodArgsSerializer methodArgsSerializer) { this.encoder = encoder; this.methodArgsSerializer = methodArgsSerializer; } public void write(MethodInvocation value) throws Exception { if (value.getArguments().length != value.getMethod().getParameterTypes().length) { throw new IllegalArgumentException(String.format("Mismatched number of parameters to method %s.", value.getMethod())); } MethodDetails methodDetails = writeMethod(value.getMethod()); writeArgs(methodDetails, value); } private void writeArgs(MethodDetails methodDetails, MethodInvocation value) throws Exception { methodDetails.argsSerializer.write(encoder, value.getArguments()); } private MethodDetails writeMethod(Method method) throws IOException { MethodDetails methodDetails = methods.get(method); if (methodDetails == null) { int methodId = methods.size(); methodDetails = new MethodDetails(methodId, method, methodArgsSerializer.forTypes(method.getParameterTypes())); methods.put(method, methodDetails); encoder.writeSmallInt(methodId); encoder.writeString(method.getDeclaringClass().getName()); encoder.writeString(method.getName()); encoder.writeSmallInt(method.getParameterTypes().length); for (int i = 0; i < method.getParameterTypes().length; i++) { Class<?> paramType = method.getParameterTypes()[i]; encoder.writeString(paramType.getName()); } } else { encoder.writeSmallInt(methodDetails.methodId); } return methodDetails; } } private static class MethodInvocationReader implements ObjectReader<MethodInvocation> { private static final Map<String, Class<?>> PRIMITIVE_TYPES; static { PRIMITIVE_TYPES = new HashMap<String, Class<?>>(); PRIMITIVE_TYPES.put(Integer.TYPE.getName(), Integer.TYPE); } private final Decoder decoder; private final ClassLoader classLoader; private final MethodArgsSerializer methodArgsSerializer; private final Map<Integer, MethodDetails> methods = new HashMap<Integer, MethodDetails>(); MethodInvocationReader(Decoder decoder, ClassLoader classLoader, MethodArgsSerializer methodArgsSerializer) { this.decoder = decoder; this.classLoader = classLoader; this.methodArgsSerializer = methodArgsSerializer; } public MethodInvocation read() throws Exception { MethodDetails methodDetails = readMethod(); Object[] args = readArguments(methodDetails); return new MethodInvocation(methodDetails.method, args); } private Object[] readArguments(MethodDetails methodDetails) throws Exception { return methodDetails.argsSerializer.read(decoder); } private MethodDetails readMethod() throws ClassNotFoundException, NoSuchMethodException, IOException { int methodId = decoder.readSmallInt(); MethodDetails methodDetails = methods.get(methodId); if (methodDetails == null) { Class<?> declaringClass = readType(); String methodName = decoder.readString(); int paramCount = decoder.readSmallInt(); Class<?>[] paramTypes = new Class<?>[paramCount]; for (int i = 0; i < paramTypes.length; i++) { paramTypes[i] = readType(); } Method method = declaringClass.getDeclaredMethod(methodName, paramTypes); methodDetails = new MethodDetails(methodId, method, methodArgsSerializer.forTypes(method.getParameterTypes())); methods.put(methodId, methodDetails); } return methodDetails; } private Class<?> readType() throws ClassNotFoundException, IOException { String typeName = decoder.readString(); Class<?> paramType = PRIMITIVE_TYPES.get(typeName); if (paramType == null) { paramType = Class.forName(typeName, false, classLoader); } return paramType; } } }