001/*
002 * Copyright 2012-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 */
016package org.springframework.batch.core.job;
017
018import java.util.Arrays;
019import java.util.Collection;
020import java.util.HashSet;
021import java.util.Set;
022
023import org.springframework.batch.core.JobParameters;
024import org.springframework.batch.core.JobParametersInvalidException;
025import org.springframework.batch.core.JobParametersValidator;
026import org.springframework.beans.factory.InitializingBean;
027import org.springframework.lang.Nullable;
028import org.springframework.util.Assert;
029
030/**
031 * Default implementation of {@link JobParametersValidator}.
032 *
033 * @author Dave Syer
034 * @author Mahmoud Ben Hassine
035 *
036 */
037public class DefaultJobParametersValidator implements JobParametersValidator, InitializingBean {
038
039        private Collection<String> requiredKeys;
040
041        private Collection<String> optionalKeys;
042
043        /**
044         * Convenient default constructor for unconstrained validation.
045         */
046        public DefaultJobParametersValidator() {
047                this(new String[0], new String[0]);
048        }
049
050        /**
051         * Create a new validator with the required and optional job parameter keys
052         * provided.
053         *
054         * @see DefaultJobParametersValidator#setOptionalKeys(String[])
055         * @see DefaultJobParametersValidator#setRequiredKeys(String[])
056         *
057         * @param requiredKeys the required keys
058         * @param optionalKeys the optional keys
059         */
060        public DefaultJobParametersValidator(String[] requiredKeys, String[] optionalKeys) {
061                super();
062                setRequiredKeys(requiredKeys);
063                setOptionalKeys(optionalKeys);
064        }
065
066        /**
067         * Check that there are no overlaps between required and optional keys.
068         * @throws IllegalStateException if there is an overlap
069         */
070        @Override
071        public void afterPropertiesSet() throws IllegalStateException {
072                for (String key : requiredKeys) {
073                        Assert.state(!optionalKeys.contains(key), "Optional keys cannot be required: " + key);
074                }
075        }
076
077        /**
078         * Check the parameters meet the specification provided. If optional keys
079         * are explicitly specified then all keys must be in that list, or in the
080         * required list. Otherwise all keys that are specified as required must be
081         * present.
082         *
083         * @see JobParametersValidator#validate(JobParameters)
084         *
085         * @throws JobParametersInvalidException if the parameters are not valid
086         */
087        @Override
088        public void validate(@Nullable JobParameters parameters) throws JobParametersInvalidException {
089
090                if (parameters == null) {
091                        throw new JobParametersInvalidException("The JobParameters can not be null");
092                }
093
094                Set<String> keys = parameters.getParameters().keySet();
095
096                // If there are explicit optional keys then all keys must be in that
097                // group, or in the required group.
098                if (!optionalKeys.isEmpty()) {
099
100                        Collection<String> missingKeys = new HashSet<String>();
101                        for (String key : keys) {
102                                if (!optionalKeys.contains(key) && !requiredKeys.contains(key)) {
103                                        missingKeys.add(key);
104                                }
105                        }
106                        if (!missingKeys.isEmpty()) {
107                                throw new JobParametersInvalidException(
108                                                "The JobParameters contains keys that are not explicitly optional or required: " + missingKeys);
109                        }
110
111                }
112
113                Collection<String> missingKeys = new HashSet<String>();
114                for (String key : requiredKeys) {
115                        if (!keys.contains(key)) {
116                                missingKeys.add(key);
117                        }
118                }
119                if (!missingKeys.isEmpty()) {
120                        throw new JobParametersInvalidException("The JobParameters do not contain required keys: " + missingKeys);
121                }
122
123        }
124
125        /**
126         * The keys that are required in the parameters. The default is empty,
127         * meaning that all parameters are optional, unless optional keys are
128         * explicitly specified.
129         *
130         * @param requiredKeys the required key values
131         *
132         * @see #setOptionalKeys(String[])
133         */
134        public final void setRequiredKeys(String[] requiredKeys) {
135                this.requiredKeys = new HashSet<String>(Arrays.asList(requiredKeys));
136        }
137
138        /**
139         * The keys that are optional in the parameters. If any keys are explicitly
140         * optional, then to be valid all other keys must be explicitly required.
141         * The default is empty, meaning that all parameters that are not required
142         * are optional.
143         *
144         * @param optionalKeys the optional key values
145         *
146         * @see #setRequiredKeys(String[])
147         */
148        public final void setOptionalKeys(String[] optionalKeys) {
149                this.optionalKeys = new HashSet<String>(Arrays.asList(optionalKeys));
150        }
151
152}