GuiceInjectorFactory.java
001 /*
002  * Copyright 2008-2016 the original author or authors.
003  *
004  * Licensed under the Apache License, Version 2.0 (the "License");
005  * you may not use this file except in compliance with the License.
006  * You may obtain a copy of the License at
007  *
008  *     http://www.apache.org/licenses/LICENSE-2.0
009  *
010  * Unless required by applicable law or agreed to in writing, software
011  * distributed under the License is distributed on an "AS IS" BASIS,
012  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013  * See the License for the specific language governing permissions and
014  * limitations under the License.
015  */
016 package org.codehaus.griffon.runtime.injection;
017 
018 import com.google.inject.AbstractModule;
019 import com.google.inject.Guice;
020 import com.google.inject.Module;
021 import com.google.inject.TypeLiteral;
022 import com.google.inject.matcher.AbstractMatcher;
023 import com.google.inject.matcher.Matchers;
024 import com.google.inject.spi.InjectionListener;
025 import com.google.inject.spi.ProvisionListener;
026 import com.google.inject.spi.TypeEncounter;
027 import com.google.inject.spi.TypeListener;
028 import griffon.core.ApplicationEvent;
029 import griffon.core.GriffonApplication;
030 import griffon.core.artifact.GriffonArtifact;
031 import griffon.core.injection.Binding;
032 import griffon.core.injection.Injector;
033 import griffon.core.injection.InjectorFactory;
034 import org.codehaus.griffon.runtime.core.injection.InjectorProvider;
035 import org.kordamp.jipsy.ServiceProviderFor;
036 import org.slf4j.Logger;
037 import org.slf4j.LoggerFactory;
038 
039 import javax.annotation.Nonnull;
040 import javax.annotation.PostConstruct;
041 import javax.inject.Singleton;
042 import java.util.ArrayList;
043 import java.util.Collection;
044 import java.util.List;
045 import java.util.Map;
046 import java.util.ServiceLoader;
047 
048 import static com.google.inject.util.Providers.guicify;
049 import static griffon.util.AnnotationUtils.sortByDependencies;
050 import static java.util.Arrays.asList;
051 import static java.util.Objects.requireNonNull;
052 import static org.codehaus.griffon.runtime.injection.GuiceInjector.moduleFromBindings;
053 import static org.codehaus.griffon.runtime.injection.MethodUtils.invokeAnnotatedMethod;
054 
055 /**
056  @author Andres Almiray
057  @since 2.0.0
058  */
059 @ServiceProviderFor(InjectorFactory.class)
060 public class GuiceInjectorFactory implements InjectorFactory {
061     private static final Logger LOG = LoggerFactory.getLogger(GuiceInjectorFactory.class);
062 
063     @Nonnull
064     @Override
065     public GuiceInjector createInjector(@Nonnull GriffonApplication application, @Nonnull Iterable<Binding<?>> bindings) {
066         requireNonNull(application, "Argument 'application' must not be null");
067         requireNonNull(bindings, "Argument 'bindings' must not be null");
068         InjectorProvider injectorProvider = new InjectorProvider();
069         GuiceInjector injector = createModules(application, injectorProvider, bindings);
070         injectorProvider.setInjector(injector);
071         return injector;
072     }
073 
074     private GuiceInjector createModules(final @Nonnull GriffonApplication application, @Nonnull final InjectorProvider injectorProvider, @Nonnull Iterable<Binding<?>> bindings) {
075         final InjectionListener<GriffonArtifact> injectionListener = new InjectionListener<GriffonArtifact>() {
076             @Override
077             public void afterInjection(GriffonArtifact injectee) {
078                 application.getEventRouter().publishEvent(
079                     ApplicationEvent.NEW_INSTANCE.getName(),
080                     asList(injectee.getClass(), injectee)
081                 );
082             }
083         };
084 
085         final InjectionListener<Object> postConstructorInjectorListener = new InjectionListener<Object>() {
086             @SuppressWarnings("ThrowableResultOfMethodCallIgnored")
087             @Override
088             public void afterInjection(Object injectee) {
089                 invokeAnnotatedMethod(injectee, PostConstruct.class);
090             }
091         };
092 
093 
094         final InstanceTracker instanceTracker = new InstanceTracker();
095         Module injectorModule = new AbstractModule() {
096             @Override
097             protected void configure() {
098                 bind(Injector.class)
099                     .toProvider(guicify(injectorProvider))
100                     .in(Singleton.class);
101 
102                 bindListener(new AbstractMatcher<TypeLiteral<?>>() {
103                                  public boolean matches(TypeLiteral<?> typeLiteral) {
104                                      return GriffonArtifact.class.isAssignableFrom(typeLiteral.getRawType());
105                                  }
106                              }new TypeListener() {
107                                  @SuppressWarnings("unchecked")
108                                  @Override
109                                  public <I> void hear(TypeLiteral<I> type, TypeEncounter<I> encounter) {
110                                      if (GriffonArtifact.class.isAssignableFrom(type.getRawType())) {
111                                          TypeEncounter<GriffonArtifact> artifactEncounter = (TypeEncounter<GriffonArtifact>encounter;
112                                          artifactEncounter.register(injectionListener);
113                                      }
114                                  }
115                              }
116                 );
117 
118                 bindListener(Matchers.any()new TypeListener() {
119                     @Override
120                     public <I> void hear(TypeLiteral<I> type, TypeEncounter<I> encounter) {
121                         encounter.register(postConstructorInjectorListener);
122                     }
123                 });
124 
125                 bindListener(Matchers.any()new ProvisionListener() {
126                     @Override
127                     public <T> void onProvision(ProvisionInvocation<T> provision) {
128                         instanceTracker.track(provision.getBinding(), provision.provision());
129                     }
130                 });
131             }
132         };
133 
134         Collection<Module> modules = new ArrayList<>();
135         modules.add(injectorModule);
136         modules.add(moduleFromBindings(bindings));
137 
138         List<Module> loadedModules = new ArrayList<>();
139         ServiceLoader<Module> moduleLoader = ServiceLoader.load(Module.class, getClass().getClassLoader());
140         for (Module module : moduleLoader) {
141             LOG.trace("Adding module {}", module);
142             loadedModules.add(module);
143         }
144         Map<String, Module> sortedModules = sortByDependencies(loadedModules, "Module""module");
145         modules.addAll(sortedModules.values());
146 
147         com.google.inject.Injector injector = Guice.createInjector(modules);
148         instanceTracker.setInjector(injector);
149         return new GuiceInjector(instanceTracker);
150     }
151 }