View Javadoc

1   package org.springframework.batch.item.xml;
2   
3   import static org.junit.Assert.assertEquals;
4   import static org.junit.Assert.assertTrue;
5   import static org.junit.Assert.fail;
6   
7   import java.io.File;
8   import java.io.IOException;
9   import java.util.Collections;
10  import java.util.List;
11  
12  import javax.xml.stream.XMLEventFactory;
13  import javax.xml.stream.XMLEventWriter;
14  import javax.xml.stream.XMLStreamException;
15  import javax.xml.transform.Result;
16  
17  import org.apache.commons.io.FileUtils;
18  import org.junit.Before;
19  import org.junit.Test;
20  import org.springframework.batch.item.ExecutionContext;
21  import org.springframework.batch.support.transaction.ResourcelessTransactionManager;
22  import org.springframework.core.io.FileSystemResource;
23  import org.springframework.core.io.Resource;
24  import org.springframework.oxm.Marshaller;
25  import org.springframework.oxm.XmlMappingException;
26  import org.springframework.transaction.PlatformTransactionManager;
27  import org.springframework.transaction.TransactionStatus;
28  import org.springframework.transaction.support.TransactionCallback;
29  import org.springframework.transaction.support.TransactionTemplate;
30  import org.springframework.util.ClassUtils;
31  import org.springframework.util.StringUtils;
32  
33  /**
34   * Tests for {@link StaxEventItemWriter}.
35   */
36  public class TransactionalStaxEventItemWriterTests {
37  
38  	// object under test
39  	private StaxEventItemWriter<Object> writer;
40  
41  	private PlatformTransactionManager transactionManager = new ResourcelessTransactionManager();
42  
43  	// output file
44  	private Resource resource;
45  
46  	private ExecutionContext executionContext;
47  
48  	// test item for writing to output
49  	private Object item = new Object() {
50  		public String toString() {
51  			return ClassUtils.getShortName(StaxEventItemWriter.class) + "-testString";
52  		}
53  	};
54  
55  	private List<? extends Object> items = Collections.singletonList(item);
56  
57  	private static final String TEST_STRING = "<!--" + ClassUtils.getShortName(StaxEventItemWriter.class)
58  			+ "-testString-->";
59  
60  	@Before
61  	public void setUp() throws Exception {
62  		resource = new FileSystemResource(File.createTempFile("StaxEventWriterOutputSourceTests", ".xml"));
63  		writer = createItemWriter();
64  		executionContext = new ExecutionContext();
65  	}
66  
67  	/**
68  	 * Item is written to the output file only after flush.
69  	 */
70  	@Test
71  	public void testWriteAndFlush() throws Exception {
72  		writer.open(executionContext);
73  		new TransactionTemplate(transactionManager).execute(new TransactionCallback() {
74  			public Object doInTransaction(TransactionStatus status) {
75  				try {
76  					writer.write(items);
77  				}
78  				catch ( Exception e) {
79  					throw new RuntimeException(e);
80  				}
81  				return null;
82  			}
83  		});
84  		writer.close();
85  		String content = outputFileContent();
86  		assertTrue("Wrong content: " + content, content.contains(TEST_STRING));
87  	}
88  
89  	/**
90  	 * Item is written to the output file only after flush.
91  	 */
92  	@Test
93  	public void testWriteWithHeaderAfterRollback() throws Exception {
94  		writer.setHeaderCallback(new StaxWriterCallback(){
95  
96  			public void write(XMLEventWriter writer) throws IOException {
97  				XMLEventFactory factory = XMLEventFactory.newInstance();
98  				try {
99  					writer.add(factory.createStartElement("", "", "header"));
100 					writer.add(factory.createEndElement("", "", "header"));
101 				}
102 				catch (XMLStreamException e) {
103 					throw new RuntimeException(e);
104 				}
105 				
106 			}
107 			
108 		});
109 		writer.open(executionContext);
110 		try {
111 			new TransactionTemplate(transactionManager).execute(new TransactionCallback() {
112 				public Object doInTransaction(TransactionStatus status) {
113 					try {
114 						writer.write(items);
115 					}
116 					catch (Exception e) {
117 						throw new RuntimeException(e);
118 					}
119 					throw new RuntimeException("Planned");
120 				}
121 			});
122 			fail("Expected RuntimeException");
123 		}
124 		catch (RuntimeException e) {
125 			// expected
126 		}
127 		writer.close();
128 		writer.open(executionContext);
129 		new TransactionTemplate(transactionManager).execute(new TransactionCallback() {
130 			public Object doInTransaction(TransactionStatus status) {
131 				try {
132 					writer.write(items);
133 				}
134 				catch (Exception e) {
135 					throw new RuntimeException(e);
136 				}
137 				return null;
138 			}
139 		});
140 		writer.close();
141 		String content = outputFileContent();
142 		assertEquals("Wrong content: " + content, 1, StringUtils.countOccurrencesOf(content, ("<header/>")));
143 		assertEquals("Wrong content: " + content, 1, StringUtils.countOccurrencesOf(content, TEST_STRING));
144 	}
145 
146 	/**
147 	 * Item is written to the output file only after flush.
148 	 */
149 	@Test
150 	public void testWriteWithHeaderAfterFlushAndRollback() throws Exception {
151 		writer.setHeaderCallback(new StaxWriterCallback(){
152 
153 			public void write(XMLEventWriter writer) throws IOException {
154 				XMLEventFactory factory = XMLEventFactory.newInstance();
155 				try {
156 					writer.add(factory.createStartElement("", "", "header"));
157 					writer.add(factory.createEndElement("", "", "header"));
158 				}
159 				catch (XMLStreamException e) {
160 					throw new RuntimeException(e);
161 				}
162 				
163 			}
164 			
165 		});
166 		writer.open(executionContext);
167 		new TransactionTemplate(transactionManager).execute(new TransactionCallback() {
168 			public Object doInTransaction(TransactionStatus status) {
169 				try {
170 					writer.write(items);
171 				}
172 				catch (Exception e) {
173 					throw new RuntimeException(e);
174 				}
175 				return null;
176 			}
177 		});
178 		writer.update(executionContext);
179 		writer.close();
180 		writer.open(executionContext);
181 		try {
182 			new TransactionTemplate(transactionManager).execute(new TransactionCallback() {
183 				public Object doInTransaction(TransactionStatus status) {
184 					try {
185 						writer.write(items);
186 					}
187 					catch (Exception e) {
188 						throw new RuntimeException(e);
189 					}
190 					throw new RuntimeException("Planned");
191 				}
192 			});
193 			fail("Expected RuntimeException");
194 		}
195 		catch (RuntimeException e) {
196 			// expected
197 		}
198 		writer.close();
199 		String content = outputFileContent();
200 		assertEquals("Wrong content: " + content, 1, StringUtils.countOccurrencesOf(content, ("<header/>")));
201 		assertEquals("Wrong content: " + content, 1, StringUtils.countOccurrencesOf(content, TEST_STRING));
202 	}
203 
204 	/**
205 	 * @return output file content as String
206 	 */
207 	private String outputFileContent() throws IOException {
208 		return FileUtils.readFileToString(resource.getFile(), null);
209 	}
210 
211 	/**
212 	 * Writes object's toString representation as XML comment.
213 	 */
214 	private static class SimpleMarshaller implements Marshaller {
215 		public void marshal(Object graph, Result result) throws XmlMappingException, IOException {
216 			try {
217 				StaxUtils.getXmlEventWriter(result).add(XMLEventFactory.newInstance().createComment(graph.toString()));
218 			}
219 			catch ( Exception e) {
220 				throw new RuntimeException("Exception while writing to output file", e);
221 			}
222 		}
223 
224 		@SuppressWarnings("rawtypes")
225 		public boolean supports(Class clazz) {
226 			return true;
227 		}
228 	}
229 
230 	/**
231 	 * @return new instance of fully configured writer
232 	 */
233 	private StaxEventItemWriter<Object> createItemWriter() throws Exception {
234 		StaxEventItemWriter<Object> source = new StaxEventItemWriter<Object>();
235 		source.setResource(resource);
236 
237 		Marshaller marshaller = new SimpleMarshaller();
238 		source.setMarshaller(marshaller);
239 
240 		source.setEncoding("UTF-8");
241 		source.setRootTagName("root");
242 		source.setVersion("1.0");
243 		source.setOverwriteOutput(true);
244 		source.setSaveState(true);
245 
246 		source.afterPropertiesSet();
247 
248 		return source;
249 	}
250 }