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((TargetBinding) binding);
082 } else if (binding instanceof InstanceBinding) {
083 handleInstanceBinding((InstanceBinding) binding);
084 } else if (binding instanceof ProviderTypeBinding) {
085 handleProviderTypeBinding((ProviderTypeBinding) binding);
086 } else if (binding instanceof ProviderBinding) {
087 handleProviderBinding((ProviderBinding) binding);
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> type) throws 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 qualifier) throws 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> type) throws 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> type) throws 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.Named) annotation).value());
244 }
245 return annotation;
246 }
247
248 @Override
249 public void injectMembers(@Nonnull Object instance) throws 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 }
|