/* * Copyright (C) 2015 Red Hat, Inc. and/or its affiliates. * * 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 org.jboss.errai.ioc.client.container; import java.lang.annotation.Annotation; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.IdentityHashMap; import java.util.Map; import java.util.Set; import javax.enterprise.context.Dependent; import org.jboss.errai.ioc.client.api.ContextualTypeProvider; import com.google.common.base.Supplier; import com.google.common.collect.Multimaps; import com.google.common.collect.SetMultimap; /** * Creates proxies and wires dependencies for a bean. The abstract methods in * this class are implemented by code generation. Because of this, care should * be taken when modifying the names of fields or parameters in this type. * * @param <T> * The type of the bean that this factory creates. * * @author Max Barkley <mbarkley@redhat.com> */ public abstract class Factory<T> { /* * Do not remove! Used in generated code. */ protected final Factory<T> thisInstance = this; /* * Do not remove! Used in generated code. */ protected final FactoryHandleImpl handle; private final Map<T, Map<String, Object>> referenceMaps = new IdentityHashMap<>(); private final SetMultimap<T, Object> dependentScopedDependencies = Multimaps .newSetMultimap(new IdentityHashMap<T, Collection<Object>>(), new Supplier<Set<Object>>() { @Override public Set<Object> get() { return Collections.newSetFromMap(new IdentityHashMap<Object, Boolean>()); } }); private T incompleteInstance; protected Factory() { this.handle = null; } protected Factory(final FactoryHandleImpl handle) { this.handle = handle; } /** * At runtime the init method is called once after all factories and * {@link Context contexts} have been registered. This allows decorators an * opportunity to register a bean type for services that may lookup instances * on demand. * * @param context * Some decorators may wish to register callbacks that create an * instance of the bean. This should be done using * {@link Context#getInstance(String)}. */ public void init(final Context context) {} /** * This method is invoked whenever an actual instance of a bean must be * constructed. If a bean is proxied, this will likely happen on the first * invocation of a method on the proxy. Otherwise this will occur when the * bean is injected into another type. * * This method always returns an unproxied, fully wired instance of a type. * * @param contextManager * For requesting instances from other factories. Never {@code null}. * @return A fully wired, unproxied instance of a bean. */ public T createInstance(final ContextManager contextManager) { throw new UnsupportedOperationException("The factory, " + getClass().getSimpleName() + ", only supports contextual instances."); } /** * Like {@link #createInstance(ContextManager)} but with contextual paramters for factories backed by a * {@link ContextualTypeProvider}. */ public T createContextualInstance(final ContextManager contextManager, final Class<?>[] typeArgs, final Annotation[] qualifiers) { throw new UnsupportedOperationException("The factory, " + getClass().getSimpleName() + ", does not support contextual instances."); } public void invokePostConstructs(final T instance) {} public void setReference(final T instance, final String referenceName, final Object ref) { final Map<String, Object> instanceRefMap = getInstanceRefMap(instance); instanceRefMap.put(referenceName, ref); } private Map<String, Object> getInstanceRefMap(final T instance) { Map<String, Object> map = referenceMaps.get(maybeUnwrapProxy(instance)); if (map == null) { map = new HashMap<>(); referenceMaps.put(instance, map); } return map; } @SuppressWarnings("unchecked") public <P> P getReferenceAs(final T instance, final String referenceName, final Class<P> type) { return (P) getInstanceRefMap(maybeUnwrapProxy(instance)).get(referenceName); } public Proxy<T> createProxy(final Context context) { return null; } public FactoryHandle getHandle() { return handle; } protected <D> D registerDependentScopedReference(final T instance, final D dependentScopedBeanRef) { dependentScopedDependencies.put(maybeUnwrapProxy(instance), dependentScopedBeanRef); return dependentScopedBeanRef; } /** * This method performs any cleanup required for destroying a type. It will * invoke generated statements from decorators, invoke disposers or predestroy * methods, and destroy and {@link Dependent} scoped dependencies. * * @param instance The instance being destroyed. * @param contextManager For destroying dependencies. */ @SuppressWarnings("unchecked") public void destroyInstance(final Object instance, final ContextManager contextManager) { final Object unwrapped = maybeUnwrapProxy(instance); generatedDestroyInstance(unwrapped, contextManager); referenceMaps.remove(unwrapped); for (final Object depRef : dependentScopedDependencies.get((T) unwrapped)) { contextManager.destroy(depRef); } dependentScopedDependencies.removeAll(instance); } protected void generatedDestroyInstance(final Object instance, final ContextManager contextManager) {} @SuppressWarnings("unchecked") public static <P> P maybeUnwrapProxy(final P instance) { if (instance instanceof Proxy) { return (P) ((Proxy<P>) instance).unwrap(); } else { return instance; } } public T getIncompleteInstance() { return incompleteInstance; } protected void setIncompleteInstance(final T instance) { incompleteInstance = instance; } }