001/* 002 * Copyright 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.item.support; 017 018import org.springframework.scripting.support.StaticScriptSource; 019import org.springframework.util.StringUtils; 020import org.springframework.batch.item.ItemProcessor; 021import org.springframework.beans.factory.InitializingBean; 022import org.springframework.core.io.Resource; 023import org.springframework.scripting.ScriptEvaluator; 024import org.springframework.scripting.ScriptSource; 025import org.springframework.scripting.support.ResourceScriptSource; 026import org.springframework.scripting.support.StandardScriptEvaluator; 027import org.springframework.util.Assert; 028 029import java.util.HashMap; 030import java.util.Map; 031 032/** 033 * <p> 034 * {@link org.springframework.batch.item.ItemProcessor} implementation that passes the current 035 * item to process to the provided script. Exposes the current item for processing via the 036 * {@link org.springframework.batch.item.support.ScriptItemProcessor#ITEM_BINDING_VARIABLE_NAME} 037 * key name ("item"). A custom key name can be set by invoking: 038 * {@link org.springframework.batch.item.support.ScriptItemProcessor#setItemBindingVariableName} 039 * with the desired key name. The thread safety of this {@link org.springframework.batch.item.ItemProcessor} 040 * depends on the implementation of the {@link org.springframework.scripting.ScriptEvaluator} used. 041 * </p> 042 * 043 * 044 * @author Chris Schaefer 045 * @since 3.0 046 */ 047public class ScriptItemProcessor<I, O> implements ItemProcessor<I, O>, InitializingBean { 048 private static final String ITEM_BINDING_VARIABLE_NAME = "item"; 049 050 private String language; 051 private ScriptSource script; 052 private ScriptSource scriptSource; 053 private ScriptEvaluator scriptEvaluator; 054 private String itemBindingVariableName = ITEM_BINDING_VARIABLE_NAME; 055 056 @Override 057 @SuppressWarnings("unchecked") 058 public O process(I item) throws Exception { 059 Map<String, Object> arguments = new HashMap<String, Object>(); 060 arguments.put(itemBindingVariableName, item); 061 062 return (O) scriptEvaluator.evaluate(getScriptSource(), arguments); 063 } 064 065 /** 066 * <p> 067 * Sets the {@link org.springframework.core.io.Resource} location of the script to use. 068 * The script language will be deduced from the filename extension. 069 * </p> 070 * 071 * @param resource the {@link org.springframework.core.io.Resource} location of the script to use. 072 */ 073 public void setScript(Resource resource) { 074 Assert.notNull(resource, "The script resource cannot be null"); 075 076 this.script = new ResourceScriptSource(resource); 077 } 078 079 /** 080 * <p> 081 * Sets the provided {@link String} as the script source code to use. 082 * </p> 083 * 084 * @param scriptSource the {@link String} form of the script source code to use. 085 * @param language the language of the script. 086 */ 087 public void setScriptSource(String scriptSource, String language) { 088 Assert.hasText(language, "Language must contain the script language"); 089 Assert.hasText(scriptSource, "Script source must contain the script source to evaluate"); 090 091 this.language = language; 092 this.scriptSource = new StaticScriptSource(scriptSource); 093 } 094 095 /** 096 * <p> 097 * Provides the ability to change the key name that scripts use to obtain the current 098 * item to process if the variable represented by: 099 * {@link org.springframework.batch.item.support.ScriptItemProcessor#ITEM_BINDING_VARIABLE_NAME} 100 * is not suitable ("item"). 101 * </p> 102 * 103 * @param itemBindingVariableName the desired binding variable name 104 */ 105 public void setItemBindingVariableName(String itemBindingVariableName) { 106 this.itemBindingVariableName = itemBindingVariableName; 107 } 108 109 /** 110 * <p> 111 * Provides the ability to set a custom {@link org.springframework.scripting.ScriptEvaluator} 112 * implementation. If not set, a {@link org.springframework.scripting.support.StandardScriptEvaluator} 113 * will be used by default. 114 * </p> 115 * 116 * @param scriptEvaluator the {@link org.springframework.scripting.ScriptEvaluator} to use 117 */ 118 public void setScriptEvaluator(ScriptEvaluator scriptEvaluator) { 119 this.scriptEvaluator = scriptEvaluator; 120 } 121 122 @Override 123 public void afterPropertiesSet() throws Exception { 124 if(scriptEvaluator == null) { 125 scriptEvaluator = new StandardScriptEvaluator(); 126 } 127 128 Assert.state(scriptSource != null || script != null, 129 "Either the script source or script file must be provided"); 130 131 Assert.state(scriptSource == null || script == null, 132 "Either a script source or script file must be provided, not both"); 133 134 if (scriptSource != null && scriptEvaluator instanceof StandardScriptEvaluator) { 135 Assert.isTrue(!StringUtils.isEmpty(language), 136 "Language must be provided when using the default ScriptEvaluator and raw source code"); 137 138 ((StandardScriptEvaluator) scriptEvaluator).setLanguage(language); 139 } 140 } 141 142 private ScriptSource getScriptSource() { 143 if (script != null) { 144 return script; 145 } 146 147 if (scriptSource != null) { 148 return scriptSource; 149 } 150 151 throw new IllegalStateException("Either a script source or script needs to be provided."); 152 } 153}