GuiceInjectorFactory.java
001 /*
002  * Copyright 2008-2015 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.TypeEncounter;
026 import com.google.inject.spi.TypeListener;
027 import griffon.core.ApplicationEvent;
028 import griffon.core.GriffonApplication;
029 import griffon.core.artifact.GriffonArtifact;
030 import griffon.core.injection.Binding;
031 import griffon.core.injection.Injector;
032 import griffon.core.injection.InjectorFactory;
033 import org.codehaus.griffon.runtime.core.injection.InjectorProvider;
034 import org.kordamp.jipsy.ServiceProviderFor;
035 import org.slf4j.Logger;
036 import org.slf4j.LoggerFactory;
037 
038 import javax.annotation.Nonnull;
039 import javax.annotation.PostConstruct;
040 import javax.inject.Singleton;
041 import java.util.ArrayList;
042 import java.util.Collection;
043 import java.util.List;
044 import java.util.Map;
045 import java.util.ServiceLoader;
046 
047 import static com.google.inject.util.Providers.guicify;
048 import static griffon.util.AnnotationUtils.sortByDependencies;
049 import static java.util.Arrays.asList;
050 import static java.util.Objects.requireNonNull;
051 import static org.codehaus.griffon.runtime.injection.GuiceInjector.moduleFromBindings;
052 import static org.codehaus.griffon.runtime.injection.MethodUtils.invokeAnnotatedMethod;
053 
054 /**
055  @author Andres Almiray
056  @since 2.0.0
057  */
058 @ServiceProviderFor(InjectorFactory.class)
059 public class GuiceInjectorFactory implements InjectorFactory {
060     private static final Logger LOG = LoggerFactory.getLogger(GuiceInjectorFactory.class);
061 
062     @Nonnull
063     @Override
064     public GuiceInjector createInjector(@Nonnull GriffonApplication application, @Nonnull Iterable<Binding<?>> bindings) {
065         requireNonNull(application, "Argument 'application' must not be null");
066         requireNonNull(bindings, "Argument 'bindings' must not be null");
067         InjectorProvider injectorProvider = new InjectorProvider();
068         GuiceInjector injector = createModules(application, injectorProvider, bindings);
069         injectorProvider.setInjector(injector);
070         return injector;
071     }
072 
073     private GuiceInjector createModules(final @Nonnull GriffonApplication application, @Nonnull final InjectorProvider injectorProvider, @Nonnull Iterable<Binding<?>> bindings) {
074         final InjectionListener<GriffonArtifact> injectionListener = new InjectionListener<GriffonArtifact>() {
075             @Override
076             public void afterInjection(GriffonArtifact injectee) {
077                 application.getEventRouter().publishEvent(
078                     ApplicationEvent.NEW_INSTANCE.getName(),
079                     asList(injectee.getClass(), injectee)
080                 );
081             }
082         };
083 
084         final InjectionListener<Object> postConstructorInjectorListener = new InjectionListener<Object>() {
085             @SuppressWarnings("ThrowableResultOfMethodCallIgnored")
086             @Override
087             public void afterInjection(Object injectee) {
088                 invokeAnnotatedMethod(injectee, PostConstruct.class);
089             }
090         };
091 
092         Module injectorModule = new AbstractModule() {
093             @Override
094             protected void configure() {
095                 bind(Injector.class)
096                     .toProvider(guicify(injectorProvider))
097                     .in(Singleton.class);
098 
099                 bindListener(new AbstractMatcher<TypeLiteral<?>>() {
100                                  public boolean matches(TypeLiteral<?> typeLiteral) {
101                                      return GriffonArtifact.class.isAssignableFrom(typeLiteral.getRawType());
102                                  }
103                              }new TypeListener() {
104                                  @SuppressWarnings("unchecked")
105                                  @Override
106                                  public <I> void hear(TypeLiteral<I> type, TypeEncounter<I> encounter) {
107                                      if (GriffonArtifact.class.isAssignableFrom(type.getRawType())) {
108                                          TypeEncounter<GriffonArtifact> artifactEncounter = (TypeEncounter<GriffonArtifact>encounter;
109                                          artifactEncounter.register(injectionListener);
110                                      }
111                                  }
112                              }
113                 );
114 
115                 bindListener(Matchers.any()new TypeListener() {
116                     @Override
117                     public <I> void hear(TypeLiteral<I> type, TypeEncounter<I> encounter) {
118                         encounter.register(postConstructorInjectorListener);
119                     }
120                 });
121             }
122         };
123 
124         Collection<Module> modules = new ArrayList<>();
125         modules.add(injectorModule);
126         modules.add(moduleFromBindings(bindings));
127 
128         List<Module> loadedModules = new ArrayList<>();
129         ServiceLoader<Module> moduleLoader = ServiceLoader.load(Module.class, getClass().getClassLoader());
130         for (Module module : moduleLoader) {
131             LOG.trace("Adding module {}", module);
132             loadedModules.add(module);
133         }
134         Map<String, Module> sortedModules = sortByDependencies(loadedModules, "Module""module");
135         modules.addAll(sortedModules.values());
136 
137         com.google.inject.Injector injector = Guice.createInjector(modules);
138         return new GuiceInjector(injector);
139     }
140 }