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