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((TargetBinding) binding);
078 } else if (binding instanceof InstanceBinding) {
079 handleInstanceBinding((InstanceBinding) binding);
080 } else if (binding instanceof ProviderTypeBinding) {
081 handleProviderTypeBinding((ProviderTypeBinding) binding);
082 } else if (binding instanceof ProviderBinding) {
083 handleProviderBinding((ProviderBinding) binding);
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> type) throws 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 qualifier) throws 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> type) throws 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> type) throws 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.Named) annotation).value());
240 }
241 return annotation;
242 }
243
244 @Override
245 public void injectMembers(@Nonnull Object instance) throws 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 }
|