-
Notifications
You must be signed in to change notification settings - Fork 19
/
PropertyFilteringMessageBodyWriter.java
195 lines (160 loc) · 6.28 KB
/
PropertyFilteringMessageBodyWriter.java
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
package com.hubspot.jackson.jaxrs;
import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.SharedMetricRegistries;
import com.codahale.metrics.Timer;
import com.codahale.metrics.servlets.MetricsServlet;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectWriter;
import com.fasterxml.jackson.databind.util.TokenBuffer;
import com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider;
import com.fasterxml.jackson.jaxrs.json.JsonEndpointConfig;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import javax.servlet.ServletContext;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Application;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.UriInfo;
import javax.ws.rs.ext.MessageBodyWriter;
import javax.ws.rs.ext.Provider;
@Provider
@Produces(MediaType.APPLICATION_JSON)
public class PropertyFilteringMessageBodyWriter implements MessageBodyWriter<Object> {
@Context
Application application;
@Context
UriInfo uriInfo;
@Context
ServletContext servletContext;
private volatile JacksonJsonProvider delegate;
@Override
public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
return isJsonType(mediaType) &&
filteringEnabled(type, genericType, annotations, mediaType) &&
getJsonProvider().isWriteable(type, genericType, annotations, mediaType);
}
@Override
public long getSize(Object object, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
return -1;
}
@Override
public void writeTo(Object o, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType,
MultivaluedMap<String, Object> httpHeaders, OutputStream os) throws IOException {
PropertyFiltering annotation = findPropertyFiltering(annotations);
Collection<String> properties = new ArrayList<>();
if (annotation != null) {
properties.addAll(getProperties(annotation.using()));
properties.addAll(Arrays.asList(annotation.always()));
}
PropertyFilter propertyFilter = createPropertyFilter(properties, o, type, genericType, annotations, httpHeaders);
if (!propertyFilter.hasFilters()) {
write(o, type, genericType, annotations, mediaType, httpHeaders, os);
return;
}
Timer timer = getTimer();
Timer.Context context = timer.time();
try {
ObjectMapper mapper = getJsonProvider().locateMapper(type, mediaType);
ObjectWriter writer = JsonEndpointConfig.forWriting(mapper.writer(), annotations, null).getWriter();
JsonNode tree = valueToTree(mapper, writer, o);
propertyFilter.filter(tree);
write(tree, tree.getClass(), tree.getClass(), annotations, mediaType, httpHeaders, os);
} finally {
context.stop();
}
}
protected Collection<String> getProperties(String name) {
List<String> values = uriInfo.getQueryParameters().get(name);
List<String> properties = new ArrayList<>();
if (values != null) {
for (String value : values) {
String[] parts = value.split(",");
for (String part : parts) {
part = part.trim();
if (!part.isEmpty()) {
properties.add(part);
}
}
}
}
return properties;
}
protected PropertyFilter createPropertyFilter(Collection<String> properties, Object o, Class<?> type,
Type genericType, Annotation[] annotations,
MultivaluedMap<String, Object> httpHeaders) {
return new PropertyFilter(properties);
}
protected Timer getTimer() {
return getMetricRegistry().timer(MetricRegistry.name(PropertyFilteringMessageBodyWriter.class, "filter"));
}
protected MetricRegistry getMetricRegistry() {
MetricRegistry registry = (MetricRegistry) servletContext.getAttribute(MetricsServlet.METRICS_REGISTRY);
if (registry == null) {
registry = SharedMetricRegistries.getOrCreate("com.hubspot");
}
return registry;
}
protected boolean filteringEnabled(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
return findPropertyFiltering(annotations) != null;
}
protected JacksonJsonProvider getJsonProvider() {
if (delegate != null) {
return delegate;
}
synchronized (this) {
if (delegate != null) {
return delegate;
}
for (Object o : application.getSingletons()) {
if (o instanceof JacksonJsonProvider) {
delegate = (JacksonJsonProvider) o;
return delegate;
}
}
delegate = new JacksonJsonProvider();
return delegate;
}
}
private JsonNode valueToTree(ObjectMapper mapper, ObjectWriter writer, Object o) {
if (o == null) {
return null;
}
TokenBuffer buf = new TokenBuffer(mapper, false);
JsonNode result;
try {
writer.writeValue(buf, o);
JsonParser jp = buf.asParser();
result = mapper.readTree(jp);
jp.close();
} catch (IOException e) { // should not occur, no real i/o...
throw new IllegalArgumentException(e.getMessage(), e);
}
return result;
}
private void write(Object o, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType,
MultivaluedMap<String, Object> httpHeaders, OutputStream os) throws IOException {
getJsonProvider().writeTo(o, type, genericType, annotations, mediaType, httpHeaders, os);
}
private static boolean isJsonType(MediaType mediaType) {
return MediaType.APPLICATION_JSON_TYPE.getType().equals(mediaType.getType()) &&
MediaType.APPLICATION_JSON_TYPE.getSubtype().equals(mediaType.getSubtype());
}
private static PropertyFiltering findPropertyFiltering(Annotation... annotations) {
for (Annotation annotation : annotations) {
if (annotation.annotationType() == PropertyFiltering.class) {
return (PropertyFiltering) annotation;
}
}
return null;
}
}