001: /**
002: * Copyright (C) 2006 Google Inc.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */package com.google.inject;
016:
017: import java.lang.reflect.InvocationHandler;
018: import java.lang.reflect.InvocationTargetException;
019: import java.lang.reflect.Method;
020: import java.lang.reflect.Proxy;
021: import java.util.ArrayList;
022: import java.util.List;
023:
024: /**
025: * Context of a dependency construction. Used to manage circular references.
026: *
027: * @author crazybob@google.com (Bob Lee)
028: */
029: class ConstructionContext<T> {
030:
031: T currentReference;
032: boolean constructing;
033:
034: List<DelegatingInvocationHandler<T>> invocationHandlers;
035:
036: T getCurrentReference() {
037: return currentReference;
038: }
039:
040: void removeCurrentReference() {
041: this .currentReference = null;
042: }
043:
044: void setCurrentReference(T currentReference) {
045: this .currentReference = currentReference;
046: }
047:
048: boolean isConstructing() {
049: return constructing;
050: }
051:
052: void startConstruction() {
053: this .constructing = true;
054: }
055:
056: void finishConstruction() {
057: this .constructing = false;
058: invocationHandlers = null;
059: }
060:
061: Object createProxy(Class<?> expectedType) {
062: // TODO: if I create a proxy which implements all the interfaces of
063: // the implementation type, I'll be able to get away with one proxy
064: // instance (as opposed to one per caller).
065:
066: if (!expectedType.isInterface()) {
067: // TODO: Report better error.
068: throw new ConfigurationException("Tried proxying "
069: + expectedType.getName()
070: + " to support a circular dependency, but"
071: + " it is not an interface.");
072: }
073:
074: if (invocationHandlers == null) {
075: invocationHandlers = new ArrayList<DelegatingInvocationHandler<T>>();
076: }
077:
078: DelegatingInvocationHandler<T> invocationHandler = new DelegatingInvocationHandler<T>();
079: invocationHandlers.add(invocationHandler);
080:
081: Object object = Proxy.newProxyInstance(expectedType
082: .getClassLoader(), new Class[] { expectedType },
083: invocationHandler);
084: return expectedType.cast(object);
085: }
086:
087: void setProxyDelegates(T delegate) {
088: if (invocationHandlers != null) {
089: for (DelegatingInvocationHandler<T> handler : invocationHandlers) {
090: handler.setDelegate(delegate);
091: }
092: }
093: }
094:
095: static class DelegatingInvocationHandler<T> implements
096: InvocationHandler {
097:
098: T delegate;
099:
100: public Object invoke(Object proxy, Method method, Object[] args)
101: throws Throwable {
102: if (delegate == null) {
103: throw new IllegalStateException(
104: "This is a proxy used to support"
105: + " circular references involving constructors. The object we're"
106: + " proxying is not constructed yet. Please wait until after"
107: + " injection has completed to use this object.");
108: }
109:
110: try {
111: // This appears to be not test-covered
112: return method.invoke(delegate, args);
113: } catch (IllegalAccessException e) {
114: throw new RuntimeException(e);
115: } catch (IllegalArgumentException e) {
116: throw new RuntimeException(e);
117: } catch (InvocationTargetException e) {
118: throw e.getTargetException();
119: }
120: }
121:
122: void setDelegate(T delegate) {
123: this.delegate = delegate;
124: }
125: }
126: }
|