Skip to content

Commit b87bde6

Browse files
committed
modify JacksonDatabindHandle and JacksonStreamHandle to work with handle registry for readAs and writeAs convenience methods; update unit tests to demonstrate functionality
1 parent 653a200 commit b87bde6

File tree

7 files changed

+236
-133
lines changed

7 files changed

+236
-133
lines changed

src/main/java/com/marklogic/client/impl/HandleFactoryRegistryImpl.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525
import com.marklogic.client.io.FileHandle;
2626
import com.marklogic.client.io.InputSourceHandle;
2727
import com.marklogic.client.io.InputStreamHandle;
28+
import com.marklogic.client.io.JacksonHandle;
29+
import com.marklogic.client.io.JacksonParserHandle;
2830
import com.marklogic.client.io.ReaderHandle;
2931
import com.marklogic.client.io.SourceHandle;
3032
import com.marklogic.client.io.StringHandle;
@@ -46,6 +48,8 @@ public static HandleFactoryRegistry registerDefaults(HandleFactoryRegistry regis
4648
registry.register(FileHandle.newFactory());
4749
registry.register(InputSourceHandle.newFactory());
4850
registry.register(InputStreamHandle.newFactory());
51+
registry.register(JacksonHandle.newFactory());
52+
registry.register(JacksonParserHandle.newFactory());
4953
registry.register(ReaderHandle.newFactory());
5054
registry.register(SourceHandle.newFactory());
5155
registry.register(StringHandle.newFactory());
@@ -141,4 +145,4 @@ Class<?> getRegisteredType(Class<?> type) {
141145

142146
return null;
143147
}
144-
}
148+
}

src/main/java/com/marklogic/client/io/JacksonDatabindHandle.java

Lines changed: 42 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,14 @@
2121
import java.io.InputStreamReader;
2222
import java.io.OutputStream;
2323
import java.io.OutputStreamWriter;
24+
import java.util.Arrays;
25+
import java.util.HashSet;
26+
import java.util.Set;
2427

2528
import com.fasterxml.jackson.core.JsonParseException;
2629
import com.fasterxml.jackson.databind.JsonMappingException;
2730
import com.fasterxml.jackson.databind.JsonNode;
28-
31+
import com.fasterxml.jackson.databind.ObjectMapper;
2932
import com.marklogic.client.MarkLogicIOException;
3033
import com.marklogic.client.io.marker.ContentHandle;
3134
import com.marklogic.client.io.marker.ContentHandleFactory;
@@ -43,6 +46,28 @@ public class JacksonDatabindHandle<T>
4346
private Class contentClass;
4447
private T content;
4548

