Skip to content

Commit 79471c4

Browse files
committed
bean parser: should choose constructor with @Inject annotation when multiple constructors are present fix #381
1 parent 9669a5e commit 79471c4

File tree

15 files changed

+110
-54
lines changed

15 files changed

+110
-54
lines changed

coverage-report/src/test/java/org/jooby/ParserOrder2Feature.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ public class ParserOrder2Feature extends ServerFeature {
2222
Multibinder.newSetBinder(binder, Parser.class).addBinding().toInstance(new Parser() {
2323

2424
@Override
25-
public Object parse(final TypeLiteral<?> type, final Context ctx) throws Exception {
25+
public Object parse(final TypeLiteral<?> type, final Context ctx) throws Throwable {
2626
return ctx.next();
2727
}
2828

@@ -37,7 +37,7 @@ public String toString() {
3737
parser(new Parser() {
3838

3939
@Override
40-
public Object parse(final TypeLiteral<?> type, final Context ctx) throws Exception {
40+
public Object parse(final TypeLiteral<?> type, final Context ctx) throws Throwable {
4141
assertEquals(
4242
"[Basic, Collection, Optional, Enum, Upload, byte[], p2, p1, p3, Date, LocalDate, Locale, bean, valueOf(String), fromString(String), forName(String), init(String)]",
4343
ctx.toString());
@@ -54,7 +54,7 @@ public String toString() {
5454
parser(new Parser() {
5555

5656
@Override
57-
public Object parse(final TypeLiteral<?> type, final Context ctx) throws Exception {
57+
public Object parse(final TypeLiteral<?> type, final Context ctx) throws Throwable {
5858
return ctx.next();
5959
}
6060

coverage-report/src/test/java/org/jooby/ParserOrderFeature.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ public class ParserOrderFeature extends ServerFeature {
2121
parser(new Parser() {
2222

2323
@Override
24-
public Object parse(final TypeLiteral<?> type, final Context ctx) throws Exception {
24+
public Object parse(final TypeLiteral<?> type, final Context ctx) throws Throwable {
2525
assertEquals(
2626
"[Basic, Collection, Optional, Enum, Upload, byte[], p1, p2, p3, Date, LocalDate, Locale, bean, valueOf(String), fromString(String), forName(String), init(String)]",
2727
ctx.toString());
@@ -39,7 +39,7 @@ public String toString() {
3939
Multibinder.newSetBinder(binder, Parser.class).addBinding().toInstance(new Parser() {
4040

4141
@Override
42-
public Object parse(final TypeLiteral<?> type, final Context ctx) throws Exception {
42+
public Object parse(final TypeLiteral<?> type, final Context ctx) throws Throwable {
4343
return ctx.next();
4444
}
4545

@@ -55,7 +55,7 @@ public String toString() {
5555
parser(new Parser() {
5656

5757
@Override
58-
public Object parse(final TypeLiteral<?> type, final Context ctx) throws Exception {
58+
public Object parse(final TypeLiteral<?> type, final Context ctx) throws Throwable {
5959
return ctx.next();
6060
}
6161

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package org.jooby.issues;
2+
3+
import javax.inject.Inject;
4+
5+
import org.jooby.mvc.GET;
6+
import org.jooby.mvc.Path;
7+
import org.jooby.test.ServerFeature;
8+
import org.junit.Test;
9+
10+
public class Issue381 extends ServerFeature {
11+
12+
public static class Foo {
13+
private String bar;
14+
private String foo;
15+
16+
@Inject
17+
public Foo(final String foo, final String bar) {
18+
this.foo = foo;
19+
this.bar = bar;
20+
}
21+
22+
public Foo() {
23+
}
24+
25+
public String foo() {
26+
return foo + ":" + bar;
27+
}
28+
}
29+
30+
@Path("/381")
31+
public static class Controller {
32+
33+
@GET
34+
public String withInject(final String foo, final Foo bean) {
35+
return foo + ":" + bean.foo();
36+
}
37+
38+
}
39+
40+
{
41+
bind(Foo.class);
42+
43+
use(Controller.class);
44+
}
45+
46+
@Test
47+
public void beanParamWithInjectAnnotation() throws Exception {
48+
request()
49+
.get("/381?foo=foo&bar=bar")
50+
.expect("foo:foo:bar");
51+
}
52+
53+
}

jooby-gson/src/main/java/org/jooby/json/GsonParser.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ public GsonParser(final MediaType type, final Gson gson) {
3838
}
3939

4040
@Override
41-
public Object parse(final TypeLiteral<?> type, final Context ctx) throws Exception {
41+
public Object parse(final TypeLiteral<?> type, final Context ctx) throws Throwable {
4242
MediaType ctype = ctx.type();
4343
if (ctype.isAny()) {
4444
// */*

jooby-hbv/src/main/java/org/jooby/hbv/HbvParser.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ public HbvParser(final Predicate<TypeLiteral<?>> predicate) {
4040
}
4141

4242
@Override
43-
public Object parse(final TypeLiteral<?> type, final Context ctx) throws Exception {
43+
public Object parse(final TypeLiteral<?> type, final Context ctx) throws Throwable {
4444
Object value = ctx.next();
4545

4646
if (predicate.test(type)) {

jooby-jackson/src/main/java/org/jooby/json/JacksonParser.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ public JacksonParser(final ObjectMapper mapper, final MediaType type) {
3838
}
3939

4040
@Override
41-
public Object parse(final TypeLiteral<?> type, final Context ctx) throws Exception {
41+
public Object parse(final TypeLiteral<?> type, final Context ctx) throws Throwable {
4242
MediaType ctype = ctx.type();
4343
if (ctype.isAny()) {
4444
// */*

jooby/src/main/java/org/jooby/Parser.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ interface Callback<T> {
8989
* @return A parsed value
9090
* @throws Exception If something goes wrong.
9191
*/
92-
Object invoke(T data) throws Exception;
92+
Object invoke(T data) throws Throwable;
9393

9494
}
9595

@@ -340,7 +340,7 @@ interface Context extends Builder {
340340
* @return A parsed value.
341341
* @throws Exception An err with a 400 status.
342342
*/
343-
Object next() throws Exception;
343+
Object next() throws Throwable;
344344

345345
/**
346346
* Invoke next parser in the chain and switch/change the target type we are looking for. Useful
@@ -350,7 +350,7 @@ interface Context extends Builder {
350350
* @return A parsed value.
351351
* @throws Exception An err with a 400 status.
352352
*/
353-
Object next(TypeLiteral<?> type) throws Exception;
353+
Object next(TypeLiteral<?> type) throws Throwable;
354354

355355
/**
356356
* Invoke next parser in the chain and switch/change the target type we are looking for but also
@@ -362,7 +362,7 @@ interface Context extends Builder {
362362
* @return A parsed value.
363363
* @throws Exception An err with a 400 status.
364364
*/
365-
Object next(TypeLiteral<?> type, Object data) throws Exception;
365+
Object next(TypeLiteral<?> type, Object data) throws Throwable;
366366

367367
}
368368

@@ -408,8 +408,8 @@ interface Context extends Builder {
408408
* @param type Requested type.
409409
* @param ctx Execution context.
410410
* @return A parsed value.
411-
* @throws Exception If conversion fails.
411+
* @throws Throwable If conversion fails.
412412
*/
413-
Object parse(TypeLiteral<?> type, Context ctx) throws Exception;
413+
Object parse(TypeLiteral<?> type, Context ctx) throws Throwable;
414414

415415
}

jooby/src/main/java/org/jooby/internal/BuiltinParser.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ ImmutableMap.<Class<?>, Function<String, Object>> builder()
7171
.build();
7272

7373
@Override
74-
public Object parse(final TypeLiteral<?> type, final Parser.Context ctx) throws Exception {
74+
public Object parse(final TypeLiteral<?> type, final Parser.Context ctx) throws Throwable {
7575
Function<String, Object> parser = parsers.get(type.getRawType());
7676
if (parser != null) {
7777
return ctx
@@ -132,7 +132,7 @@ private boolean matches(final TypeLiteral<?> toType) {
132132
}
133133

134134
@Override
135-
public Object parse(final TypeLiteral<?> type, final Parser.Context ctx) throws Exception {
135+
public Object parse(final TypeLiteral<?> type, final Parser.Context ctx) throws Throwable {
136136
if (matches(type)) {
137137
return ctx.param(values -> {
138138
ImmutableCollection.Builder builder = parsers.get(type.getRawType()).get();
@@ -163,7 +163,7 @@ private boolean matches(final TypeLiteral<?> toType) {
163163

164164
@Override
165165
public Object parse(final TypeLiteral<?> type, final Parser.Context ctx)
166-
throws Exception {
166+
throws Throwable {
167167
if (matches(type)) {
168168
TypeLiteral<?> paramType = TypeLiteral.get(((ParameterizedType) type.getType())
169169
.getActualTypeArguments()[0]);
@@ -190,7 +190,7 @@ public Object parse(final TypeLiteral<?> type, final Parser.Context ctx)
190190
Enum {
191191
@Override
192192
public Object parse(final TypeLiteral<?> type, final Parser.Context ctx)
193-
throws Exception {
193+
throws Throwable {
194194
Class rawType = type.getRawType();
195195
if (Enum.class.isAssignableFrom(rawType)) {
196196
return ctx
@@ -207,7 +207,7 @@ public Object parse(final TypeLiteral<?> type, final Parser.Context ctx)
207207

208208
Upload {
209209
@Override
210-
public Object parse(final TypeLiteral<?> type, final Context ctx) throws Exception {
210+
public Object parse(final TypeLiteral<?> type, final Context ctx) throws Throwable {
211211
if (Upload.class == type.getRawType()) {
212212
return ctx.upload(uploads -> uploads.get(0));
213213
} else {
@@ -218,7 +218,7 @@ public Object parse(final TypeLiteral<?> type, final Context ctx) throws Excepti
218218

219219
Bytes {
220220
@Override
221-
public Object parse(final TypeLiteral<?> type, final Parser.Context ctx) throws Exception {
221+
public Object parse(final TypeLiteral<?> type, final Parser.Context ctx) throws Throwable {
222222
if (type.getRawType() == byte[].class) {
223223
return ctx.body(body -> body.bytes());
224224
}

jooby/src/main/java/org/jooby/internal/mvc/RequestParam.java

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
package org.jooby.internal.mvc;
2020

2121
import java.lang.reflect.AnnotatedElement;
22-
import java.lang.reflect.Field;
2322
import java.lang.reflect.Parameter;
2423
import java.lang.reflect.Type;
2524
import java.util.Map;
@@ -80,8 +79,7 @@ private interface GetValue {
8079
*/
8180
builder.put(TypeLiteral.get(Session.class), (req, rsp, param) -> req.session());
8281
builder.put(TypeLiteral.get(Types.newParameterizedType(Optional.class, Session.class)),
83-
(req, rsp, param) -> req.ifSession()
84-
);
82+
(req, rsp, param) -> req.ifSession());
8583
/**
8684
* Cookie
8785
*/
@@ -90,8 +88,7 @@ private interface GetValue {
9088
builder.put(TypeLiteral.get(Types.listOf(Cookie.class)), (req, rsp, param) -> req.cookies());
9189
builder.put(TypeLiteral.get(Types.newParameterizedType(Optional.class, Cookie.class)),
9290
(req, rsp, param) -> req.cookies().stream()
93-
.filter(c -> c.name().equalsIgnoreCase(param.name)).findFirst()
94-
);
91+
.filter(c -> c.name().equalsIgnoreCase(param.name)).findFirst());
9592
/**
9693
* Header
9794
*/
@@ -122,10 +119,6 @@ private interface GetValue {
122119

123120
private boolean optional;
124121

125-
public RequestParam(final Field field) {
126-
this(field, field.getName(), field.getGenericType());
127-
}
128-
129122
public RequestParam(final Parameter parameter, final String name) {
130123
this(parameter, name, parameter.getParameterizedType());
131124
}
@@ -147,7 +140,7 @@ public RequestParam(final AnnotatedElement elem, final String name, final Type t
147140
this.strategy = injector.getOrDefault(strategyType, param());
148141
}
149142

150-
public Object value(final Request req, final Response rsp) throws Exception {
143+
public Object value(final Request req, final Response rsp) throws Throwable {
151144
return strategy.apply(req, rsp, this);
152145
}
153146

jooby/src/main/java/org/jooby/internal/parser/BeanParser.java

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,13 @@
2121
import java.lang.reflect.Constructor;
2222
import java.lang.reflect.Field;
2323
import java.lang.reflect.Modifier;
24+
import java.util.Arrays;
2425
import java.util.List;
2526
import java.util.Map;
2627
import java.util.Map.Entry;
28+
import java.util.stream.Collectors;
29+
30+
import javax.inject.Inject;
2731

2832
import org.jooby.Mutant;
2933
import org.jooby.Parser;
@@ -45,7 +49,7 @@
4549
public class BeanParser implements Parser {
4650

4751
@Override
48-
public Object parse(final TypeLiteral<?> type, final Context ctx) throws Exception {
52+
public Object parse(final TypeLiteral<?> type, final Context ctx) throws Throwable {
4953
Class<?> beanType = type.getRawType();
5054
if (Primitives.isWrapperType(Primitives.wrap(beanType))
5155
|| CharSequence.class.isAssignableFrom(beanType)) {
@@ -69,17 +73,22 @@ public String toString() {
6973
}
7074

7175
private Object newBean(final Request req, final Response rsp,
72-
final Map<String, Mutant> params, final Class<?> beanType)
73-
throws Exception {
76+
final Map<String, Mutant> params, final Class<?> beanType) throws Throwable {
7477
ParameterNameProvider classInfo = req.require(ParameterNameProvider.class);
75-
Constructor<?>[] constructors = beanType.getDeclaredConstructors();
76-
if (constructors.length > 1) {
78+
List<Constructor<?>> constructors = Arrays.asList(beanType.getDeclaredConstructors()).stream()
79+
.filter(c -> c.isAnnotationPresent(Inject.class))
80+
.collect(Collectors.toList());
81+
if (constructors.size() == 0) {
82+
// No inject annotation, use a declared constructor
83+
constructors.addAll(Arrays.asList(beanType.getDeclaredConstructors()));
84+
}
85+
if (constructors.size() > 1) {
7786
return null;
7887
}
7988
final Object bean;
80-
Constructor<?> constructor = constructors[0];
81-
RequestParamProvider provider =
82-
new RequestParamProviderImpl(new RequestParamNameProviderImpl(classInfo));
89+
Constructor<?> constructor = constructors.get(0);
90+
RequestParamProvider provider = new RequestParamProviderImpl(
91+
new RequestParamNameProviderImpl(classInfo));
8392
List<RequestParam> parameters = provider.parameters(constructor);
8493
Object[] args = new Object[parameters.size()];
8594
for (int i = 0; i < args.length; i++) {
@@ -100,9 +109,8 @@ private Object newBean(final Request req, final Response rsp,
100109
int mods = field.getModifiers();
101110
if (!Modifier.isFinal(mods) && !Modifier.isStatic(mods) && !Modifier.isTransient(mods)) {
102111
// get
103-
RequestParam fparam = new RequestParam(field);
104-
@SuppressWarnings("unchecked")
105-
Object value = req.param(pname).to(fparam.type);
112+
RequestParam fparam = new RequestParam(field, pname, field.getGenericType());
113+
Object value = fparam.value(req, rsp);
106114

107115
// set
108116
field.setAccessible(true);
@@ -145,8 +153,7 @@ private Object newBeanInterface(final Request req, final Class<?> beanType) {
145153
return Reflection.newProxy(beanType, (proxy, method, args) -> {
146154
StringBuilder name = new StringBuilder(method.getName()
147155
.replace("get", "")
148-
.replace("is", "")
149-
);
156+
.replace("is", ""));
150157
name.setCharAt(0, Character.toLowerCase(name.charAt(0)));
151158
return req.param(name.toString()).to(TypeLiteral.get(method.getGenericReturnType()));
152159
});

0 commit comments

Comments
 (0)