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