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