My favorites | Sign in
Google
                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
/**
* Copyright (C) 2007 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.google.inject.throwingproviders;

import com.google.inject.Binder;
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.Key;
import com.google.inject.Provider;
import com.google.inject.TypeLiteral;
import com.google.inject.binder.ScopedBindingBuilder;
import static com.google.inject.internal.Preconditions.checkNotNull;
import com.google.inject.internal.UniqueAnnotations;
import com.google.inject.util.Types;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Proxy;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;

/**
* <p>Builds a binding for a {@link ThrowingProvider} using a fluent API:
* <pre><code>ThrowingProviderBinder.create(binder())
* .bind(RemoteProvider.class, Customer.class)
* .to(RemoteCustomerProvider.class)
* .in(RequestScope.class);
* </code></pre>
*
* @author jmourits@google.com (Jerome Mourits)
* @author jessewilson@google.com (Jesse Wilson)
*/
public class ThrowingProviderBinder {

private final Binder binder;

private ThrowingProviderBinder(Binder binder) {
this.binder = binder;
}

public static ThrowingProviderBinder create(Binder binder) {
return new ThrowingProviderBinder(binder);
}

public <P extends ThrowingProvider> SecondaryBinder<P>
bind(final Class<P> interfaceType, final Type valueType) {
return new SecondaryBinder<P>(interfaceType, valueType);
}

public class SecondaryBinder<P extends ThrowingProvider> {
private final Class<P> interfaceType;
private final Type valueType;
private Class<? extends Annotation> annotationType;
private Annotation annotation;
private final Class<?> exceptionType;

public SecondaryBinder(Class<P> interfaceType, Type valueType) {
this.interfaceType = checkNotNull(interfaceType, "interfaceType");
this.valueType = checkNotNull(valueType, "valueType");
checkInterface();
this.exceptionType = getExceptionType(interfaceType);
}

public SecondaryBinder<P> annotatedWith(Class<? extends Annotation> annotationType) {
if (!(this.annotationType == null && this.annotation == null)) {
throw new IllegalStateException();
}
this.annotationType = annotationType;
return this;
}

public SecondaryBinder<P> annotatedWith(Annotation annotation) {
if (!(this.annotationType == null && this.annotation == null)) {
throw new IllegalStateException();
}
this.annotation = annotation;
return this;
}

public ScopedBindingBuilder to(P target) {
Key<P> targetKey = Key.get(interfaceType, UniqueAnnotations.create());
binder.bind(targetKey).toInstance(target);
return to(targetKey);
}

public ScopedBindingBuilder to(Class<? extends P> targetType) {
return to(Key.get(targetType));
}

public ScopedBindingBuilder to(final Key<? extends P> targetKey) {
checkNotNull(targetKey, "targetKey");
final Key<Result> resultKey = Key.get(Result.class, UniqueAnnotations.create());
final Key<P> key = createKey();

binder.bind(key).toProvider(new Provider<P>() {
private P instance;

@Inject void initialize(final Injector injector) {
instance = interfaceType.cast(Proxy.newProxyInstance(
interfaceType.getClassLoader(), new Class<?>[] { interfaceType },
new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
return injector.getInstance(resultKey).getOrThrow();
}
}));
}

public P get() {
return instance;
}
});

return binder.bind(resultKey).toProvider(new Provider<Result>() {
private Injector injector;

@Inject void initialize(Injector injector) {
this.injector = injector;
}

public Result get() {
try {
return Result.forValue(injector.getInstance(targetKey).get());
} catch (Exception e) {
if (exceptionType.isInstance(e)) {
return Result.forException(e);
} else if (e instanceof RuntimeException) {
throw (RuntimeException) e;
} else {
// this should never happen
throw new RuntimeException(e);
}
}
}
});
}

/**
* Returns the exception type declared to be thrown by the get method of
* {@code interfaceType}.
*/
@SuppressWarnings({"unchecked"})
private <P extends ThrowingProvider> Class<?> getExceptionType(Class<P> interfaceType) {
ParameterizedType genericUnreliableProvider
= (ParameterizedType) interfaceType.getGenericInterfaces()[0];
return (Class<? extends Exception>) genericUnreliableProvider.getActualTypeArguments()[1];
}

