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