001/*
002 * Copyright 2017-2018 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.data.builder;
018
019import java.util.List;
020import java.util.Map;
021
022import org.springframework.batch.item.data.MongoItemReader;
023import org.springframework.data.domain.Sort;
024import org.springframework.data.mongodb.core.MongoOperations;
025import org.springframework.data.mongodb.core.query.Query;
026import org.springframework.util.Assert;
027import org.springframework.util.StringUtils;
028
029/**
030 * A builder implementation for the {@link MongoItemReader}
031 *
032 * @author Glenn Renfro
033 * @author Mahmoud Ben Hassine
034 * @since 4.0
035 * @see MongoItemReader
036 */
037public class MongoItemReaderBuilder<T> {
038        private MongoOperations template;
039
040        private String jsonQuery;
041
042        private Class<? extends T> targetType;
043
044        Map<String, Sort.Direction> sorts;
045
046        private String hint;
047
048        private String fields;
049
050        private String collection;
051
052        private List<Object> parameterValues;
053
054        protected int pageSize = 10;
055
056        private boolean saveState = true;
057
058        private String name;
059
060        private int maxItemCount = Integer.MAX_VALUE;
061
062        private int currentItemCount;
063
064        private Query query;
065
066        /**
067         * Configure if the state of the {@link org.springframework.batch.item.ItemStreamSupport}
068         * should be persisted within the {@link org.springframework.batch.item.ExecutionContext}
069         * for restart purposes.
070         *
071         * @param saveState defaults to true
072         * @return The current instance of the builder.
073         */
074        public MongoItemReaderBuilder<T> saveState(boolean saveState) {
075                this.saveState = saveState;
076
077                return this;
078        }
079
080        /**
081         * The name used to calculate the key within the
082         * {@link org.springframework.batch.item.ExecutionContext}. Required if
083         * {@link #saveState(boolean)} is set to true.
084         *
085         * @param name name of the reader instance
086         * @return The current instance of the builder.
087         * @see org.springframework.batch.item.ItemStreamSupport#setName(String)
088         */
089        public MongoItemReaderBuilder<T> name(String name) {
090                this.name = name;
091
092                return this;
093        }
094
095        /**
096         * Configure the max number of items to be read.
097         *
098         * @param maxItemCount the max items to be read
099         * @return The current instance of the builder.
100         * @see org.springframework.batch.item.support.AbstractItemCountingItemStreamItemReader#setMaxItemCount(int)
101         */
102        public MongoItemReaderBuilder<T> maxItemCount(int maxItemCount) {
103                this.maxItemCount = maxItemCount;
104
105                return this;
106        }
107
108        /**
109         * Index for the current item. Used on restarts to indicate where to start from.
110         *
111         * @param currentItemCount current index
112         * @return this instance for method chaining
113         * @see org.springframework.batch.item.support.AbstractItemCountingItemStreamItemReader#setCurrentItemCount(int)
114         */
115        public MongoItemReaderBuilder<T> currentItemCount(int currentItemCount) {
116                this.currentItemCount = currentItemCount;
117
118                return this;
119        }
120
121        /**
122         * Used to perform operations against the MongoDB instance. Also handles the mapping
123         * of documents to objects.
124         *
125         * @param template the MongoOperations instance to use
126         * @see MongoOperations
127         * @return The current instance of the builder
128         * @see MongoItemReader#setTemplate(MongoOperations)
129         */
130        public MongoItemReaderBuilder<T> template(MongoOperations template) {
131                this.template = template;
132
133                return this;
134        }
135
136        /**
137         * A JSON formatted MongoDB jsonQuery. Parameterization of the provided jsonQuery is allowed
138         * via ?&lt;index&gt; placeholders where the &lt;index&gt; indicates the index of the
139         * parameterValue to substitute.
140         *
141         * @param query JSON formatted Mongo jsonQuery
142         * @return The current instance of the builder
143         * @see MongoItemReader#setQuery(String)
144         */
145        public MongoItemReaderBuilder<T> jsonQuery(String query) {
146                this.jsonQuery = query;
147
148                return this;
149        }
150
151        /**
152         * The type of object to be returned for each {@link MongoItemReader#read()} call.
153         *
154         * @param targetType the type of object to return
155         * @return The current instance of the builder
156         * @see MongoItemReader#setTargetType(Class)
157         */
158        public MongoItemReaderBuilder<T> targetType(Class<? extends T> targetType) {
159                this.targetType = targetType;
160
161                return this;
162        }
163
164        /**
165         * {@link List} of values to be substituted in for each of the parameters in the
166         * query.
167         *
168         * @param parameterValues values
169         * @return The current instance of the builder
170         * @see MongoItemReader#setParameterValues(List)
171         */
172        public MongoItemReaderBuilder<T> parameterValues(List<Object> parameterValues) {
173                this.parameterValues = parameterValues;
174
175                return this;
176        }
177
178        /**
179         * JSON defining the fields to be returned from the matching documents by MongoDB.
180         *
181         * @param fields JSON string that identifies the fields to sort by.
182         * @return The current instance of the builder
183         * @see MongoItemReader#setFields(String)
184         */
185        public MongoItemReaderBuilder<T> fields(String fields) {
186                this.fields = fields;
187
188                return this;
189        }
190
191        /**
192         * {@link Map} of property
193         * names/{@link org.springframework.data.domain.Sort.Direction} values to sort the
194         * input by.
195         *
196         * @param sorts map of properties and direction to sort each.
197         * @return The current instance of the builder
198         * @see MongoItemReader#setSort(Map)
199         */
200        public MongoItemReaderBuilder<T> sorts(Map<String, Sort.Direction> sorts) {
201                this.sorts = sorts;
202
203                return this;
204        }
205
206        /**
207         * Establish an optional collection that can be queried.
208         *
209         * @param collection Mongo collection to be queried.
210         * @return The current instance of the builder
211         * @see MongoItemReader#setCollection(String)
212         */
213        public MongoItemReaderBuilder<T> collection(String collection) {
214                this.collection = collection;
215
216                return this;
217        }
218
219        /**
220         * JSON String telling MongoDB what index to use.
221         *
222         * @param hint string indicating what index to use.
223         * @return The current instance of the builder
224         * @see MongoItemReader#setHint(String)
225         */
226        public MongoItemReaderBuilder<T> hint(String hint) {
227                this.hint = hint;
228
229                return this;
230        }
231
232        /**
233         * The number of items to be read with each page.
234         *
235         * @param pageSize the number of items
236         * @return this instance for method chaining
237         * @see MongoItemReader#setPageSize(int)
238         */
239        public MongoItemReaderBuilder<T> pageSize(int pageSize) {
240                this.pageSize = pageSize;
241
242                return this;
243        }
244
245        /**
246         * Provide a Spring Data Mongo {@link Query}.  This will take precedence over a JSON
247         * configured query.
248         *
249         * @param query Query to execute
250         * @return this instance for method chaining
251         * @see MongoItemReader#setQuery(Query)
252         */
253        public MongoItemReaderBuilder<T> query(Query query) {
254                this.query = query;
255
256                return this;
257        }
258
259        /**
260         * Validates and builds a {@link MongoItemReader}.
261         *
262         * @return a {@link MongoItemReader}
263         */
264        public MongoItemReader<T> build() {
265                Assert.notNull(this.template, "template is required.");
266                if (this.saveState) {
267                        Assert.hasText(this.name, "A name is required when saveState is set to true");
268                }
269                Assert.notNull(this.targetType, "targetType is required.");
270                Assert.state(StringUtils.hasText(this.jsonQuery) || this.query != null, "A query is required");
271
272                if(StringUtils.hasText(this.jsonQuery)) {
273                        Assert.notNull(this.sorts, "sorts map is required.");
274                }
275                else {
276                        Assert.state(this.query.getLimit() != 0, "PageSize in Query object was ignored.");
277                }
278
279                MongoItemReader<T> reader = new MongoItemReader<>();
280                reader.setTemplate(this.template);
281                reader.setTargetType(this.targetType);
282                reader.setQuery(this.jsonQuery);
283                reader.setSort(this.sorts);
284                reader.setHint(this.hint);
285                reader.setFields(this.fields);
286                reader.setCollection(this.collection);
287                reader.setParameterValues(this.parameterValues);
288                reader.setQuery(this.query);
289
290                reader.setPageSize(this.pageSize);
291                reader.setName(this.name);
292                reader.setSaveState(this.saveState);
293                reader.setCurrentItemCount(this.currentItemCount);
294                reader.setMaxItemCount(this.maxItemCount);
295
296                return reader;
297        }
298}