private void checkInterface() {
String errorMessage = "%s is not a compliant interface "
+ "- see the Javadoc for ThrowingProvider";

checkArgument(interfaceType.isInterface(), errorMessage, interfaceType.getName());
checkArgument(interfaceType.getGenericInterfaces().length == 1, errorMessage,
interfaceType.getName());
checkArgument(interfaceType.getInterfaces()[0] == ThrowingProvider.class,
errorMessage, interfaceType.getName());

// Ensure that T is parameterized and unconstrained.
ParameterizedType genericThrowingProvider
= (ParameterizedType) interfaceType.getGenericInterfaces()[0];
if (interfaceType.getTypeParameters().length == 1) {
checkArgument(interfaceType.getTypeParameters().length == 1, errorMessage,
interfaceType.getName());
String returnTypeName = interfaceType.getTypeParameters()[0].getName();
Type returnType = genericThrowingProvider.getActualTypeArguments()[0];
checkArgument(returnType instanceof TypeVariable, errorMessage, interfaceType.getName());
checkArgument(returnTypeName.equals(((TypeVariable) returnType).getName()),
errorMessage, interfaceType.getName());
} else {
checkArgument(interfaceType.getTypeParameters().length == 0,
errorMessage, interfaceType.getName());
checkArgument(genericThrowingProvider.getActualTypeArguments()[0].equals(valueType),
errorMessage, interfaceType.getName());
}

Type exceptionType = genericThrowingProvider.getActualTypeArguments()[1];
checkArgument(exceptionType instanceof Class, errorMessage, interfaceType.getName());

if (interfaceType.getDeclaredMethods().length == 1) {
Method method = interfaceType.getDeclaredMethods()[0];
checkArgument(method.getName().equals("get"), errorMessage, interfaceType.getName());
checkArgument(method.getParameterTypes().length == 0,
errorMessage, interfaceType.getName());
} else {
checkArgument(interfaceType.getDeclaredMethods().length == 0,
errorMessage, interfaceType.getName());
}
}

private void checkArgument(boolean condition,
String messageFormat, Object... args) {
if (!condition) {
throw new IllegalArgumentException(String.format(messageFormat, args));
}
}

@SuppressWarnings({"unchecked"})
private Key<P> createKey() {
TypeLiteral<P> typeLiteral;
if (interfaceType.getTypeParameters().length == 1) {
ParameterizedType type = Types.newParameterizedTypeWithOwner(
interfaceType.getEnclosingClass(), interfaceType, valueType);
typeLiteral = (TypeLiteral<P>) TypeLiteral.get(type);
} else {
typeLiteral = TypeLiteral.get(interfaceType);
}

if (annotation != null) {
return Key.get(typeLiteral, annotation);

} else if (annotationType != null) {
return Key.get(typeLiteral, annotationType);

} else {
return Key.get(typeLiteral);
}
}
}

/**
* Represents the returned value from a call to {@link
* ThrowingProvider#get()}. This is the value that will be scoped by Guice.
*/
private static class Result {
private final Object value;
private final Exception exception;

private Result(Object value, Exception exception) {
this.value = value;
this.exception = exception;
}

public static Result forValue(Object value) {
return new Result(value, null);
}

public static Result forException(Exception e) {
return new Result(null, e);
}

public Object getOrThrow() throws Exception {
if (exception != null) {
throw exception;
} else {
return value;
}
}
}
}
Show details Hide details

Change log

r859 by limpbizkit on Feb 20, 2009   Diff
Regrettably replacing jarjar'd Google
Collections with minimal copies of the
parts that we use.

The main benefit is a (significant)
reduction in size of the Guice+AOP .jar -
from 1004KB to 641KB. The drawback is that
it's now a lot harder to use new Google
Collections features, or to keep up-to-
date with Google Collections bugfixes and
optimizations.
Go to: 
Sign in to write a code review

Older revisions

r742 by limpbizkit on Dec 26, 2008   Diff
Mikeward's Javadoc fixes. Mike read
through all of our Javadoc (thank
you!) and found a few typos and
simplifications
r514 by limpbizkit on Jun 10, 2008   Diff
Fixing owner types so they're no
longer missing in
ThrowingProviderBinder. Also adding
the necessary precondition checks so
that we always include 'em when
...
r504 by kevinb9n on Jun 03, 2008   Diff
Adopt latest google collections
snapshot (partially); some style
cleanup
All revisions of this file

File info

Size: 9515 bytes, 265 lines