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 }
|