001/*
002 * Copyright 2013-2014 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.jsr.configuration.support;
017
018import java.util.Enumeration;
019import java.util.HashMap;
020import java.util.Map;
021import java.util.Properties;
022
023import org.springframework.util.Assert;
024
025/**
026 * <p>
027 * Context object to hold parsed JSR-352 batch properties, mapping properties to beans /
028 * "batch artifacts". Used internally when parsing property tags from a batch configuration
029 * file and to obtain corresponding values when injecting into batch artifacts.
030 * </p>
031 *
032 * @author Chris Schaefer
033 * @author Michael Minella
034 * @since 3.0
035 */
036public class BatchPropertyContext {
037        private static final String PARTITION_INDICATOR = ":partition";
038
039        private Properties jobProperties = new Properties();
040        private Map<String, Properties> stepProperties = new HashMap<String, Properties>();
041        private Map<String, Properties> artifactProperties = new HashMap<String, Properties>();
042        private Map<String, Map<String, Properties>> stepArtifactProperties = new HashMap<String, Map<String, Properties>>();
043
044        /**
045         * <p>
046         * Obtains the Job level properties.
047         * </p>
048         *
049         * @return the Job level properties
050         */
051        public Properties getJobProperties() {
052                return jobProperties;
053        }
054
055        /**
056         * <p>
057         * Adds Job level properties to the context.
058         * </p>
059         *
060         * @param properties the job {@link Properties} to add
061         */
062        public void setJobProperties(Properties properties) {
063                Assert.notNull(properties, "Job properties cannot be null");
064                this.jobProperties.putAll(properties);
065        }
066
067        /**
068         * <p>
069         * Obtains the Step level properties for the provided Step name.
070         * </p>
071         *
072         * @param stepName the Step name to obtain properties for
073         * @return the {@link Properties} for the Step
074         */
075        public Properties getStepProperties(String stepName) {
076                Assert.hasText(stepName, "Step name must be provided");
077                Properties properties = new Properties();
078
079                if(stepProperties.containsKey(stepName)) {
080                        properties.putAll(stepProperties.get(stepName));
081                }
082
083                if(stepName.contains(PARTITION_INDICATOR)) {
084                        String parentStepName = stepName.substring(0, stepName.indexOf(PARTITION_INDICATOR));
085                        properties.putAll(getStepProperties(parentStepName));
086                }
087
088                return properties;
089        }
090
091        /**
092         * <p>
093         * Adds Step level properties to the context.
094         * </p>
095         *
096         * @param properties the step {@link Properties} to add
097         */
098        public void setStepProperties(Map<String, Properties> properties) {
099                Assert.notNull(properties, "Step properties cannot be null");
100
101                for(Map.Entry<String, Properties> propertiesEntry : properties.entrySet()) {
102                        String stepName = propertiesEntry.getKey();
103                        Properties stepProperties = propertiesEntry.getValue();
104
105                        if (!stepProperties.isEmpty()) {
106                                if (this.stepProperties.containsKey(stepName)) {
107                                        Properties existingStepProperties = this.stepProperties.get(stepName);
108
109                                        Enumeration<?> stepPropertyNames = stepProperties.propertyNames();
110
111                                        while(stepPropertyNames.hasMoreElements()) {
112                                                String propertyEntryName = (String) stepPropertyNames.nextElement();
113                                                existingStepProperties.put(propertyEntryName, stepProperties.getProperty(propertyEntryName));
114                                        }
115
116                                        this.stepProperties.put(stepName, existingStepProperties);
117                                } else {
118                                        this.stepProperties.put(stepName, propertiesEntry.getValue());
119                                }
120                        }
121                }
122        }
123
124        /**
125         * <p>
126         * Convenience method to set step level properties. Simply wraps the provided parameters
127         * and delegates to {@link #setStepProperties(java.util.Map)}.
128         * </p>
129         *
130         * @param stepName the step name to set {@link Properties} for
131         * @param properties the {@link Properties} to set
132         */
133        public void setStepProperties(String stepName, Properties properties) {
134                Assert.hasText(stepName, "Step name must be provided");
135                Assert.notNull(properties, "Step properties must not be null");
136
137                Map<String, Properties> stepProperties = new HashMap<String, Properties>();
138                stepProperties.put(stepName, properties);
139
140                setStepProperties(stepProperties);
141        }
142
143        /**
144         * <p>
145         * Obtains the batch {@link Properties} for the provided artifact name.
146         * </p>
147         *
148         * @param artifactName the batch artifact to obtain properties for
149         * @return the {@link Properties} for the provided batch artifact
150         */
151        public Properties getArtifactProperties(String artifactName) {
152                Properties properties = new Properties();
153
154                if (artifactProperties.containsKey(artifactName)) {
155                        properties.putAll(artifactProperties.get(artifactName));
156                }
157
158                return properties;
159        }
160
161        /**
162         * <p>
163         * Adds non-step artifact properties to the context.
164         * </p>
165         *
166         * @param properties the artifact {@link Properties} to add
167         */
168        public void setArtifactProperties(Map<String, Properties> properties) {
169                Assert.notNull(properties, "Step properties cannot be null");
170
171                for(Map.Entry<String, Properties> propertiesEntry : properties.entrySet()) {
172                        String artifactName = propertiesEntry.getKey();
173                        Properties artifactProperties = propertiesEntry.getValue();
174
175                        if(!artifactProperties.isEmpty()) {
176                                this.artifactProperties.put(artifactName, artifactProperties);
177                        }
178                }
179        }
180
181        /**
182         * <p>
183         * Obtains the batch {@link Properties} for the provided Step and artifact name.
184         * </p>
185         *
186         * @param stepName the Step name the artifact is associated with
187         * @param artifactName the artifact name to obtain {@link Properties} for
188         * @return the {@link Properties} for the provided Step artifact
189         */
190        public Properties getStepArtifactProperties(String stepName, String artifactName) {
191                Properties properties = new Properties();
192                properties.putAll(getStepProperties(stepName));
193
194                Map<String, Properties> artifactProperties = stepArtifactProperties.get(stepName);
195
196                if (artifactProperties != null && artifactProperties.containsKey(artifactName)) {
197                        properties.putAll(artifactProperties.get(artifactName));
198                }
199
200                if(stepName.contains(PARTITION_INDICATOR)) {
201                        String parentStepName = stepName.substring(0, stepName.indexOf(PARTITION_INDICATOR));
202                        properties.putAll(getStepProperties(parentStepName));
203
204                        Map<String, Properties> parentArtifactProperties = stepArtifactProperties.get(parentStepName);
205
206                        if (parentArtifactProperties != null && parentArtifactProperties.containsKey(artifactName)) {
207                                properties.putAll(parentArtifactProperties.get(artifactName));
208                        }
209                }
210
211                return properties;
212        }
213
214        /**
215         * <p>
216         * Adds Step artifact properties to the context.
217         * </p>
218         *
219         * @param properties the step artifact {@link Properties} to add
220         */
221        @SuppressWarnings("serial")
222        public void setStepArtifactProperties(Map<String, Map<String, Properties>> properties) {
223                Assert.notNull(properties, "Step artifact properties cannot be null");
224
225                for(Map.Entry<String, Map<String, Properties>> propertyEntries : properties.entrySet()) {
226                        String stepName = propertyEntries.getKey();
227
228                        for(Map.Entry<String, Properties> artifactEntries : propertyEntries.getValue().entrySet()) {
229                                final String artifactName = artifactEntries.getKey();
230                                final Properties props = artifactEntries.getValue();
231
232                                Map<String, Properties> artifactProperties = stepArtifactProperties.get(stepName);
233
234                                if (artifactProperties == null) {
235                                        stepArtifactProperties.put(stepName, new HashMap<String, Properties>() {{
236                                                put(artifactName, props);
237                                        }});
238                                } else {
239                                        artifactProperties.put(artifactName, props);
240                                }
241                        }
242                }
243        }
244}