GuiceInjector.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.Key;
020 import com.google.inject.Module;
021 import com.google.inject.TypeLiteral;
022 import com.google.inject.binder.AnnotatedBindingBuilder;
023 import com.google.inject.binder.LinkedBindingBuilder;
024 import com.google.inject.binder.ScopedBindingBuilder;
025 import griffon.core.injection.Binding;
026 import griffon.core.injection.Injector;
027 import griffon.core.injection.InstanceBinding;
028 import griffon.core.injection.ProviderBinding;
029 import griffon.core.injection.ProviderTypeBinding;
030 import griffon.core.injection.Qualified;
031 import griffon.core.injection.TargetBinding;
032 import griffon.exceptions.ClosedInjectorException;
033 import griffon.exceptions.InstanceNotFoundException;
034 import griffon.exceptions.MembersInjectionException;
035 import griffon.util.AnnotationUtils;
036 
037 import javax.annotation.Nonnull;
038 import javax.annotation.Nullable;
039 import javax.annotation.concurrent.GuardedBy;
040 import javax.inject.Singleton;
041 import java.lang.annotation.Annotation;
042 import java.util.ArrayList;
043 import java.util.Collection;
044 import java.util.List;
045 
046 import static com.google.inject.util.Providers.guicify;
047 import static java.util.Objects.requireNonNull;
048 
049 /**
050  @author Andres Almiray
051  @since 2.0.0
052  */
053 public class GuiceInjector implements Injector<com.google.inject.Injector> {
054     private static final String ERROR_TYPE_NULL = "Argument 'type' must not be null";
055     private static final String ERROR_DELEGATE_NULL = "Argument 'delegate' must not be null";
056     private static final String ERROR_INSTANCE_TRACKER_NULL = "Argument 'instanceTracker' must not be null";
057     private static final String ERROR_QUALIFIER_NULL = "Argument 'qualifier' must not be null";
058     private static final String ERROR_INSTANCE_NULL = "Argument 'instance' must not be null";
059 
060     private final InstanceTracker instanceTracker;
061     private final com.google.inject.Injector delegate;
062     private final Object lock = new Object[0];
063     @GuardedBy("lock")
064     private boolean closed;
065 
066     public GuiceInjector(@Nonnull InstanceTracker instanceTracker) {
067         this.instanceTracker = requireNonNull(instanceTracker, ERROR_INSTANCE_TRACKER_NULL);
068         this.delegate = requireNonNull(instanceTracker.getInjector(), ERROR_DELEGATE_NULL);
069     }
070 
071     static Module moduleFromBindings(final @Nonnull Iterable<Binding<?>> bindings) {
072         return new AbstractModule() {
073             @Override
074             protected void configure() {
075                 for (Binding<?> binding : bindings) {
076                     if (binding instanceof TargetBinding) {
077                         handleTargetBinding((TargetBindingbinding);
078                     else if (binding instanceof InstanceBinding) {
079                         handleInstanceBinding((InstanceBindingbinding);
080                     else if (binding instanceof ProviderTypeBinding) {
081                         handleProviderTypeBinding((ProviderTypeBindingbinding);
082                     else if (binding instanceof ProviderBinding) {
083                         handleProviderBinding((ProviderBindingbinding);
084                     else {
085                         throw new IllegalArgumentException("Don't know how to handle " + binding);
086                     }
087                 }
088             }
089 
090             @Nonnull
091             private LinkedBindingBuilder handleBinding(@Nonnull Binding<?> binding) {
092                 AnnotatedBindingBuilder<?> builder = bind(binding.getSource());
093                 if (binding.getClassifier() != null) {
094                     return builder.annotatedWith(binding.getClassifier());
095                 else if (binding.getClassifierType() != null) {
096                     return builder.annotatedWith(binding.getClassifierType());
097                 }
098                 return builder;
099             }
100 
101             @SuppressWarnings("unchecked")
102             private void handleTargetBinding(@Nonnull TargetBinding<?> binding) {
103                 LinkedBindingBuilder lbuilder = handleBinding(binding);
104                 if (binding.getSource() != binding.getTarget()) {
105                     ScopedBindingBuilder sbuilder = lbuilder.to(binding.getTarget());
106                     if (binding.isSingleton()) {
107                         sbuilder.in(Singleton.class);
108                     }
109                 else if (binding.isSingleton()) {
110                     lbuilder.in(Singleton.class);
111                 }
112             }
113 
114             @SuppressWarnings("unchecked")
115             private void handleInstanceBinding(@Nonnull InstanceBinding<?> binding) {
116                 handleBinding(binding).toInstance(binding.getInstance());
117             }
118 
119             @SuppressWarnings("unchecked")
120             private void handleProviderTypeBinding(@Nonnull ProviderTypeBinding<?> binding) {
121                 ScopedBindingBuilder builder = handleBinding(binding).toProvider(binding.getProviderType());
122                 if (binding.isSingleton()) {
123                     builder.in(Singleton.class);
124                 }
125             }
126 
127             @SuppressWarnings("unchecked")
128             private void handleProviderBinding(@Nonnull ProviderBinding<?> binding) {
129                 ScopedBindingBuilder builder = handleBinding(binding).toProvider(guicify(binding.getProvider()));
130                 if (binding.isSingleton()) {
131                     builder.in(Singleton.class);
132                 }
133             }
134         };
135     }
136 
137     @Nonnull
138     @Override
139     public <T> T getInstance(@Nonnull Class<T> typethrows InstanceNotFoundException {
140         requireNonNull(type, ERROR_TYPE_NULL);
141 
142         if (isClosed()) {
143             throw new InstanceNotFoundException(type, new ClosedInjectorException(this));
144         }
145 
146         try {
147             return delegate.getInstance(type);
148         catch (RuntimeException e) {
149             throw new InstanceNotFoundException(type, e);
150         }
151     }
152 
153     @Nonnull
154     @Override
155     public <T> T getInstance(@Nonnull Class<T> type, @Nonnull Annotation qualifierthrows InstanceNotFoundException {
156         requireNonNull(type, ERROR_TYPE_NULL);
157         requireNonNull(qualifier, ERROR_QUALIFIER_NULL);
158 
159         if (isClosed()) {
160             throw new InstanceNotFoundException(type, qualifier, new ClosedInjectorException(this));
161         }
162 
163         try {
164             return delegate.getInstance(Key.get(type, qualifier));
165         catch (RuntimeException e) {
166             throw new InstanceNotFoundException(type, qualifier, e);
167         }
168     }
169 
170     @Nonnull
171     @Override
172     public <T> Collection<T> getInstances(@Nonnull Class<T> typethrows InstanceNotFoundException {
173         requireNonNull(type, ERROR_TYPE_NULL);
174 
175         if (isClosed()) {
176             throw new InstanceNotFoundException(type, new ClosedInjectorException(this));
177         }
178 
179         List<T> instances = new ArrayList<>();
180 
181         List<com.google.inject.Binding<T>> bindings;
182         try {
183             bindings = delegate.findBindingsByType(TypeLiteral.get(type));
184         catch (RuntimeException e) {
185             throw new InstanceNotFoundException(type, e);
186         }
187         if (bindings == null) {
188             throw new InstanceNotFoundException(type);
189         }
190 
191         for (com.google.inject.Binding<T> binding : bindings) {
192             try {
193                 instances.add(delegate.getInstance(binding.getKey()));
194             catch (RuntimeException e) {
195                 throw new InstanceNotFoundException(type, e);
196             }
197         }
198 
199         return instances;
200     }
201 
202     @Nonnull
203     @Override
204     public <T> Collection<Qualified<T>> getQualifiedInstances(@Nonnull Class<T> typethrows InstanceNotFoundException {
205         requireNonNull(type, ERROR_TYPE_NULL);
206 
207         if (isClosed()) {
208             throw new InstanceNotFoundException(type, new ClosedInjectorException(this));
209         }
210 
211         List<Qualified<T>> instances = new ArrayList<>();
212 
213         List<com.google.inject.Binding<T>> bindings;
214         try {
215             bindings = delegate.findBindingsByType(TypeLiteral.get(type));
216         catch (RuntimeException e) {
217             throw new InstanceNotFoundException(type, e);
218         }
219         if (bindings == null) {
220             throw new InstanceNotFoundException(type);
221         }
222 
223         for (com.google.inject.Binding<T> binding : bindings) {
224             try {
225                 Key<T> key = binding.getKey();
226                 final T instance = delegate.getInstance(key);
227                 instances.add(new Qualified<>(instance, translate(key.getAnnotation())));
228             catch (RuntimeException e) {
229                 throw new InstanceNotFoundException(type, e);
230             }
231         }
232 
233         return instances;
234     }
235 
236     @Nullable
237     private Annotation translate(@Nullable Annotation annotation) {
238         if (annotation instanceof com.google.inject.name.Named) {
239             return AnnotationUtils.named(((com.google.inject.name.Namedannotation).value());
240         }
241         return annotation;
242     }
243 
244     @Override
245     public void injectMembers(@Nonnull Object instancethrows MembersInjectionException {
246         requireNonNull(instance, ERROR_INSTANCE_NULL);
247 
248         if (isClosed()) {
249             throw new MembersInjectionException(instance, new ClosedInjectorException(this));
250         }
251 
252         try {
253             delegate.injectMembers(instance);
254         catch (RuntimeException e) {
255             throw new MembersInjectionException(instance, e);
256         }
257     }
258 
259     @Nonnull
260     @Override
261     public com.google.inject.Injector getDelegateInjector() {
262         return delegate;
263     }
264 
265     @Override
266     public void release(@Nonnull Object instance) {
267         instanceTracker.release(instance);
268     }
269 
270     @Override
271     public void close() {
272         if (isClosed()) {
273             throw new ClosedInjectorException(this);
274         }
275 
276         instanceTracker.releaseAll();
277 
278         synchronized (lock) {
279             closed = true;
280         }
281     }
282 
283     private boolean isClosed() {
284         synchronized (lock) {
285             return closed;
286         }
287     }
288 }