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 * {@code 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
065public final class InjectionFuture<T> implements Future<T> {
066
067  protected final InjectorImpl injector;
068
069  private final Class<? extends T> iface;
070
071  private final T instance;
072
073  public InjectionFuture() {
074    injector = null;
075    iface = null;
076    instance = null;
077  }
078
079  public InjectionFuture(final Injector injector, final Class<? extends T> iface) {
080    this.injector = (InjectorImpl) injector;
081    this.iface = iface;
082    this.instance = null;
083  }
084
085  public InjectionFuture(final T instance) {
086    this.injector = null;
087    this.iface = null;
088    this.instance = instance;
089  }
090
091  @Override
092  public boolean cancel(final boolean mayInterruptIfRunning) {
093    return false;
094  }
095
096  @Override
097  public boolean isCancelled() {
098    return false;
099  }
100
101  @Override
102  public boolean isDone() {
103    return true;
104  }
105
106  @SuppressWarnings("unchecked")
107  @Override
108  public T get() {
109    if (instance != null) {
110      return instance;
111    }
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        final Aspect a = injector.getAspect();
121        if (a != null) {
122          a.injectionFutureInstantiated(this, t);
123        }
124        return t;
125      }
126    } catch (final InjectionException e) {
127      throw new RuntimeException(e);
128    }
129  }
130
131  @Override
132  public T get(final long timeout, final TimeUnit unit) {
133    throw new UnsupportedOperationException();
134  }
135
136}