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.List; 019 020import org.springframework.batch.core.jsr.configuration.support.BatchArtifactType; 021import org.springframework.beans.factory.config.BeanDefinition; 022import org.springframework.beans.factory.config.RuntimeBeanReference; 023import org.springframework.beans.factory.parsing.CompositeComponentDefinition; 024import org.springframework.beans.factory.support.AbstractBeanDefinition; 025import org.springframework.beans.factory.support.BeanDefinitionBuilder; 026import org.springframework.beans.factory.support.BeanDefinitionRegistry; 027import org.springframework.beans.factory.support.ManagedList; 028import org.springframework.beans.factory.xml.ParserContext; 029import org.springframework.util.xml.DomUtils; 030import org.w3c.dom.Element; 031 032/** 033 * Parses the various listeners defined in JSR-352. Current state assumes 034 * the ref attributes point to implementations of Spring Batch interfaces 035 * and not JSR interfaces 036 * 037 * @author Michael Minella 038 * @author Chris Schaefer 039 * @since 3.0 040 */ 041public class ListenerParser { 042 private static final String REF_ATTRIBUTE = "ref"; 043 private static final String LISTENER_ELEMENT = "listener"; 044 private static final String LISTENERS_ELEMENT = "listeners"; 045 private static final String SCOPE_STEP = "step"; 046 private static final String SCOPE_JOB = "job"; 047 048 private Class<?> listenerType; 049 private String propertyKey; 050 051 public ListenerParser(Class<?> listenerType, String propertyKey) { 052 this.propertyKey = propertyKey; 053 this.listenerType = listenerType; 054 } 055 056 public void parseListeners(Element element, ParserContext parserContext, AbstractBeanDefinition bd, String stepName) { 057 ManagedList<AbstractBeanDefinition> listeners = parseListeners(element, parserContext, stepName); 058 059 if(listeners.size() > 0) { 060 bd.getPropertyValues().add(propertyKey, listeners); 061 } 062 } 063 064 public void parseListeners(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) { 065 ManagedList<AbstractBeanDefinition> listeners = parseListeners(element, parserContext, ""); 066 067 if(listeners.size() > 0) { 068 builder.addPropertyValue(propertyKey, listeners); 069 } 070 } 071 072 private ManagedList<AbstractBeanDefinition> parseListeners(Element element, ParserContext parserContext, String stepName) { 073 List<Element> listenersElements = DomUtils.getChildElementsByTagName(element, LISTENERS_ELEMENT); 074 075 ManagedList<AbstractBeanDefinition> listeners = new ManagedList<AbstractBeanDefinition>(); 076 077 if (listenersElements.size() == 1) { 078 Element listenersElement = listenersElements.get(0); 079 CompositeComponentDefinition compositeDef = new CompositeComponentDefinition(listenersElement.getTagName(), 080 parserContext.extractSource(element)); 081 parserContext.pushContainingComponent(compositeDef); 082 listeners.setMergeEnabled(false); 083 List<Element> listenerElements = DomUtils.getChildElementsByTagName(listenersElement, LISTENER_ELEMENT); 084 for (Element listenerElement : listenerElements) { 085 String beanName = listenerElement.getAttribute(REF_ATTRIBUTE); 086 087 BeanDefinitionBuilder bd = BeanDefinitionBuilder.genericBeanDefinition(listenerType); 088 bd.addPropertyValue("delegate", new RuntimeBeanReference(beanName)); 089 090 applyListenerScope(beanName, parserContext.getRegistry()); 091 092 listeners.add(bd.getBeanDefinition()); 093 094 new PropertyParser(beanName, parserContext, getBatchArtifactType(stepName), stepName).parseProperties(listenerElement); 095 } 096 parserContext.popAndRegisterContainingComponent(); 097 } 098 else if (listenersElements.size() > 1) { 099 parserContext.getReaderContext().error( 100 "The '<listeners/>' element may not appear more than once in a single " + element.getLocalName(), element); 101 } 102 103 return listeners; 104 } 105 106 protected void applyListenerScope(String beanName, BeanDefinitionRegistry beanDefinitionRegistry) { 107 BeanDefinition beanDefinition = getListenerBeanDefinition(beanName, beanDefinitionRegistry); 108 beanDefinition.setScope(getListenerScope()); 109 beanDefinition.setLazyInit(isLazyInit()); 110 111 if (!beanDefinitionRegistry.containsBeanDefinition(beanName)) { 112 beanDefinitionRegistry.registerBeanDefinition(beanName, beanDefinition); 113 } 114 } 115 116 private BeanDefinition getListenerBeanDefinition(String beanName, BeanDefinitionRegistry beanDefinitionRegistry) { 117 if (beanDefinitionRegistry.containsBeanDefinition(beanName)) { 118 return beanDefinitionRegistry.getBeanDefinition(beanName); 119 } 120 121 return BeanDefinitionBuilder.genericBeanDefinition(beanName).getBeanDefinition(); 122 } 123 124 private boolean isLazyInit() { 125 return listenerType == JsrJobListenerFactoryBean.class; 126 } 127 128 private String getListenerScope() { 129 if (listenerType == JsrJobListenerFactoryBean.class) { 130 return SCOPE_JOB; 131 } 132 133 return SCOPE_STEP; 134 } 135 136 private BatchArtifactType getBatchArtifactType(String stepName) { 137 return (stepName != null && !"".equals(stepName)) ? BatchArtifactType.STEP_ARTIFACT 138 : BatchArtifactType.ARTIFACT; 139 } 140}