Home | History | Annotate | Download | only in internal
      1 /**
      2  * Copyright (C) 2006 Google Inc.
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  * http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.google.inject.internal;
     18 
     19 import com.google.inject.internal.InjectorImpl.InjectorOptions;
     20 
     21 import java.lang.reflect.Proxy;
     22 import java.util.ArrayList;
     23 import java.util.List;
     24 
     25 /**
     26  * Context of a dependency construction. Used to manage circular references.
     27  *
     28  * @author crazybob (at) google.com (Bob Lee)
     29  */
     30 final class ConstructionContext<T> {
     31 
     32   T currentReference;
     33   boolean constructing;
     34 
     35   List<DelegatingInvocationHandler<T>> invocationHandlers;
     36 
     37   public T getCurrentReference() {
     38     return currentReference;
     39   }
     40 
     41   public void removeCurrentReference() {
     42     this.currentReference = null;
     43   }
     44 
     45   public void setCurrentReference(T currentReference) {
     46     this.currentReference = currentReference;
     47   }
     48 
     49   public boolean isConstructing() {
     50     return constructing;
     51   }
     52 
     53   public void startConstruction() {
     54     this.constructing = true;
     55   }
     56 
     57   public void finishConstruction() {
     58     this.constructing = false;
     59     invocationHandlers = null;
     60   }
     61 
     62   public Object createProxy(Errors errors, InjectorOptions injectorOptions,
     63       Class<?> expectedType) throws ErrorsException {
     64     if (injectorOptions.disableCircularProxies) {
     65       throw errors.circularProxiesDisabled(expectedType).toException();
     66     }
     67     if (!expectedType.isInterface()) {
     68       throw errors.cannotSatisfyCircularDependency(expectedType).toException();
     69     }
     70 
     71     if (invocationHandlers == null) {
     72       invocationHandlers = new ArrayList<DelegatingInvocationHandler<T>>();
     73     }
     74 
     75     DelegatingInvocationHandler<T> invocationHandler = new DelegatingInvocationHandler<T>();
     76     invocationHandlers.add(invocationHandler);
     77 
     78     // TODO: if I create a proxy which implements all the interfaces of
     79     // the implementation type, I'll be able to get away with one proxy
     80     // instance (as opposed to one per caller).
     81     ClassLoader classLoader = BytecodeGen.getClassLoader(expectedType);
     82     return expectedType.cast(Proxy.newProxyInstance(classLoader,
     83         new Class[] { expectedType, CircularDependencyProxy.class }, invocationHandler));
     84   }
     85 
     86   public void setProxyDelegates(T delegate) {
     87     if (invocationHandlers != null) {
     88       for (DelegatingInvocationHandler<T> handler : invocationHandlers) {
     89         handler.setDelegate(delegate);
     90       }
     91       // initialization of each handler can happen no more than once
     92       invocationHandlers = null;
     93     }
     94   }
     95 }
     96