49+
/**
50+
* Creates a factory to create a JacksonDatabindHandle instance for POJO instances
51+
* of the specified classes.
52+
* @param pojoClasses the POJO classes for which this factory provides a handle
53+
* @return the factory
54+
*/
55+
static public ContentHandleFactory newFactory(Class<?>... pojoClasses) {
56+
if (pojoClasses == null || pojoClasses.length == 0) return null;
57+
return new JacksonDatabindHandleFactory(pojoClasses);
58+
}
59+
/**
60+
* Creates a factory to create a JacksonDatabindHandle instance for POJO instances
61+
* of the specified classes.
62+
* @param mapper the Jackson ObjectMapper for marshaling the POJO classes
63+
* @param pojoClasses the POJO classes for which this factory provides a handle
64+
* @return the factory
65+
*/
66+
static public ContentHandleFactory newFactory(ObjectMapper mapper, Class<?>... pojoClasses) {
67+
if (mapper == null || pojoClasses == null || pojoClasses.length == 0) return null;
68+
return new JacksonDatabindHandleFactory(mapper, pojoClasses);
69+
}
70+
4671
/**
4772
* Specify the type of content this JacksonDatabindHandle will manage.
4873
*
@@ -126,26 +151,34 @@ public void write(OutputStream out) throws IOException {
126151
}
127152

128153
static private class JacksonDatabindHandleFactory implements ContentHandleFactory {
129-
private Class<?> contentClass;
154+
private Class<?>[] contentClasses;
155+
private ObjectMapper mapper = null;
156+
private Set<Class<?>> classSet;
157+
158+
private JacksonDatabindHandleFactory(Class<?>... contentClasses) {
159+
this(null, contentClasses);
160+
}
130161

131-
private JacksonDatabindHandleFactory(Class<?> contentClass) {
162+
private JacksonDatabindHandleFactory(ObjectMapper mapper, Class<?>... contentClasses) {
132163
super();
133-
this.contentClass = contentClass;
164+
this.contentClasses = contentClasses;
165+
this.mapper = mapper;
166+
this.classSet = new HashSet<Class<?>>(Arrays.asList(contentClasses));
134167
}
135168

136169
@Override
137170
public Class<?>[] getHandledClasses() {
138-
return new Class<?>[] { contentClass };
171+
return contentClasses;
139172
}
140173
@Override
141174
public boolean isHandled(Class<?> type) {
142-
return contentClass.isAssignableFrom(type);
175+
return classSet.contains(type);
143176
}
144177
@Override
145178
public <C> ContentHandle<C> newHandle(Class<C> type) {
146-
@SuppressWarnings("unchecked")
147-
ContentHandle<C> handle = isHandled(type) ?
148-
(ContentHandle<C>) new JacksonDatabindHandle<C>(type) : null;
179+
if ( ! isHandled(type) ) return null;
180+
JacksonDatabindHandle handle = new JacksonDatabindHandle<C>(type);
181+
if ( mapper != null ) handle.setMapper(mapper);
149182
return handle;
150183
}
151184
}

src/main/java/com/marklogic/client/io/JacksonParserHandle.java

Lines changed: 53 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,9 @@
2626
import com.fasterxml.jackson.core.JsonGenerator;
2727
import com.fasterxml.jackson.core.JsonParseException;
2828
import com.fasterxml.jackson.core.JsonParser;
29-
import com.fasterxml.jackson.databind.JsonMappingException;
30-
import com.fasterxml.jackson.databind.JsonNode;
3129
import com.fasterxml.jackson.databind.ObjectMapper;
3230

3331
import com.marklogic.client.MarkLogicIOException;
34-
import com.marklogic.client.io.marker.BufferableHandle;
3532
import com.marklogic.client.io.marker.ContentHandle;
3633
import com.marklogic.client.io.marker.ContentHandleFactory;
3734
import com.marklogic.client.io.marker.GenericReadHandle;
@@ -48,48 +45,45 @@
4845
*/
4946
// TODO: add link to jackson streaming documentation
5047
public class JacksonParserHandle
51-
extends BaseHandle<InputStream, OperationNotSupported>
52-
implements BufferableHandle, GenericReadHandle,
53-
JSONReadHandle, TextReadHandle, XMLReadHandle, StructureReadHandle
48+
extends JacksonBaseHandle<JsonParser>
49+
implements ContentHandle<JsonParser>
5450
{
5551
private ObjectMapper mapper;
5652
private JsonParser parser = null;
5753
private InputStream content = null;
5854

5955
final static private int BUFFER_SIZE = 8192;
6056

57+
/**
58+
* Creates a factory to create a JacksonParserHandle instance for a JsonParser.
59+
* @return the factory
60+
*/
61+
static public ContentHandleFactory newFactory() {
62+
return new ContentHandleFactory() {
63+
@Override
64+
public Class<?>[] getHandledClasses() {
65+
return new Class<?>[]{ JsonParser.class };
66+
}
67+
@Override
68+
public boolean isHandled(Class<?> type) {
69+
return JsonParser.class.isAssignableFrom(type);
70+
}
71+
@Override
72+
public <C> ContentHandle<C> newHandle(Class<C> type) {
73+
@SuppressWarnings("unchecked")
74+
ContentHandle<C> handle = isHandled(type) ?
75+
(ContentHandle<C>) new JacksonParserHandle() : null;
76+
return handle;
77+
}
78+
};
79+
}
80+
6181
public JacksonParserHandle() {
6282
super();
6383
setFormat(Format.JSON);
64-
setResendable(true);
65-
}
66-
67-
/**
68-
* Returns the mapper used to generate the JsonParser.
69-
* @return the JSON mapper.
70-
*/
71-
public ObjectMapper getMapper() {
72-
if (mapper == null) {
73-
mapper = new ObjectMapper();
74-
mapper.configure(JsonGenerator.Feature.AUTO_CLOSE_TARGET, false);
75-
mapper.configure(JsonParser.Feature.AUTO_CLOSE_SOURCE, false);
76-
}
77-
return mapper;
84+
setResendable(false);
7885
}
7986

80-
/**
81-
* Enables clients to use any mapper, including databinding mappers for formats other than JSON.
82-
* Use at your own risk! Note that you may want to configure your mapper as we do to not close
83-
* streams which we may need to reuse if we have to resend a network request:
84-
* <code>
85-
* mapper.configure(JsonGenerator.Feature.AUTO_CLOSE_TARGET, false);
86-
* mapper.configure(JsonParser.Feature.AUTO_CLOSE_SOURCE, false);
87-
* </code>
88-
**/
89-
public void setMapper(ObjectMapper mapper) {
90-
this.mapper = mapper;
91-
}
92-
9387
/**
9488
* Specifies the format of the content and returns the handle
9589
* as a fluent convenience.
@@ -104,7 +98,7 @@ public JacksonParserHandle withFormat(Format format) {
10498
/**
10599
* JsonParser allows streaming access to content as it arrives.
106100
*/
107-
public JsonParser getParser() {
101+
public JsonParser get() {
108102
if ( parser == null ) {
109103
if ( content == null ) {
110104
throw new IllegalStateException("Handle is not yet populated with content");
@@ -119,49 +113,43 @@ public JsonParser getParser() {
119113
}
120114
return parser;
121115
}
122-
123-
@Override
124-
protected Class<InputStream> receiveAs() {
125-
return InputStream.class;
116+
/**
117+
* Available for the edge case that content from a JsonParser must be written.
118+
*/
119+
public void set(JsonParser parser) {
120+
this.parser = parser;
121+
if ( parser == null ) {
122+
content = null;
123+
} else if ( parser.getInputSource() instanceof InputStream ) {
124+
content = (InputStream) parser.getInputSource();
125+
}
126126
}
127127

128128
@Override
129129
protected void receiveContent(InputStream content) {
130-
if (content == null) return;
131130
this.content = content;
131+
if (content == null) parser = null;
132132
}
133133
protected boolean hasContent() {
134-
return content != null;
134+
return content != null || parser != null;
135135
}
136-
@Override
137-
public void fromBuffer(byte[] buffer) {
138-
if (buffer == null || buffer.length == 0) {
139-
content = null;
140-
} else {
141-
receiveContent(new ByteArrayInputStream(buffer));
142-
}
143-
}
144-
@Override
145-
public byte[] toBuffer() {
136+
@Override
137+
public void write(OutputStream out) throws IOException {
146138
try {
147-
if (content == null) return null;
148-
149-
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
150-
151-
byte[] b = new byte[BUFFER_SIZE];
152-
int len = 0;
153-
while ((len = content.read(b)) != -1) {
154-
buffer.write(b, 0, len);
139+
if ( parser != null && parser.nextToken() != null ) {
140+
JsonGenerator generator = getMapper().getFactory().createGenerator(out);
141+
generator.copyCurrentStructure(parser);
142+
generator.close();
143+
} else if (content != null) {
144+
byte[] b = new byte[BUFFER_SIZE];
145+
int len = 0;
146+
while ((len = content.read(b)) != -1) {
147+
out.write(b, 0, len);
148+
}
149+
content.close();
155150
}
156-
content.close();
157-
158-
b = buffer.toByteArray();
159-
fromBuffer(b);
160-
161-
return b;
162151
} catch (IOException e) {
163152
throw new MarkLogicIOException(e);
164153
}
165154
}
166155
}
167-

src/main/javadoc/overview.html

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -287,7 +287,6 @@ <h2>Registering New IO Representations for Shortcut Methods</h2>
287287
To use extra IO handles like
288288
{@link com.marklogic.client.extra.dom4j.DOM4JHandle},
289289
{@link com.marklogic.client.extra.gson.GSONHandle},
290-
{@link com.marklogic.client.extra.jackson.JacksonHandle},
291290
{@link com.marklogic.client.extra.jdom.JDOMHandle}, or
292291
{@link com.marklogic.client.extra.xom.XOMHandle}, you download the
293292
library integrated by the handle and add the library to the classpath.
@@ -296,10 +295,11 @@ <h2>Registering New IO Representations for Shortcut Methods</h2>
296295
using {@link com.marklogic.client.DatabaseClientFactory}.getHandleRegistry().
297296
</p>
298297
<p>
299-
You can also register a factory for the built-in JAXB handle to support
300-
IO on your POJO classes in shortcut methods. The following example
298+
You can also register a factory for the built-in JAXB and JacksonDatabind handles
299+
to support IO on your POJO classes in shortcut methods. The following example
301300
registers the JAXB handle for one or more POJO classes and writes
302-
and reads the POJOs as database documents.
301+
and reads the POJOs as database documents. The same pattern works with
302+
JacksonDatabindHandle.
303303
</p>
304304
<pre>
305305
// configure once before creating a client

src/test/java/com/marklogic/client/test/BulkReadWriteTest.java

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@
3838
import org.junit.BeforeClass;
3939
import org.junit.Test;
4040

41-
import com.marklogic.client.DatabaseClientFactory;
4241
import com.marklogic.client.document.DocumentDescriptor;
4342
import com.marklogic.client.document.DocumentPage;
4443
import com.marklogic.client.document.DocumentRecord;
@@ -278,11 +277,6 @@ private class BulkCityWriter implements CityWriter {
278277
private DocumentWriteSet writeSet = docMgr.newWriteSet();
279278

280279
BulkCityWriter() throws JAXBException {
281-
// register the POJO class
282-
DatabaseClientFactory.getHandleRegistry().register(
283-
JAXBHandle.newFactory(City.class)
284-
);
285-
286280
context = JAXBContext.newInstance(City.class);
287281
}
288282

0 commit comments

Comments
 (0)