-
Notifications
You must be signed in to change notification settings - Fork 2.4k
Add a streaming Json item reader #607
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
9f4c4ec
to
284e0b5
Compare
c04d17f
to
56e9720
Compare
This commit adds a new Json item reader with two implementations based on Jackson and Gson. Resolves BATCH-2691
|
||
@Override | ||
protected Class<? extends Exception> getJsonParsingException() { | ||
return JsonSyntaxException.class; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These exceptions being exposed outside the impl is a leaky abstraction. We should be catching the implementation's exception and wrapping it in a standard exception. Using the Marshaller
as an example, it throws an XmlMappingException
if there is an issue and that would wrap the underlying implementation's exception.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree! Good catch. The best exception I see as a wrapper is org.springframework.batch.item.ParseException
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in 53262e6. Now the implementation detail exception is wrapped in a ParseException
.
public void afterPropertiesSet() throws Exception { | ||
Assert.notNull(this.jsonObjectReader, "The json object reader must not be null."); | ||
Assert.notNull(this.resource, "The resource must not be null."); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This really is an old school way of doing things. We should require these in the constructor if possible.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For the StaxEventItemReader
, both the resource and unmarshaller are mandatory resources but they are not required at construction time (they are checked in afterPropertiesSet
). I've seen this in many places in the code base like for example the ValidatingItemProcessor
where I can create an instance with the default constructor while the validator is mandatory. Another example is the JmsItemReader
: the jmsTemplate
is mandatory but it is not required in the constructor.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When XML was the preferred way to configure things, setter injection and using InitializingBean
made sense (since java doesn't have named parameters). However, with java config the norm now, we should take advantage of that and utilize constructor injection where we can. Also, given the builders will be used in most cases going forward, there's even less reason to take this approach.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's more about class design than configuration style: I should not be able to create a class without specifying its required dependencies (fail fast, at construction time) rather than letting me create the class and then throw me an exception when the container calls afterPropertiesSet
. Here, I used the InitializingBean
way for consistency with the current code base, but for me, we should always use constructor injection for required dependencies and setter injection for optional ones regardless of the configuration style. I couldn't agree more with you, so I fixed that in 493196c.
* @since 4.1 | ||
*/ | ||
public class JsonItemReader<T> extends AbstractItemCountingItemStreamItemReader<T> implements | ||
ResourceAwareItemReaderItemStream<T>, InitializingBean { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since you're already bringing in the ItemStream
and the ItemReader
interfaces via the AbstractItemCountingItemStreamItemReader
, you can probably just implement ResourceAware
(no need to use the composite).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I took the same as StaxEventItemWriter
.
* @param resource the input resource | ||
* @throws Exception if unable to open the resource | ||
*/ | ||
default void open(Resource resource) throws Exception { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We try not to throw Exception
unless there is a real reason to do so in an API. We'd rather throw a documented RuntimeException
if possible which this feels like it should be able to.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here I think there is a real reason to do so. If the implementation cannot open the resource, we gave it a chance to throw a checked exception and signal the problem to the framework. We do the same in org.springframework.batch.item.support.AbstractItemCountingItemStreamItemReader#doOpen
. The javadoc makes sense to me: @throws Exception Allows subclasses to throw checked exceptions for interpretation by the framework
.
* @return the next object or null if the resource is exhausted | ||
* @throws Exception if unable to read the next object | ||
*/ | ||
T read() throws Exception; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See above about throwing Exception
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same as previous comment on doOpen
. We throw an exception in org.springframework.batch.item.support.AbstractItemCountingItemStreamItemReader#doRead
and org.springframework.batch.item.ItemReader#read
.
* Close the input resource. | ||
* @throws Exception if unable to close the input resource | ||
*/ | ||
default void close() throws Exception { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See above about throwing Exception
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same as org.springframework.batch.item.support.AbstractItemCountingItemStreamItemReader#doClose
.
new JsonItemReaderBuilder<String>() | ||
.jsonObjectReader(this.jsonObjectReader) | ||
.resource(this.resource) | ||
.saveState(true) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This line is redundant isn't it (save state defaults to true
)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought being explicit is better for this specific test case. But I will remove it, I agree it is not really necessary.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed in 5b6e7b9
wrapping them in a ParseException
LGTM. Rebased, squashed, and merged as 5281d6d |
This PR adds a new Json item reader with two implementations based on Jackson and gson.
Resolves BATCH-2691.