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.xml;
017
018import java.util.HashMap;
019import java.util.List;
020import java.util.Map;
021import java.util.Properties;
022
023import org.springframework.batch.core.jsr.configuration.support.BatchArtifactType;
024import org.springframework.beans.factory.config.BeanDefinition;
025import org.springframework.beans.factory.support.ManagedMap;
026import org.springframework.beans.factory.xml.ParserContext;
027import org.springframework.util.xml.DomUtils;
028import org.w3c.dom.Element;
029
030/**
031 * <p>
032 * Parser for the &lt;properties /&gt; element defined by JSR-352.
033 * </p>
034 *
035 * @author Chris Schaefer
036 * @since 3.0
037 */
038public class PropertyParser {
039        private static final String PROPERTY_ELEMENT = "property";
040        private static final String PROPERTIES_ELEMENT = "properties";
041        private static final String PROPERTY_NAME_ATTRIBUTE = "name";
042        private static final String PROPERTY_VALUE_ATTRIBUTE = "value";
043        private static final String JOB_PROPERTIES_BEAN_NAME = "jobProperties";
044        private static final String BATCH_PROPERTY_CONTEXT_BEAN_NAME = "batchPropertyContext";
045        private static final String JOB_PROPERTIES_PROPERTY_NAME = "jobProperties";
046        private static final String STEP_PROPERTIES_PROPERTY_NAME = "stepProperties";
047        private static final String ARTIFACT_PROPERTIES_PROPERTY_NAME = "artifactProperties";
048        private static final String STEP_ARTIFACT_PROPERTIES_PROPERTY_NAME = "stepArtifactProperties";
049
050        private String beanName;
051        private String stepName;
052        private ParserContext parserContext;
053        private BatchArtifactType batchArtifactType;
054
055        public PropertyParser(String beanName, ParserContext parserContext, BatchArtifactType batchArtifactType) {
056                this.beanName = beanName;
057                this.parserContext = parserContext;
058                this.batchArtifactType = batchArtifactType;
059        }
060
061        public PropertyParser(String beanName, ParserContext parserContext, BatchArtifactType batchArtifactType, String stepName) {
062                this(beanName, parserContext, batchArtifactType);
063                this.stepName = stepName;
064        }
065
066        /**
067         * <p>
068         * Parses &lt;property&gt; tag values from the provided {@link Element} if it contains a &lt;properties /&gt; element.
069         * Only one &lt;properties /&gt; element may be present. &lt;property&gt; elements have a name and value attribute
070         * which represent the property entries key and value.
071         * </p>
072         *
073         * @param element the element to parse looking for &lt;properties /&gt;
074         */
075        public void parseProperties(Element element) {
076                List<Element> propertiesElements = DomUtils.getChildElementsByTagName(element, PROPERTIES_ELEMENT);
077
078                if (propertiesElements.size() == 1) {
079                        parsePropertyElement(propertiesElements.get(0));
080                } else if (propertiesElements.size() > 1) {
081                        parserContext.getReaderContext().error("The <properties> element may not appear more than once.", element);
082                }
083        }
084
085        /**
086         * <p>
087         * Parses a &lt;property&gt; tag value from the provided {@link Element}. &lt;property&gt; elements have a name and
088         * value attribute which represent the property entries key and value.
089         * </p>
090         *
091         * @param element the element to parse looking for &lt;property/&gt;
092         */
093        public void parseProperty(Element element) {
094                parsePropertyElement(element);
095        }
096
097        private void parsePropertyElement(Element propertyElement) {
098                Properties properties = new Properties();
099
100                for (Element element : DomUtils.getChildElementsByTagName(propertyElement, PROPERTY_ELEMENT)) {
101                        properties.put(element.getAttribute(PROPERTY_NAME_ATTRIBUTE), element.getAttribute(PROPERTY_VALUE_ATTRIBUTE));
102                }
103
104                setProperties(properties);
105                setJobPropertiesBean(properties);
106        }
107
108        private void setProperties(Properties properties) {
109                Object propertyValue;
110                BeanDefinition beanDefinition = parserContext.getRegistry().getBeanDefinition(BATCH_PROPERTY_CONTEXT_BEAN_NAME);
111
112                if(batchArtifactType.equals(BatchArtifactType.JOB)) {
113                        propertyValue = getJobProperties(properties);
114                } else if (batchArtifactType.equals(BatchArtifactType.STEP)) {
115                        propertyValue = getProperties(stepName, properties);
116                } else if (batchArtifactType.equals(BatchArtifactType.ARTIFACT)) {
117                        propertyValue = getProperties(beanName, properties);
118                } else if (batchArtifactType.equals(BatchArtifactType.STEP_ARTIFACT)) {
119                        propertyValue = getStepArtifactProperties(beanDefinition, properties);
120                } else {
121                        throw new IllegalStateException("Unhandled BatchArtifactType of: " + batchArtifactType);
122                }
123
124                beanDefinition.getPropertyValues().addPropertyValue(getPropertyName(batchArtifactType), propertyValue);
125        }
126
127        private Map<String, Properties> getProperties(String keyName, Properties properties) {
128                ManagedMap<String, Properties> stepProperties = new ManagedMap<String, Properties>();
129                stepProperties.setMergeEnabled(true);
130                stepProperties.put(keyName, properties);
131
132                return stepProperties;
133        }
134
135        private Properties getJobProperties(Properties properties) {
136                return properties;
137        }
138
139        @SuppressWarnings("unchecked")
140        private Map<String, Map<String, Properties>> getStepArtifactProperties(BeanDefinition beanDefinition, Properties properties) {
141                ManagedMap<String, Map<String, Properties>> stepArtifacts = new ManagedMap<String, Map<String, Properties>>();
142                stepArtifacts.setMergeEnabled(true);
143
144                Map<String, Map<String, Properties>> existingArtifacts
145                                = (Map<String, Map<String, Properties>>) beanDefinition.getPropertyValues().get(getPropertyName(batchArtifactType));
146
147                ManagedMap<String, Properties> artifactProperties = new ManagedMap<String, Properties>();
148                artifactProperties.setMergeEnabled(true);
149
150                if(existingArtifacts != null && existingArtifacts.containsKey(stepName)) {
151                        Map<String, Properties> existingArtifactsMap = existingArtifacts.get(stepName);
152
153                        for(Map.Entry<String, Properties> existingArtifactEntry : existingArtifactsMap.entrySet()) {
154                                artifactProperties.put(existingArtifactEntry.getKey(), existingArtifactEntry.getValue());
155                        }
156                }
157
158                artifactProperties.put(beanName, properties);
159                stepArtifacts.put(stepName, artifactProperties);
160
161                return stepArtifacts;
162        }
163
164        private void setJobPropertiesBean(Properties properties) {
165                if (batchArtifactType.equals(BatchArtifactType.JOB)) {
166                        Map<String, String> jobProperties = new HashMap<String, String>();
167
168                        if (properties != null && !properties.isEmpty()) {
169                                for (String param : properties.stringPropertyNames()) {
170                                        jobProperties.put(param, properties.getProperty(param));
171                                }
172                        }
173
174                        BeanDefinition jobPropertiesBeanDefinition = parserContext.getRegistry().getBeanDefinition(JOB_PROPERTIES_BEAN_NAME);
175                        jobPropertiesBeanDefinition.getConstructorArgumentValues().addGenericArgumentValue(jobProperties);
176                }
177        }
178
179        private String getPropertyName(BatchArtifactType batchArtifactType) {
180                if(batchArtifactType.equals(BatchArtifactType.JOB)) {
181                        return JOB_PROPERTIES_PROPERTY_NAME;
182                } else if (batchArtifactType.equals(BatchArtifactType.STEP)) {
183                        return STEP_PROPERTIES_PROPERTY_NAME;
184                } else if (batchArtifactType.equals(BatchArtifactType.ARTIFACT)) {
185                        return ARTIFACT_PROPERTIES_PROPERTY_NAME;
186                } else if (batchArtifactType.equals(BatchArtifactType.STEP_ARTIFACT)) {
187                        return STEP_ARTIFACT_PROPERTIES_PROPERTY_NAME;
188                } else {
189                        throw new IllegalStateException("Unhandled BatchArtifactType of: " + batchArtifactType);
190                }
191        }
192}