alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

Java example source code file (Initializer.java)

This example Java source code file (Initializer.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.

Java - Java tags/keywords

cycledetectinglock, errorsexception, illegalstateexception, initializable, injectablereference, injectablereferencestate, key, new, object, provisionlistenerstackcallback, ready, typeliteral, util, validated

The Initializer.java Java example source code

/**
 * Copyright (C) 2008 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.common.base.Preconditions.checkNotNull;

import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
import com.google.inject.Binding;
import com.google.inject.Key;
import com.google.inject.Stage;
import com.google.inject.TypeLiteral;
import com.google.inject.internal.CycleDetectingLock.CycleDetectingLockFactory;
import com.google.inject.spi.InjectionPoint;

import java.util.IdentityHashMap;
import java.util.List;
import java.util.Set;

/**
 * Manages and injects instances at injector-creation time. This is made more complicated by
 * instances that request other instances while they're being injected. We overcome this by using
 * {@link Initializable}, which attempts to perform injection before use.
 *
 * @author jessewilson@google.com (Jesse Wilson)
 */
final class Initializer {

  /** Is set to true once {@link #validateOustandingInjections} is called. */
  private volatile boolean validationStarted = false;

  /**
   * Allows us to detect circular dependencies. It's only used during injectable reference
   * initialization. After initialization direct access through volatile field is used.
   */
  private final CycleDetectingLockFactory<Class cycleDetectingLockFactory =
      new CycleDetectingLockFactory<Class();

  /**
   * Instances that need injection during injector creation to a source that registered them.
   * New references added before {@link #validateOustandingInjections}.
   * Cleared up in {@link #injectAll}.
   */
  private final List<InjectableReference pendingInjections = Lists.newArrayList();

  /**
   * Map that guarantees that no instance would get two references. New references added
   * before {@link #validateOustandingInjections}. Cleared up in
   * {@link #validateOustandingInjections}.
   */
  private final IdentityHashMap<Object, InjectableReference initializablesCache =
      Maps.newIdentityHashMap();

  /**
   * Registers an instance for member injection when that step is performed.
   *
   * @param instance an instance that optionally has members to be injected (each annotated with
   *      @Inject).
   * @param binding the binding that caused this initializable to be created, if it exists.
   * @param source the source location that this injection was requested
   */
  <T> Initializable requestInjection(InjectorImpl injector, T instance, Binding binding,
      Object source, Set<InjectionPoint> injectionPoints) {
    checkNotNull(source);
    Preconditions.checkState(!validationStarted,
        "Member injection could not be requested after validation is started");
    ProvisionListenerStackCallback<T> provisionCallback =
        binding == null ? null : injector.provisionListenerStore.get(binding);

    // short circuit if the object has no injections or listeners.
    if (instance == null || (injectionPoints.isEmpty()
        && !injector.membersInjectorStore.hasTypeListeners()
        && (provisionCallback == null || !provisionCallback.hasListeners()))) {
      return Initializables.of(instance);
    }

    if (initializablesCache.containsKey(instance)) {
      @SuppressWarnings("unchecked") // Map from T to InjectableReference<T>
      Initializable<T> cached = (Initializable) initializablesCache.get(instance);
      return cached;
    }

    InjectableReference<T> injectableReference = new InjectableReference(
        injector, instance, binding == null ? null : binding.getKey(), provisionCallback, source,
        cycleDetectingLockFactory.create(instance.getClass()));
    initializablesCache.put(instance, injectableReference);
    pendingInjections.add(injectableReference);
    return injectableReference;
  }

  /**
   * Prepares member injectors for all injected instances. This prompts Guice to do static analysis
   * on the injected instances.
   */
  void validateOustandingInjections(Errors errors) {
    validationStarted = true;
    initializablesCache.clear();
    for (InjectableReference<?> reference : pendingInjections) {
      try {
        reference.validate(errors);
      } catch (ErrorsException e) {
        errors.merge(e.getErrors());
      }
    }
  }

  /**
   * Performs creation-time injections on all objects that require it. Whenever fulfilling an
   * injection depends on another object that requires injection, we inject it first. If the two
   * instances are codependent (directly or transitively), ordering of injection is arbitrary.
   */
  void injectAll(final Errors errors) {
    Preconditions.checkState(validationStarted, "Validation should be done before injection");
    for (InjectableReference<?> reference : pendingInjections) {
      try {
        reference.get(errors);
      } catch (ErrorsException e) {
        errors.merge(e.getErrors());
      }
    }
    pendingInjections.clear();
  }

  private enum InjectableReferenceState { NEW, VALIDATED, INJECTING, READY }

  private static class InjectableReference<T> implements Initializable {
    private volatile InjectableReferenceState state = InjectableReferenceState.NEW;
    private volatile MembersInjectorImpl<T> membersInjector = null;

    private final InjectorImpl injector;
    private final T instance;
    private final Object source;
    private final Key<T> key;
    private final ProvisionListenerStackCallback<T> provisionCallback;
    private final CycleDetectingLock<?> lock;

    public InjectableReference(InjectorImpl injector, T instance, Key<T> key,
        ProvisionListenerStackCallback<T> provisionCallback, Object source,
        CycleDetectingLock<?> lock) {
      this.injector = injector;
      this.key = key; // possibly null!
      this.provisionCallback = provisionCallback; // possibly null!
      this.instance = checkNotNull(instance, "instance");
      this.source = checkNotNull(source, "source");
      this.lock = checkNotNull(lock, "lock");
    }

    public void validate(Errors errors) throws ErrorsException {
      @SuppressWarnings("unchecked") // the type of 'T' is a TypeLiteral<T>
      TypeLiteral<T> type = TypeLiteral.get((Class) instance.getClass());
      membersInjector = injector.membersInjectorStore.get(type, errors.withSource(source));
      Preconditions.checkNotNull(membersInjector,
          "No membersInjector available for instance: %s, from key: %s", instance, key);
      state = InjectableReferenceState.VALIDATED;
    }

    /**
     * Reentrant. If {@code instance} was registered for injection at injector-creation time, this
     * method will ensure that all its members have been injected before returning.
     */
    public T get(Errors errors) throws ErrorsException {
      // skipping acquiring lock if initialization is already finished
      if (state == InjectableReferenceState.READY) {
        return instance;
      }

      // acquire lock for current binding to initialize an instance
      Multimap<?, ?> lockCycle = lock.lockOrDetectPotentialLocksCycle();
      if (!lockCycle.isEmpty()) {
        // Potential deadlock detected and creation lock is not taken.
        // According to injectAll()'s contract return non-initialized instance.

        // This condition should not be possible under the current Guice implementation.
        // This clause exists for defensive programming purposes.

        // Reasoning:
        // get() is called either directly from injectAll(), holds no locks and can not create
        // a cycle, or it is called through a singleton scope, which resolves deadlocks by itself.
        // Before calling get() object has to be requested for injection.
        // Initializer.requestInjection() is called either for constant object bindings, which wrap
        // creation into a Singleton scope, or from Binder.requestInjection(), which
        // has to use Singleton scope to reuse the same InjectableReference to potentially
        // create a lock cycle.
        return instance;
      }
      try {
        // lock acquired, current thread owns this instance initialization
        switch (state) {
          case READY:
            return instance;
          // When instance depends on itself in the same thread potential dead lock
          // is not detected. We have to prevent a stack overflow and we use
          // an "injecting" stage to short-circuit a call.
          case INJECTING:
            return instance;
          case VALIDATED:
            state = InjectableReferenceState.INJECTING;
            break;
          case NEW:
            throw new IllegalStateException("InjectableReference is not validated yet");
          default:
            throw new IllegalStateException("Unknown state: " + state);
        }

        // if in Stage.TOOL, we only want to inject & notify toolable injection points.
        // (otherwise we'll inject all of them)
        membersInjector.injectAndNotify(instance,
            errors.withSource(source),
            key,
            provisionCallback,
            source,
            injector.options.stage == Stage.TOOL);

        // mark instance as ready to skip a lock on subsequent calls
        state = InjectableReferenceState.READY;
        return instance;
      } finally {
        // always release our creation lock, even on failures
        lock.unlock();
      }
    }

    @Override public String toString() {
      return instance.toString();
    }
  }
}

Other Java examples (source code examples)

Here is a short list of links related to this Java Initializer.java source code file:

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.