|
Java example source code file (ProxyFactory.java)
This example Java source code file (ProxyFactory.java) is included in the alvinalexander.com
"Java Source Code
Warehouse" project. The intent of this project is to help you "Learn
Java by Example" TM.
Learn more about this Java project at its project page.
The ProxyFactory.java Java example source code
/**
* Copyright (C) 2009 Google Inc.
*
* 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 com.google.inject.internal;
import static com.google.inject.internal.BytecodeGen.newFastClassForMember;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.inject.spi.InjectionPoint;
import net.sf.cglib.core.MethodWrapper;
import net.sf.cglib.proxy.Callback;
import net.sf.cglib.proxy.CallbackFilter;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.reflect.FastClass;
import org.aopalliance.intercept.MethodInterceptor;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Builds a construction proxy that can participate in AOP. This class manages applying type and
* method matchers to come up with the set of intercepted methods.
*
* @author jessewilson@google.com (Jesse Wilson)
*/
final class ProxyFactory<T> implements ConstructionProxyFactory {
private static final Logger logger = Logger.getLogger(ProxyFactory.class.getName());
private final InjectionPoint injectionPoint;
private final ImmutableMap<Method, List interceptors;
private final Class<T> declaringClass;
private final List<Method> methods;
private final Callback[] callbacks;
/**
* PUBLIC is default; it's used if all the methods we're intercepting are public. This impacts
* which classloader we should use for loading the enhanced class
*/
private BytecodeGen.Visibility visibility = BytecodeGen.Visibility.PUBLIC;
ProxyFactory(InjectionPoint injectionPoint, Iterable<MethodAspect> methodAspects) {
this.injectionPoint = injectionPoint;
@SuppressWarnings("unchecked") // the member of injectionPoint is always a Constructor<T>
Constructor<T> constructor = (Constructor) injectionPoint.getMember();
declaringClass = constructor.getDeclaringClass();
// Find applicable aspects. Bow out if none are applicable to this class.
List<MethodAspect> applicableAspects = Lists.newArrayList();
for (MethodAspect methodAspect : methodAspects) {
if (methodAspect.matches(declaringClass)) {
applicableAspects.add(methodAspect);
}
}
if (applicableAspects.isEmpty()) {
interceptors = ImmutableMap.of();
methods = ImmutableList.of();
callbacks = null;
return;
}
// Get list of methods from cglib.
methods = Lists.newArrayList();
Enhancer.getMethods(declaringClass, null, methods);
// Create method/interceptor holders and record indices.
List<MethodInterceptorsPair> methodInterceptorsPairs = Lists.newArrayList();
for (Method method : methods) {
methodInterceptorsPairs.add(new MethodInterceptorsPair(method));
}
// Iterate over aspects and add interceptors for the methods they apply to
boolean anyMatched = false;
for (MethodAspect methodAspect : applicableAspects) {
for (MethodInterceptorsPair pair : methodInterceptorsPairs) {
if (methodAspect.matches(pair.method)) {
if(pair.method.isSynthetic()) {
logger.log(Level.WARNING,
"Method [{0}] is synthetic and is being intercepted by {1}."
+ " This could indicate a bug. The method may be intercepted twice,"
+ " or may not be intercepted at all.",
new Object[] { pair.method, methodAspect.interceptors() });
}
visibility = visibility.and(BytecodeGen.Visibility.forMember(pair.method));
pair.addAll(methodAspect.interceptors());
anyMatched = true;
}
}
}
if (!anyMatched) {
interceptors = ImmutableMap.of();
callbacks = null;
return;
}
ImmutableMap.Builder<Method, List interceptorsMapBuilder = null; // lazy
callbacks = new Callback[methods.size()];
for (int i = 0; i < methods.size(); i++) {
MethodInterceptorsPair pair = methodInterceptorsPairs.get(i);
if (!pair.hasInterceptors()) {
callbacks[i] = net.sf.cglib.proxy.NoOp.INSTANCE;
continue;
}
if (interceptorsMapBuilder == null) {
interceptorsMapBuilder = ImmutableMap.builder();
}
ImmutableList<MethodInterceptor> deDuplicated =
ImmutableSet.copyOf(pair.interceptors).asList();
interceptorsMapBuilder.put(pair.method, deDuplicated);
callbacks[i] = new InterceptorStackCallback(pair.method, deDuplicated);
}
interceptors = interceptorsMapBuilder != null
? interceptorsMapBuilder.build()
: ImmutableMap.<Method, Listof();
}
/**
* Returns the interceptors that apply to the constructed type.
*/
public ImmutableMap<Method, List getInterceptors() {
return interceptors;
}
public ConstructionProxy<T> create() throws ErrorsException {
if (interceptors.isEmpty()) {
return new DefaultConstructionProxyFactory<T>(injectionPoint).create();
}
@SuppressWarnings("unchecked")
Class<? extends Callback>[] callbackTypes = new Class[callbacks.length];
for (int i = 0; i < callbacks.length; i++) {
if (callbacks[i] == net.sf.cglib.proxy.NoOp.INSTANCE) {
callbackTypes[i] = net.sf.cglib.proxy.NoOp.class;
} else {
callbackTypes[i] = net.sf.cglib.proxy.MethodInterceptor.class;
}
}
// Create the proxied class. We're careful to ensure that all enhancer state is not-specific
// to this injector. Otherwise, the proxies for each injector will waste PermGen memory
try {
Enhancer enhancer = BytecodeGen.newEnhancer(declaringClass, visibility);
enhancer.setCallbackFilter(new IndicesCallbackFilter(methods));
enhancer.setCallbackTypes(callbackTypes);
return new ProxyConstructor<T>(enhancer, injectionPoint, callbacks, interceptors);
} catch (Throwable e) {
throw new Errors().errorEnhancingClass(declaringClass, e).toException();
}
}
private static class MethodInterceptorsPair {
final Method method;
List<MethodInterceptor> interceptors; // lazy
MethodInterceptorsPair(Method method) {
this.method = method;
}
void addAll(List<MethodInterceptor> interceptors) {
if (this.interceptors == null) {
this.interceptors = Lists.newArrayList();
}
this.interceptors.addAll(interceptors);
}
boolean hasInterceptors() {
return interceptors != null;
}
}
/**
* A callback filter that maps methods to unique IDs. We define equals and
* hashCode without using any state related to the injector so that enhanced
* classes intercepting the same methods can be shared between injectors (and
* child injectors, etc).
*/
private static class IndicesCallbackFilter implements CallbackFilter {
final Map<Object, Integer> indices;
final int hashCode;
IndicesCallbackFilter(List<Method> methods) {
final Map<Object, Integer> indices = Maps.newHashMap();
for (int i = 0; i < methods.size(); i++) {
indices.put(MethodWrapper.create(methods.get(i)), i);
}
this.indices = indices;
this.hashCode = indices.hashCode();
}
public int accept(Method method) {
return indices.get(MethodWrapper.create(method));
}
@Override public boolean equals(Object o) {
return o instanceof IndicesCallbackFilter &&
((IndicesCallbackFilter) o).indices.equals(indices);
}
@Override public int hashCode() {
return hashCode;
}
}
/**
* Constructs instances that participate in AOP.
*/
private static class ProxyConstructor<T> implements ConstructionProxy {
final Class<?> enhanced;
final InjectionPoint injectionPoint;
final Constructor<T> constructor;
final Callback[] callbacks;
final int constructorIndex;
final ImmutableMap<Method, List methodInterceptors;
final FastClass fastClass;
@SuppressWarnings("unchecked") // the constructor promises to construct 'T's
ProxyConstructor(Enhancer enhancer, InjectionPoint injectionPoint, Callback[] callbacks,
ImmutableMap<Method, List methodInterceptors) {
this.enhanced = enhancer.createClass(); // this returns a cached class if possible
this.injectionPoint = injectionPoint;
this.constructor = (Constructor<T>) injectionPoint.getMember();
this.callbacks = callbacks;
this.methodInterceptors = methodInterceptors;
this.fastClass = newFastClassForMember(enhanced, constructor);
this.constructorIndex = fastClass.getIndex(constructor.getParameterTypes());
}
@SuppressWarnings("unchecked") // the constructor promises to produce 'T's
public T newInstance(Object... arguments) throws InvocationTargetException {
Enhancer.registerCallbacks(enhanced, callbacks);
try {
return (T) fastClass.newInstance(constructorIndex, arguments);
} finally {
Enhancer.registerCallbacks(enhanced, null);
}
}
public InjectionPoint getInjectionPoint() {
return injectionPoint;
}
public Constructor<T> getConstructor() {
return constructor;
}
public ImmutableMap<Method, List getMethodInterceptors() {
return methodInterceptors;
}
}
}
Other Java examples (source code examples)
Here is a short list of links related to this Java ProxyFactory.java source code file:
|