001/*
002 * Copyright 2018-2020 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *       https://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.springframework.batch.item.json;
018
019import org.apache.commons.logging.Log;
020import org.apache.commons.logging.LogFactory;
021
022import org.springframework.batch.item.ItemStreamReader;
023import org.springframework.batch.item.file.ResourceAwareItemReaderItemStream;
024import org.springframework.batch.item.support.AbstractItemCountingItemStreamItemReader;
025import org.springframework.core.io.Resource;
026import org.springframework.util.Assert;
027import org.springframework.util.ClassUtils;
028
029/**
030 * {@link ItemStreamReader} implementation that reads Json objects from a
031 * {@link Resource} having the following format:
032 * <p>
033 * <code>
034 * [
035 *    {
036 *       // JSON object
037 *    },
038 *    {
039 *       // JSON object
040 *    }
041 * ]
042 * </code>
043 * <p>
044 *
045 * The implementation is <b>not</b> thread-safe.
046 *
047 * @param <T> the type of json objects to read
048 *
049 * @author Mahmoud Ben Hassine
050 * @since 4.1
051 */
052public class JsonItemReader<T> extends AbstractItemCountingItemStreamItemReader<T> implements
053                ResourceAwareItemReaderItemStream<T> {
054
055        private static final Log LOGGER = LogFactory.getLog(JsonItemReader.class);
056
057        private Resource resource;
058
059        private JsonObjectReader<T> jsonObjectReader;
060
061        private boolean strict = true;
062
063        /**
064         * Create a new {@link JsonItemReader} instance.
065         * @param resource the input json resource
066         * @param jsonObjectReader the json object reader to use
067         */
068        public JsonItemReader(Resource resource, JsonObjectReader<T> jsonObjectReader) {
069                Assert.notNull(resource, "The resource must not be null.");
070                Assert.notNull(jsonObjectReader, "The json object reader must not be null.");
071                this.resource = resource;
072                this.jsonObjectReader = jsonObjectReader;
073                setExecutionContextName(ClassUtils.getShortName(JsonItemReader.class));
074        }
075
076        /**
077         * Set the {@link JsonObjectReader} to use to read and map Json fragments to domain objects.
078         * @param jsonObjectReader the json object reader to use
079         */
080        public void setJsonObjectReader(JsonObjectReader<T> jsonObjectReader) {
081                this.jsonObjectReader = jsonObjectReader;
082        }
083
084        /**
085         * In strict mode the reader will throw an exception on
086         * {@link #open(org.springframework.batch.item.ExecutionContext)} if the
087         * input resource does not exist.
088         * @param strict true by default
089         */
090        public void setStrict(boolean strict) {
091                this.strict = strict;
092        }
093
094        @Override
095        public void setResource(Resource resource) {
096                this.resource = resource;
097        }
098
099        @Override
100        protected T doRead() throws Exception {
101                return jsonObjectReader.read();
102        }
103
104        @Override
105        protected void doOpen() throws Exception {
106                if (!this.resource.exists()) {
107                        if (this.strict) {
108                                throw new IllegalStateException("Input resource must exist (reader is in 'strict' mode)");
109                        }
110                        LOGGER.warn("Input resource does not exist " + this.resource.getDescription());
111                        return;
112                }
113                if (!this.resource.isReadable()) {
114                        if (this.strict) {
115                                throw new IllegalStateException("Input resource must be readable (reader is in 'strict' mode)");
116                        }
117                        LOGGER.warn("Input resource is not readable " + this.resource.getDescription());
118                        return;
119                }
120                this.jsonObjectReader.open(this.resource);
121        }
122
123        @Override
124        protected void doClose() throws Exception {
125                this.jsonObjectReader.close();
126        }
127
128}