Skip to content

Commit 8f55785

Browse files
committed
Allow passing object mapper configurer (fixes #42)
1 parent e9a6204 commit 8f55785

File tree

5 files changed

+103
-18
lines changed

5 files changed

+103
-18
lines changed
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package graphql.servlet;
2+
3+
import com.fasterxml.jackson.databind.ObjectMapper;
4+
5+
/**
6+
* @author Andrew Potter
7+
*/
8+
public class DefaultObjectMapperConfigurer implements ObjectMapperConfigurer {
9+
@Override
10+
public void configure(ObjectMapper mapper) {
11+
12+
}
13+
}

src/main/java/graphql/servlet/GraphQLServlet.java

Lines changed: 37 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,14 @@
1414
*/
1515
package graphql.servlet;
1616

17+
import com.fasterxml.jackson.annotation.JacksonInject;
1718
import com.fasterxml.jackson.core.JsonParser;
1819
import com.fasterxml.jackson.core.type.TypeReference;
1920
import com.fasterxml.jackson.databind.DeserializationContext;
21+
import com.fasterxml.jackson.databind.InjectableValues;
2022
import com.fasterxml.jackson.databind.JsonDeserializer;
2123
import com.fasterxml.jackson.databind.ObjectMapper;
22-
import com.fasterxml.jackson.databind.SerializationFeature;
24+
import com.fasterxml.jackson.databind.ObjectReader;
2325
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
2426
import graphql.ExecutionInput;
2527
import graphql.ExecutionResult;
@@ -69,8 +71,6 @@ public abstract class GraphQLServlet extends HttpServlet implements Servlet, Gra
6971
public static final int STATUS_OK = 200;
7072
public static final int STATUS_BAD_REQUEST = 400;
7173

72-
private static final ObjectMapper mapper = new ObjectMapper().disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
73-
7474
protected abstract GraphQLSchemaProvider getSchemaProvider();
7575
protected abstract GraphQLContext createContext(Optional<HttpServletRequest> request, Optional<HttpServletResponse> response);
7676
protected abstract Object createRootObject(Optional<HttpServletRequest> request, Optional<HttpServletResponse> response);
@@ -79,17 +79,19 @@ public abstract class GraphQLServlet extends HttpServlet implements Servlet, Gra
7979
protected abstract Map<String, Object> transformVariables(GraphQLSchema schema, String query, Map<String, Object> variables);
8080
protected abstract GraphQLErrorHandler getGraphQLErrorHandler();
8181

82+
private final LazyObjectMapperBuilder lazyObjectMapperBuilder;
8283
private final List<GraphQLServletListener> listeners;
8384
private final ServletFileUpload fileUpload;
8485

8586
private final RequestHandler getHandler;
8687
private final RequestHandler postHandler;
8788

8889
public GraphQLServlet() {
89-
this(null, null);
90+
this(null, null, null);
9091
}
9192

92-
public GraphQLServlet(List<GraphQLServletListener> listeners, FileItemFactory fileItemFactory) {
93+
public GraphQLServlet(ObjectMapperConfigurer objectMapperConfigurer, List<GraphQLServletListener> listeners, FileItemFactory fileItemFactory) {
94+
this.lazyObjectMapperBuilder = new LazyObjectMapperBuilder(objectMapperConfigurer != null ? objectMapperConfigurer : new DefaultObjectMapperConfigurer());
9395
this.listeners = listeners != null ? new ArrayList<>(listeners) : new ArrayList<>();
9496
this.fileUpload = new ServletFileUpload(fileItemFactory != null ? fileItemFactory : new DiskFileItemFactory());
9597

@@ -172,7 +174,7 @@ public GraphQLServlet(List<GraphQLServletListener> listeners, FileItemFactory fi
172174
}
173175

174176
if (graphQLRequest == null) {
175-
graphQLRequest = mapper.readValue(inputStream, GraphQLRequest.class);
177+
graphQLRequest = getGraphQLRequestMapper().readValue(inputStream);
176178
}
177179

178180
} catch (Exception e) {
@@ -190,6 +192,21 @@ public GraphQLServlet(List<GraphQLServletListener> listeners, FileItemFactory fi
190192
};
191193
}
192194

195+
private ObjectMapper getMapper() {
196+
return lazyObjectMapperBuilder.getMapper();
197+
}
198+
199+
/**
200+
* Creates an {@link ObjectReader} for deserializing {@link GraphQLRequest}
201+
*/
202+
private ObjectReader getGraphQLRequestMapper() {
203+
// Add object mapper to injection so VariablesDeserializer can access it...
204+
InjectableValues.Std injectableValues = new InjectableValues.Std();
205+
injectableValues.addValue(ObjectMapper.class, getMapper());
206+
207+
return getMapper().reader(injectableValues).forType(GraphQLRequest.class);
208+
}
209+
193210
public void addListener(GraphQLServletListener servletListener) {
194211
listeners.add(servletListener);
195212
}
@@ -211,8 +228,8 @@ public String[] getMutations() {
211228
@Override
212229
public String executeQuery(String query) {
213230
try {
214-
final ExecutionResult result = newGraphQL(getSchemaProvider().getSchema()).execute(query, createContext(Optional.empty(), Optional.empty()), new HashMap<>());
215-
return mapper.writeValueAsString(createResultFromDataAndErrors(result.getData(), result.getErrors()));
231+
final ExecutionResult result = newGraphQL(getSchemaProvider().getSchema()).execute(new ExecutionInput(query, null, createContext(Optional.empty(), Optional.empty()), createRootObject(Optional.empty(), Optional.empty()), new HashMap<>()));
232+
return getMapper().writeValueAsString(createResultFromDataAndErrors(result.getData(), result.getErrors()));
216233
} catch (Exception e) {
217234
return e.getMessage();
218235
}
@@ -282,7 +299,7 @@ private void query(String query, String operationName, Map<String, Object> varia
282299
final List<GraphQLError> errors = executionResult.getErrors();
283300
final Object data = executionResult.getData();
284301

285-
final String response = mapper.writeValueAsString(createResultFromDataAndErrors(data, errors));
302+
final String response = getMapper().writeValueAsString(createResultFromDataAndErrors(data, errors));
286303

287304
resp.setContentType(APPLICATION_JSON_UTF8);
288305
resp.setStatus(STATUS_OK);
@@ -339,21 +356,28 @@ private <T> void runCallbacks(List<T> callbacks, Consumer<T> action) {
339356
}
340357

341358
protected static class VariablesDeserializer extends JsonDeserializer<Map<String, Object>> {
359+
360+
private final ObjectMapper mapper;
361+
362+
public VariablesDeserializer(@JacksonInject ObjectMapper mapper) {
363+
this.mapper = mapper;
364+
}
365+
342366
@Override
343367
public Map<String, Object> deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
344-
return deserializeVariablesObject(p.readValueAs(Object.class));
368+
return deserializeVariablesObject(p.readValueAs(Object.class), mapper);
345369
}
346370
}
347371

348-
private static Map<String, Object> deserializeVariables(String variables) {
372+
private Map<String, Object> deserializeVariables(String variables) {
349373
try {
350-
return deserializeVariablesObject(mapper.readValue(variables, Object.class));
374+
return deserializeVariablesObject(getMapper().readValue(variables, Object.class), getMapper());
351375
} catch (IOException e) {
352376
throw new RuntimeException(e);
353377
}
354378
}
355379

356-
private static Map<String, Object> deserializeVariablesObject(Object variables) {
380+
private static Map<String, Object> deserializeVariablesObject(Object variables, ObjectMapper mapper) {
357381
if (variables instanceof Map) {
358382
@SuppressWarnings("unchecked")
359383
Map<String, Object> genericVariables = (Map<String, Object>) variables;
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package graphql.servlet;
2+
3+
import com.fasterxml.jackson.databind.ObjectMapper;
4+
import com.fasterxml.jackson.databind.SerializationFeature;
5+
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
6+
7+
/**
8+
* @author Andrew Potter
9+
*/
10+
public class LazyObjectMapperBuilder {
11+
private final ObjectMapperConfigurer configurer;
12+
private volatile ObjectMapper mapper;
13+
14+
public LazyObjectMapperBuilder(ObjectMapperConfigurer configurer) {
15+
this.configurer = configurer;
16+
}
17+
18+
// Double-check idiom for lazy initialization of instance fields.
19+
public ObjectMapper getMapper() {
20+
ObjectMapper result = mapper;
21+
if (result == null) { // First check (no locking)
22+
synchronized(this) {
23+
result = mapper;
24+
if (result == null) // Second check (with locking)
25+
mapper = result = createObjectMapper();
26+
}
27+
}
28+
29+
return result;
30+
}
31+
32+
private ObjectMapper createObjectMapper() {
33+
ObjectMapper mapper = new ObjectMapper().disable(SerializationFeature.FAIL_ON_EMPTY_BEANS).registerModule(new Jdk8Module());
34+
configurer.configure(mapper);
35+
36+
return mapper;
37+
}
38+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package graphql.servlet;
2+
3+
import com.fasterxml.jackson.databind.ObjectMapper;
4+
5+
/**
6+
* @author Andrew Potter
7+
*/
8+
public interface ObjectMapperConfigurer {
9+
void configure(ObjectMapper mapper);
10+
}

src/main/java/graphql/servlet/SimpleGraphQLServlet.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,15 +39,15 @@ public SimpleGraphQLServlet(GraphQLSchema schema, ExecutionStrategy executionStr
3939
}
4040

4141
public SimpleGraphQLServlet(GraphQLSchema schema, ExecutionStrategyProvider executionStrategyProvider) {
42-
this(schema, executionStrategyProvider, null, null, null, null, null);
42+
this(schema, executionStrategyProvider, null, null, null, null, null, null);
4343
}
4444

45-
public SimpleGraphQLServlet(final GraphQLSchema schema, ExecutionStrategyProvider executionStrategyProvider, List<GraphQLServletListener> listeners, Instrumentation instrumentation, GraphQLErrorHandler errorHandler, GraphQLContextBuilder contextBuilder, GraphQLRootObjectBuilder rootObjectBuilder) {
46-
this(new DefaultGraphQLSchemaProvider(schema), executionStrategyProvider, listeners, instrumentation, errorHandler, contextBuilder, rootObjectBuilder);
45+
public SimpleGraphQLServlet(final GraphQLSchema schema, ExecutionStrategyProvider executionStrategyProvider, ObjectMapperConfigurer objectMapperConfigurer, List<GraphQLServletListener> listeners, Instrumentation instrumentation, GraphQLErrorHandler errorHandler, GraphQLContextBuilder contextBuilder, GraphQLRootObjectBuilder rootObjectBuilder) {
46+
this(new DefaultGraphQLSchemaProvider(schema), executionStrategyProvider, objectMapperConfigurer, listeners, instrumentation, errorHandler, contextBuilder, rootObjectBuilder);
4747
}
4848

49-
public SimpleGraphQLServlet(GraphQLSchemaProvider schemaProvider, ExecutionStrategyProvider executionStrategyProvider, List<GraphQLServletListener> listeners, Instrumentation instrumentation, GraphQLErrorHandler errorHandler, GraphQLContextBuilder contextBuilder, GraphQLRootObjectBuilder rootObjectBuilder) {
50-
super(listeners, null);
49+
public SimpleGraphQLServlet(GraphQLSchemaProvider schemaProvider, ExecutionStrategyProvider executionStrategyProvider, ObjectMapperConfigurer objectMapperConfigurer, List<GraphQLServletListener> listeners, Instrumentation instrumentation, GraphQLErrorHandler errorHandler, GraphQLContextBuilder contextBuilder, GraphQLRootObjectBuilder rootObjectBuilder) {
50+
super(objectMapperConfigurer, listeners, null);
5151

5252
this.schemaProvider = schemaProvider;
5353
this.executionStrategyProvider = executionStrategyProvider;

0 commit comments

Comments
 (0)