This project has retired. For details please refer to its Attic page.
Source code
001/**
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *   http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied.  See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 */
019package org.apache.reef.tang;
020
021import org.apache.reef.tang.annotations.Name;
022import org.apache.reef.tang.exceptions.InjectionException;
023import org.apache.reef.tang.implementation.java.InjectorImpl;
024
025import java.util.concurrent.Future;
026import java.util.concurrent.TimeUnit;
027
028/**
029 * A future-based mechanism for cyclic object injections. Since Tang is a
030 * constructor-based dependency injector, there is no way to directly create
031 * cycles of objects.
032 * <p/>
033 * In situations where you need to have two objects that point at each other, you
034 * can use an InjectionFuture to break the cycle. Simply ask Tang to inject an
035 * InjectionFuture<T> into your constructor.  Later (after your constructor
036 * returns) invoke the get() method of the injection future to get a reference
037 * to an injected object of type T.
038 * <p/>
039 * Note that InjectorFutures and singletons interact in subtle ways.
040 * <p/>
041 * Normally, Tang never reuses a reference to an injected object unless the
042 * object is a singleton or a volatile instance. If InjectionFutures followed
043 * this convention, then a cyclic injection of two non-singleton classes would
044 * result in an an infinite chain of objects of the two types. Tang addresses
045 * this issue as follows:
046 * <p/>
047 * 1) In the first pass, it injects a complete object tree, making note of
048 * InjectionFuture objects that will need to be populated later. The injection
049 * of this tree respects standard Tang singleton and volatile semantics.
050 * <p/>
051 * 2) In a second pass, Tang populates each of the InjectionFutures with the
052 * reference to the requested class that was instantiated in the first pass. If
053 * this reference does not exist (or is non-unique) then an InjectionException
054 * is thrown.
055 * <p/>
056 * Note: The semantics of complex cyclic injections may change over time.
057 * <p/>
058 * We haven't seen many complicated injections that involve cycles in practice.
059 * A second approach would be to establish some scoping rules, so that each
060 * InjectionFuture binds to the innermost matching parent in the InjectionPlan.
061 * This would allow plans to inject multiple cycles involving distinct objects
062 * of the same type.
063 *
064 * @param <T>
065 */
066
067public final class InjectionFuture<T> implements Future<T> {
068
069  protected final InjectorImpl injector;
070
071  private final Class<? extends T> iface;
072
073  private final T instance;
074
075  public InjectionFuture() {
076    injector = null;
077    iface = null;
078    instance = null;
079  }
080
081  public InjectionFuture(final Injector injector, Class<? extends T> iface) {
082    this.injector = (InjectorImpl) injector;
083    this.iface = iface;
084    this.instance = null;
085  }
086
087  public InjectionFuture(T instance) {
088    this.injector = null;
089    this.iface = null;
090    this.instance = instance;
091  }
092
093  @Override
094  public final boolean cancel(boolean mayInterruptIfRunning) {
095    return false;
096  }
097
098  @Override
099  public final boolean isCancelled() {
100    return false;
101  }
102
103  @Override
104  public final boolean isDone() {
105    return true;
106  }
107
108  @SuppressWarnings("unchecked")
109  @Override
110  public T get() {
111    if (instance != null) return instance;
112    try {
113      synchronized (injector) {
114        final T t;
115        if (Name.class.isAssignableFrom(iface)) {
116          t = injector.getNamedInstance((Class<Name<T>>) iface);
117        } else {
118          t = injector.getInstance(iface);
119        }
120        Aspect a = injector.getAspect();
121        if (a != null) {
122          a.injectionFutureInstantiated(this, t);
123        }
124        return t;
125      }
126    } catch (InjectionException e) {
127      throw new RuntimeException(e);
128    }
129  }
130
131  @Override
132  public T get(long timeout, TimeUnit unit) {
133    throw new UnsupportedOperationException();
134  }
135
136}