// Generated by delombok at Sun Feb 26 12:31:38 KST 2017 package scouter.bytebuddy.implementation.bind.annotation; import scouter.bytebuddy.ByteBuddy; import scouter.bytebuddy.ClassFileVersion; import scouter.bytebuddy.description.annotation.AnnotationDescription; import scouter.bytebuddy.description.field.FieldDescription; import scouter.bytebuddy.description.field.FieldList; import scouter.bytebuddy.description.method.MethodDescription; import scouter.bytebuddy.description.method.MethodList; import scouter.bytebuddy.description.method.ParameterDescription; import scouter.bytebuddy.description.modifier.Visibility; import scouter.bytebuddy.description.type.TypeDescription; import scouter.bytebuddy.description.type.TypeList; import scouter.bytebuddy.dynamic.DynamicType; import scouter.bytebuddy.dynamic.scaffold.InstrumentedType; import scouter.bytebuddy.dynamic.scaffold.subclass.ConstructorStrategy; import scouter.bytebuddy.implementation.Implementation; import scouter.bytebuddy.implementation.MethodAccessorFactory; import scouter.bytebuddy.implementation.auxiliary.AuxiliaryType; import scouter.bytebuddy.implementation.bind.MethodDelegationBinder; import scouter.bytebuddy.implementation.bytecode.ByteCodeAppender; import scouter.bytebuddy.implementation.bytecode.Duplication; import scouter.bytebuddy.implementation.bytecode.StackManipulation; import scouter.bytebuddy.implementation.bytecode.TypeCreation; import scouter.bytebuddy.implementation.bytecode.assign.Assigner; import scouter.bytebuddy.implementation.bytecode.member.FieldAccess; import scouter.bytebuddy.implementation.bytecode.member.MethodInvocation; import scouter.bytebuddy.implementation.bytecode.member.MethodReturn; import scouter.bytebuddy.implementation.bytecode.member.MethodVariableAccess; import scouter.bytebuddy.matcher.ElementMatchers; import scouter.bytebuddy.jar.asm.MethodVisitor; import scouter.bytebuddy.implementation.MethodDelegation; import java.io.Serializable; import java.lang.annotation.*; import java.util.LinkedHashMap; import java.util.Map; /** * A target method parameter that is annotated with this annotation allows to forward an intercepted method * invocation to another instance. The instance to which a method call is forwarded must be of the most specific * type that declares the intercepted method on the intercepted type. * <p> </p> * Unfortunately, before Java 8, the Java Class Library does not define any interface type which takes a single * {@link java.lang.Object} type and returns another {@link java.lang.Object} type. For this reason, a * {@link Pipe.Binder} needs to be installed explicitly * and registered on a {@link MethodDelegation}. The installed type is allowed to be an * interface without any super types that declares a single method which maps an {@link java.lang.Object} type to * a another {@link java.lang.Object} type as a result value. It is however not prohibited to use generics in the * process. The following example demonstrates how the {@code @Pipe} annotation can be installed on a user type. * As a preparation, one needs to define a type for which the {@code @Pipe} implements the forwarding behavior: * <pre> * interface Forwarder<T, S> { * T forwardTo(S s); * } * </pre> * Based on this type, one can now implement an interceptor: * <pre> * class Interceptor { * private final Foo foo; * * public Interceptor(Foo foo) { * this.foo = foo; * } * * public String intercept(@Pipe Forwarder<String, Foo> forwarder) { * return forwarder.forwardTo(foo); * } * } * </pre> * Using both of these types, one can now install the * {@link Pipe.Binder} and register it on a * {@link MethodDelegation}: * <pre> * MethodDelegation * .to(new Interceptor(new Foo())) * .appendParameterBinder(Pipe.Binder.install(ForwardingType.class)) * </pre> * * @see MethodDelegation * @see TargetMethodAnnotationDrivenBinder */ @Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.PARAMETER) public @interface Pipe { /** * Determines if the generated proxy should be {@link java.io.Serializable}. * * @return {@code true} if the generated proxy should be {@link java.io.Serializable}. */ boolean serializableProxy() default false; /** * A {@link TargetMethodAnnotationDrivenBinder.ParameterBinder} * for binding the {@link Pipe} annotation. */ class Binder implements TargetMethodAnnotationDrivenBinder.ParameterBinder<Pipe> { /** * The method which implements the behavior of forwarding a method invocation. This method needs to define * a single non-static method with an {@link java.lang.Object} to {@link java.lang.Object} mapping. */ private final MethodDescription forwardingMethod; /** * Creates a new binder. This constructor is not doing any validation of the forwarding method and its * declaring type. Such validation is normally performed by the * {@link Pipe.Binder#install(Class)} * method. * * @param forwardingMethod The method which implements the behavior of forwarding a method invocation. This * method needs to define a single non-static method with an {@link java.lang.Object} * to {@link java.lang.Object} mapping. */ protected Binder(MethodDescription forwardingMethod) { this.forwardingMethod = forwardingMethod; } /** * Installs a given type for use on a {@link Pipe} * annotation. The given type must be an interface without any super interfaces and a single method which * maps an {@link java.lang.Object} type to another {@link java.lang.Object} type. The use of generics is * permitted. * * @param type The type to install. * @return A binder for the {@link Pipe} * annotation. */ public static TargetMethodAnnotationDrivenBinder.ParameterBinder<Pipe> install(Class<?> type) { return install(new TypeDescription.ForLoadedType(type)); } /** * Installs a given type for use on a {@link Pipe} * annotation. The given type must be an interface without any super interfaces and a single method which * maps an {@link java.lang.Object} type to another {@link java.lang.Object} type. The use of generics is * permitted. * * @param typeDescription The type to install. * @return A binder for the {@link Pipe} * annotation. */ public static TargetMethodAnnotationDrivenBinder.ParameterBinder<Pipe> install(TypeDescription typeDescription) { return new Binder(onlyMethod(typeDescription)); } /** * Locates the only method of a type that is compatible to being overridden for invoking the proxy. * * @param typeDescription The type that is being installed. * @return Its only method after validation. */ private static MethodDescription onlyMethod(TypeDescription typeDescription) { if (!typeDescription.isInterface()) { throw new IllegalArgumentException(typeDescription + " is not an interface"); } else if (!typeDescription.getInterfaces().isEmpty()) { throw new IllegalArgumentException(typeDescription + " must not extend other interfaces"); } else if (!typeDescription.isPublic()) { throw new IllegalArgumentException(typeDescription + " is mot public"); } MethodList<?> methodCandidates = typeDescription.getDeclaredMethods().filter(ElementMatchers.isAbstract()); if (methodCandidates.size() != 1) { throw new IllegalArgumentException(typeDescription + " must declare exactly one abstract method"); } MethodDescription methodDescription = methodCandidates.getOnly(); if (!methodDescription.getReturnType().asErasure().represents(Object.class)) { throw new IllegalArgumentException(methodDescription + " does not return an Object-type"); } else if (methodDescription.getParameters().size() != 1 || !methodDescription.getParameters().getOnly().getType().asErasure().represents(Object.class)) { throw new IllegalArgumentException(methodDescription + " does not take a single Object-typed argument"); } return methodDescription; } @Override public Class<Pipe> getHandledType() { return Pipe.class; } @Override public MethodDelegationBinder.ParameterBinding<?> bind(AnnotationDescription.Loadable<Pipe> annotation, MethodDescription source, ParameterDescription target, Implementation.Target implementationTarget, Assigner assigner, Assigner.Typing typing) { if (!target.getType().asErasure().equals(forwardingMethod.getDeclaringType())) { throw new IllegalStateException("Illegal use of @Pipe for " + target + " which was installed for " + forwardingMethod.getDeclaringType()); } else if (source.isStatic()) { return MethodDelegationBinder.ParameterBinding.Illegal.INSTANCE; } return new MethodDelegationBinder.ParameterBinding.Anonymous(new Redirection(forwardingMethod.getDeclaringType().asErasure(), source, assigner, annotation.loadSilent().serializableProxy())); } /** * An auxiliary type for performing the redirection of a method invocation as requested by the * {@link Pipe} annotation. */ protected static class Redirection implements AuxiliaryType, StackManipulation { /** * The prefix for naming fields to store method arguments. */ private static final String FIELD_NAME_PREFIX = "argument"; /** * The type that declares the method for forwarding a method invocation. */ private final TypeDescription forwardingType; /** * The method that is to be forwarded. */ private final MethodDescription sourceMethod; /** * The assigner to use. */ private final Assigner assigner; /** * Determines if the generated proxy should be {@link java.io.Serializable}. */ private final boolean serializableProxy; /** * Creates a new redirection. * * @param forwardingType The type that declares the method for forwarding a method invocation. * @param sourceMethod The method that is to be forwarded. * @param assigner The assigner to use. * @param serializableProxy Determines if the generated proxy should be {@link java.io.Serializable}. */ protected Redirection(TypeDescription forwardingType, MethodDescription sourceMethod, Assigner assigner, boolean serializableProxy) { this.forwardingType = forwardingType; this.sourceMethod = sourceMethod; this.assigner = assigner; this.serializableProxy = serializableProxy; } /** * Extracts all parameters of a method to fields. * * @param methodDescription The method to extract the parameters from. * @return A linked hash map of field names to the types of these fields representing all parameters of the * given method. */ private static LinkedHashMap<String, TypeDescription> extractFields(MethodDescription methodDescription) { TypeList parameterTypes = methodDescription.getParameters().asTypeList().asErasures(); LinkedHashMap<String, TypeDescription> typeDescriptions = new LinkedHashMap<String, TypeDescription>(); int currentIndex = 0; for (TypeDescription parameterType : parameterTypes) { typeDescriptions.put(fieldName(currentIndex++), parameterType); } return typeDescriptions; } /** * Creates a new field name. * * @param index The index of the field. * @return The field name that corresponds to the index. */ private static String fieldName(int index) { return String.format("%s%d", FIELD_NAME_PREFIX, index); } @Override public DynamicType make(String auxiliaryTypeName, ClassFileVersion classFileVersion, MethodAccessorFactory methodAccessorFactory) { LinkedHashMap<String, TypeDescription> parameterFields = extractFields(sourceMethod); DynamicType.Builder<?> builder = new ByteBuddy(classFileVersion).subclass(forwardingType, ConstructorStrategy.Default.NO_CONSTRUCTORS).name(auxiliaryTypeName).modifiers(DEFAULT_TYPE_MODIFIER).implement(serializableProxy ? new Class<?>[] {Serializable.class} : new Class<?>[0]).method(ElementMatchers.<MethodDescription>isAbstract().and(ElementMatchers.isDeclaredBy(forwardingType))).intercept(new MethodCall(sourceMethod, assigner)).defineConstructor().withParameters(parameterFields.values()).intercept(ConstructorCall.INSTANCE); for (Map.Entry<String, TypeDescription> field : parameterFields.entrySet()) { builder = builder.defineField(field.getKey(), field.getValue(), Visibility.PRIVATE); } return builder.make(); } @Override public boolean isValid() { return true; } @Override public Size apply(MethodVisitor methodVisitor, Implementation.Context implementationContext) { TypeDescription forwardingType = implementationContext.register(this); return new Compound(TypeCreation.of(forwardingType), Duplication.SINGLE, MethodVariableAccess.allArgumentsOf(sourceMethod), MethodInvocation.invoke(forwardingType.getDeclaredMethods().filter(ElementMatchers.isConstructor()).getOnly())).apply(methodVisitor, implementationContext); } /** * The implementation to implement a * {@link Pipe.Binder.Redirection}'s * constructor. */ protected enum ConstructorCall implements Implementation { /** * The singleton instance. */ INSTANCE; /** * A reference of the {@link Object} type default constructor. */ private final MethodDescription.InDefinedShape objectTypeDefaultConstructor; /** * Creates the constructor call singleton. */ ConstructorCall() { objectTypeDefaultConstructor = TypeDescription.OBJECT.getDeclaredMethods().filter(ElementMatchers.isConstructor()).getOnly(); } @Override public InstrumentedType prepare(InstrumentedType instrumentedType) { return instrumentedType; } @Override public ByteCodeAppender appender(Target implementationTarget) { return new Appender(implementationTarget.getInstrumentedType()); } /** * The appender for implementing the * {@link Pipe.Binder.Redirection.ConstructorCall}. */ private static class Appender implements ByteCodeAppender { /** * The instrumented type being created. */ private final TypeDescription instrumentedType; /** * Creates a new appender. * * @param instrumentedType The instrumented type that is being created. */ private Appender(TypeDescription instrumentedType) { this.instrumentedType = instrumentedType; } @Override public Size apply(MethodVisitor methodVisitor, Context implementationContext, MethodDescription instrumentedMethod) { FieldList<?> fieldList = instrumentedType.getDeclaredFields(); StackManipulation[] fieldLoading = new StackManipulation[fieldList.size()]; int index = 0; for (FieldDescription fieldDescription : fieldList) { fieldLoading[index] = new StackManipulation.Compound(MethodVariableAccess.loadThis(), MethodVariableAccess.load(instrumentedMethod.getParameters().get(index)), FieldAccess.forField(fieldDescription).write()); index++; } StackManipulation.Size stackSize = new StackManipulation.Compound(MethodVariableAccess.loadThis(), MethodInvocation.invoke(ConstructorCall.INSTANCE.objectTypeDefaultConstructor), new StackManipulation.Compound(fieldLoading), MethodReturn.VOID).apply(methodVisitor, implementationContext); return new Size(stackSize.getMaximalSize(), instrumentedMethod.getStackSize()); } @java.lang.Override @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") public boolean equals(final java.lang.Object o) { if (o == this) return true; if (!(o instanceof Pipe.Binder.Redirection.ConstructorCall.Appender)) return false; final Pipe.Binder.Redirection.ConstructorCall.Appender other = (Pipe.Binder.Redirection.ConstructorCall.Appender) o; if (!other.canEqual((java.lang.Object) this)) return false; final java.lang.Object this$instrumentedType = this.instrumentedType; final java.lang.Object other$instrumentedType = other.instrumentedType; if (this$instrumentedType == null ? other$instrumentedType != null : !this$instrumentedType.equals(other$instrumentedType)) return false; return true; } @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") protected boolean canEqual(final java.lang.Object other) { return other instanceof Pipe.Binder.Redirection.ConstructorCall.Appender; } @java.lang.Override @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") public int hashCode() { final int PRIME = 59; int result = 1; final java.lang.Object $instrumentedType = this.instrumentedType; result = result * PRIME + ($instrumentedType == null ? 43 : $instrumentedType.hashCode()); return result; } } } /** * The implementation to implement a * {@link Pipe.Binder.Redirection}'s * forwarding method. */ protected static class MethodCall implements Implementation { /** * The method that is invoked by the implemented method. */ private final MethodDescription redirectedMethod; /** * The assigner to be used for invoking the forwarded method. */ private final Assigner assigner; /** * Creates a new method call implementation. * * @param redirectedMethod The method that is invoked by the implemented method. * @param assigner The assigner to be used for invoking the forwarded method. */ private MethodCall(MethodDescription redirectedMethod, Assigner assigner) { this.redirectedMethod = redirectedMethod; this.assigner = assigner; } @Override public InstrumentedType prepare(InstrumentedType instrumentedType) { return instrumentedType; } @Override public ByteCodeAppender appender(Target implementationTarget) { if (!redirectedMethod.isAccessibleTo(implementationTarget.getInstrumentedType())) { throw new IllegalStateException("Cannot invoke " + redirectedMethod + " from outside of class via @Pipe proxy"); } return new Appender(implementationTarget.getInstrumentedType()); } /** * The appender for implementing the * {@link Pipe.Binder.Redirection.MethodCall}. */ private class Appender implements ByteCodeAppender { /** * The instrumented type that is implemented. */ private final TypeDescription instrumentedType; /** * Creates a new appender. * * @param instrumentedType The instrumented type to be implemented. */ private Appender(TypeDescription instrumentedType) { this.instrumentedType = instrumentedType; } @Override public Size apply(MethodVisitor methodVisitor, Context implementationContext, MethodDescription instrumentedMethod) { StackManipulation thisReference = MethodVariableAccess.of(instrumentedType).loadFrom(0); FieldList<?> fieldList = instrumentedType.getDeclaredFields(); StackManipulation[] fieldLoading = new StackManipulation[fieldList.size()]; int index = 0; for (FieldDescription fieldDescription : fieldList) { fieldLoading[index++] = new StackManipulation.Compound(thisReference, FieldAccess.forField(fieldDescription).read()); } StackManipulation.Size stackSize = new StackManipulation.Compound(MethodVariableAccess.REFERENCE.loadFrom(1), assigner.assign(TypeDescription.Generic.OBJECT, redirectedMethod.getDeclaringType().asGenericType(), Assigner.Typing.DYNAMIC), new StackManipulation.Compound(fieldLoading), MethodInvocation.invoke(redirectedMethod), assigner.assign(redirectedMethod.getReturnType(), instrumentedMethod.getReturnType(), Assigner.Typing.DYNAMIC), MethodReturn.REFERENCE).apply(methodVisitor, implementationContext); return new Size(stackSize.getMaximalSize(), instrumentedMethod.getStackSize()); } /** * Returns the outer instance. * * @return The outer instance. */ private MethodCall getMethodCall() { return MethodCall.this; } // HE: Remove when Lombok support for getOuter is added. @Override public boolean equals(Object other) { return this == other || !(other == null || getClass() != other.getClass()) && instrumentedType.equals(((Appender) other).instrumentedType) && MethodCall.this.equals(((Appender) other).getMethodCall()); } // HE: Remove when Lombok support for getOuter is added. @Override public int hashCode() { return 31 * MethodCall.this.hashCode() + instrumentedType.hashCode(); } } @java.lang.Override @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") public boolean equals(final java.lang.Object o) { if (o == this) return true; if (!(o instanceof Pipe.Binder.Redirection.MethodCall)) return false; final Pipe.Binder.Redirection.MethodCall other = (Pipe.Binder.Redirection.MethodCall) o; if (!other.canEqual((java.lang.Object) this)) return false; final java.lang.Object this$redirectedMethod = this.redirectedMethod; final java.lang.Object other$redirectedMethod = other.redirectedMethod; if (this$redirectedMethod == null ? other$redirectedMethod != null : !this$redirectedMethod.equals(other$redirectedMethod)) return false; final java.lang.Object this$assigner = this.assigner; final java.lang.Object other$assigner = other.assigner; if (this$assigner == null ? other$assigner != null : !this$assigner.equals(other$assigner)) return false; return true; } @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") protected boolean canEqual(final java.lang.Object other) { return other instanceof Pipe.Binder.Redirection.MethodCall; } @java.lang.Override @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") public int hashCode() { final int PRIME = 59; int result = 1; final java.lang.Object $redirectedMethod = this.redirectedMethod; result = result * PRIME + ($redirectedMethod == null ? 43 : $redirectedMethod.hashCode()); final java.lang.Object $assigner = this.assigner; result = result * PRIME + ($assigner == null ? 43 : $assigner.hashCode()); return result; } } @java.lang.Override @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") public boolean equals(final java.lang.Object o) { if (o == this) return true; if (!(o instanceof Pipe.Binder.Redirection)) return false; final Pipe.Binder.Redirection other = (Pipe.Binder.Redirection) o; if (!other.canEqual((java.lang.Object) this)) return false; final java.lang.Object this$forwardingType = this.forwardingType; final java.lang.Object other$forwardingType = other.forwardingType; if (this$forwardingType == null ? other$forwardingType != null : !this$forwardingType.equals(other$forwardingType)) return false; final java.lang.Object this$sourceMethod = this.sourceMethod; final java.lang.Object other$sourceMethod = other.sourceMethod; if (this$sourceMethod == null ? other$sourceMethod != null : !this$sourceMethod.equals(other$sourceMethod)) return false; final java.lang.Object this$assigner = this.assigner; final java.lang.Object other$assigner = other.assigner; if (this$assigner == null ? other$assigner != null : !this$assigner.equals(other$assigner)) return false; if (this.serializableProxy != other.serializableProxy) return false; return true; } @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") protected boolean canEqual(final java.lang.Object other) { return other instanceof Pipe.Binder.Redirection; } @java.lang.Override @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") public int hashCode() { final int PRIME = 59; int result = 1; final java.lang.Object $forwardingType = this.forwardingType; result = result * PRIME + ($forwardingType == null ? 43 : $forwardingType.hashCode()); final java.lang.Object $sourceMethod = this.sourceMethod; result = result * PRIME + ($sourceMethod == null ? 43 : $sourceMethod.hashCode()); final java.lang.Object $assigner = this.assigner; result = result * PRIME + ($assigner == null ? 43 : $assigner.hashCode()); result = result * PRIME + (this.serializableProxy ? 79 : 97); return result; } } @java.lang.Override @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") public boolean equals(final java.lang.Object o) { if (o == this) return true; if (!(o instanceof Pipe.Binder)) return false; final Pipe.Binder other = (Pipe.Binder) o; if (!other.canEqual((java.lang.Object) this)) return false; final java.lang.Object this$forwardingMethod = this.forwardingMethod; final java.lang.Object other$forwardingMethod = other.forwardingMethod; if (this$forwardingMethod == null ? other$forwardingMethod != null : !this$forwardingMethod.equals(other$forwardingMethod)) return false; return true; } @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") protected boolean canEqual(final java.lang.Object other) { return other instanceof Pipe.Binder; } @java.lang.Override @java.lang.SuppressWarnings("all") @javax.annotation.Generated("lombok") public int hashCode() { final int PRIME = 59; int result = 1; final java.lang.Object $forwardingMethod = this.forwardingMethod; result = result * PRIME + ($forwardingMethod == null ? 43 : $forwardingMethod.hashCode()); return result; } } }