1
1
/*
2
- * Copyright 2006-2020 the original author or authors.
2
+ * Copyright 2006-2022 the original author or authors.
3
3
*
4
4
* Licensed under the Apache License, Version 2.0 (the "License");
5
5
* you may not use this file except in compliance with the License.
52
52
import org .springframework .batch .support .transaction .TransactionAwareBufferedWriter ;
53
53
import org .springframework .beans .factory .InitializingBean ;
54
54
import org .springframework .core .io .Resource ;
55
- import org .springframework .dao .DataAccessResourceFailureException ;
56
55
import org .springframework .oxm .Marshaller ;
57
56
import org .springframework .oxm .XmlMappingException ;
58
57
import org .springframework .util .Assert ;
64
63
/**
65
64
* An implementation of {@link ItemWriter} which uses StAX and
66
65
* {@link Marshaller} for serializing object to XML.
67
- *
66
+ *
68
67
* This item writer also provides restart, statistics and transaction features
69
68
* by implementing corresponding interfaces.
70
- *
69
+ *
71
70
* The implementation is <b>not</b> thread-safe.
72
- *
71
+ *
73
72
* @author Peter Zozom
74
73
* @author Robert Kasanicky
75
74
* @author Michael Minella
@@ -98,7 +97,7 @@ public class StaxEventItemWriter<T> extends AbstractItemStreamItemWriter<T> impl
98
97
99
98
// unclosed header callback elements property name
100
99
private static final String UNCLOSED_HEADER_CALLBACK_ELEMENTS_NAME = "unclosedHeaderCallbackElements" ;
101
-
100
+
102
101
// restart data property name
103
102
private static final String WRITE_STATISTICS_NAME = "record.count" ;
104
103
@@ -159,11 +158,11 @@ public class StaxEventItemWriter<T> extends AbstractItemStreamItemWriter<T> impl
159
158
private boolean forceSync ;
160
159
161
160
private boolean shouldDeleteIfEmpty = false ;
162
-
161
+
163
162
private boolean restarted = false ;
164
163
165
164
private boolean initialized = false ;
166
-
165
+
167
166
// List holding the QName of elements that were opened in the header callback, but not closed
168
167
private List <QName > unclosedHeaderCallbackElements = Collections .emptyList ();
169
168
@@ -173,7 +172,7 @@ public StaxEventItemWriter() {
173
172
174
173
/**
175
174
* Set output file.
176
- *
175
+ *
177
176
* @param resource the output file
178
177
*/
179
178
@ Override
@@ -183,7 +182,7 @@ public void setResource(Resource resource) {
183
182
184
183
/**
185
184
* Set Object to XML marshaller.
186
- *
185
+ *
187
186
* @param marshaller the Object to XML marshaller
188
187
*/
189
188
public void setMarshaller (Marshaller marshaller ) {
@@ -212,7 +211,7 @@ public void setFooterCallback(StaxWriterCallback footerCallback) {
212
211
/**
213
212
* Flag to indicate that writes should be deferred to the end of a
214
213
* transaction if present. Defaults to true.
215
- *
214
+ *
216
215
* @param transactional the flag to set
217
216
*/
218
217
public void setTransactional (boolean transactional ) {
@@ -225,7 +224,7 @@ public void setTransactional(boolean transactional) {
225
224
* be lost if the OS crashes in between a write and a cache flush. Setting
226
225
* to true may result in slower performance for usage patterns involving
227
226
* many frequent writes.
228
- *
227
+ *
229
228
* @param forceSync the flag value to set
230
229
*/
231
230
public void setForceSync (boolean forceSync ) {
@@ -235,7 +234,7 @@ public void setForceSync(boolean forceSync) {
235
234
/**
236
235
* Flag to indicate that the target file should be deleted if no items have
237
236
* been written (other than header and footer) on close. Defaults to false.
238
- *
237
+ *
239
238
* @param shouldDeleteIfEmpty the flag value to set
240
239
*/
241
240
public void setShouldDeleteIfEmpty (boolean shouldDeleteIfEmpty ) {
@@ -244,7 +243,7 @@ public void setShouldDeleteIfEmpty(boolean shouldDeleteIfEmpty) {
244
243
245
244
/**
246
245
* Get used encoding.
247
- *
246
+ *
248
247
* @return the encoding used
249
248
*/
250
249
public String getEncoding () {
@@ -253,7 +252,7 @@ public String getEncoding() {
253
252
254
253
/**
255
254
* Set encoding to be used for output file.
256
- *
255
+ *
257
256
* @param encoding the encoding to be used
258
257
*/
259
258
public void setEncoding (String encoding ) {
@@ -271,7 +270,7 @@ public String getVersion() {
271
270
272
271
/**
273
272
* Set XML version to be used for output XML.
274
- *
273
+ *
275
274
* @param version the XML version to be used
276
275
*/
277
276
public void setVersion (String version ) {
@@ -303,7 +302,7 @@ public void setStandalone(Boolean standalone) {
303
302
304
303
/**
305
304
* Get the tag name of the root element.
306
- *
305
+ *
307
306
* @return the root element tag name
308
307
*/
309
308
public String getRootTagName () {
@@ -314,16 +313,16 @@ public String getRootTagName() {
314
313
* Set the tag name of the root element. If not set, default name is used
315
314
* ("root"). Namespace URI and prefix can also be set optionally using the
316
315
* notation:
317
- *
316
+ *
318
317
* <pre>
319
318
* {uri}prefix:root
320
319
* </pre>
321
- *
320
+ *
322
321
* The prefix is optional (defaults to empty), but if it is specified then
323
322
* the uri must be provided. In addition you might want to declare other
324
323
* namespaces using the {@link #setRootElementAttributes(Map) root
325
324
* attributes}.
326
- *
325
+ *
327
326
* @param rootTagName the tag name to be used for the root element
328
327
*/
329
328
public void setRootTagName (String rootTagName ) {
@@ -332,7 +331,7 @@ public void setRootTagName(String rootTagName) {
332
331
333
332
/**
334
333
* Get the namespace prefix of the root element. Empty by default.
335
- *
334
+ *
336
335
* @return the rootTagNamespacePrefix
337
336
*/
338
337
public String getRootTagNamespacePrefix () {
@@ -341,7 +340,7 @@ public String getRootTagNamespacePrefix() {
341
340
342
341
/**
343
342
* Get the namespace of the root element.
344
- *
343
+ *
345
344
* @return the rootTagNamespace
346
345
*/
347
346
public String getRootTagNamespace () {
@@ -350,7 +349,7 @@ public String getRootTagNamespace() {
350
349
351
350
/**
352
351
* Get attributes of the root element.
353
- *
352
+ *
354
353
* @return attributes of the root element
355
354
*/
356
355
public Map <String , String > getRootElementAttributes () {
@@ -360,7 +359,7 @@ public Map<String, String> getRootElementAttributes() {
360
359
/**
361
360
* Set the root element attributes to be written. If any of the key names
362
361
* begin with "xmlns:" then they are treated as namespace declarations.
363
- *
362
+ *
364
363
* @param rootElementAttributes attributes of the root element
365
364
*/
366
365
public void setRootElementAttributes (Map <String , String > rootElementAttributes ) {
@@ -370,7 +369,7 @@ public void setRootElementAttributes(Map<String, String> rootElementAttributes)
370
369
/**
371
370
* Set "overwrite" flag for the output file. Flag is ignored when output
372
371
* file processing is restarted.
373
- *
372
+ *
374
373
* @param overwriteOutput If set to true, output file will be overwritten
375
374
* (this flag is ignored when processing is restart).
376
375
*/
@@ -403,7 +402,7 @@ public void afterPropertiesSet() throws Exception {
403
402
* Open the output source
404
403
*
405
404
* @param executionContext the batch context.
406
- *
405
+ *
407
406
* @see org.springframework.batch.item.ItemStream#open(ExecutionContext)
408
407
*/
409
408
@ SuppressWarnings ("unchecked" )
@@ -414,7 +413,7 @@ public void open(ExecutionContext executionContext) {
414
413
Assert .notNull (resource , "The resource must be set" );
415
414
416
415
long startAtPosition = 0 ;
417
-
416
+
418
417
// if restart data is provided, restart from provided offset
419
418
// otherwise start from beginning
420
419
if (executionContext .containsKey (getExecutionContextKey (RESTART_DATA_NAME ))) {
@@ -424,7 +423,7 @@ public void open(ExecutionContext executionContext) {
424
423
unclosedHeaderCallbackElements = (List <QName >) executionContext
425
424
.get (getExecutionContextKey (UNCLOSED_HEADER_CALLBACK_ELEMENTS_NAME ));
426
425
}
427
-
426
+
428
427
restarted = true ;
429
428
if (shouldDeleteIfEmpty && currentRecordCount == 0 ) {
430
429
// previous execution deleted the output file because no items were written
@@ -476,7 +475,7 @@ private void open(long position) {
476
475
setPosition (position );
477
476
}
478
477
catch (IOException ioe ) {
479
- throw new DataAccessResourceFailureException ("Unable to write to file resource: [" + resource + "]" , ioe );
478
+ throw new ItemStreamException ("Unable to write to file resource: [" + resource + "]" , ioe );
480
479
}
481
480
482
481
XMLOutputFactory outputFactory = createXmlOutputFactory ();
@@ -524,14 +523,14 @@ public void run() {
524
523
}
525
524
}
526
525
catch (XMLStreamException xse ) {
527
- throw new DataAccessResourceFailureException ("Unable to write to file resource: [" + resource + "]" , xse );
526
+ throw new ItemStreamException ("Unable to write to file resource: [" + resource + "]" , xse );
528
527
}
529
528
catch (UnsupportedEncodingException e ) {
530
- throw new DataAccessResourceFailureException ("Unable to write to file resource: [" + resource
529
+ throw new ItemStreamException ("Unable to write to file resource: [" + resource
531
530
+ "] with encoding=[" + encoding + "]" , e );
532
- }
531
+ }
533
532
catch (IOException e ) {
534
- throw new DataAccessResourceFailureException ("Unable to write to file resource: [" + resource + "]" , e );
533
+ throw new ItemStreamException ("Unable to write to file resource: [" + resource + "]" , e );
535
534
}
536
535
}
537
536
@@ -588,7 +587,7 @@ protected Result createStaxResult() {
588
587
* <li>rootTagNamespacePrefix for rootTagName</li>
589
588
* <li>any other xmlns namespace prefix declarations in the root element attributes</li>
590
589
* </ul>
591
- *
590
+ *
592
591
* @param writer XML event writer
593
592
*
594
593
* @throws XMLStreamException thrown if error occurs while setting the
@@ -627,7 +626,7 @@ protected void initNamespaceContext(XMLEventWriter writer) throws XMLStreamExcep
627
626
* </ul>
628
627
* If this is not sufficient for you, simply override this method. Encoding,
629
628
* version and root tag name can be retrieved with corresponding getters.
630
- *
629
+ *
631
630
* @param writer XML event writer
632
631
*
633
632
* @throws XMLStreamException thrown if error occurs.
@@ -685,7 +684,7 @@ protected void startDocument(XMLEventWriter writer) throws XMLStreamException {
685
684
686
685
/**
687
686
* Writes the EndDocument tag manually.
688
- *
687
+ *
689
688
* @param writer XML event writer
690
689
*
691
690
* @throws XMLStreamException thrown if error occurs.
@@ -700,13 +699,13 @@ protected void endDocument(XMLEventWriter writer) throws XMLStreamException {
700
699
bufferedWriter .write ("</" + nsPrefix + getRootTagName () + ">" );
701
700
}
702
701
catch (IOException ioe ) {
703
- throw new DataAccessResourceFailureException ("Unable to close file resource: [" + resource + "]" , ioe );
702
+ throw new XMLStreamException ("Unable to close file resource: [" + resource + "]" , ioe );
704
703
}
705
704
}
706
705
707
706
/**
708
707
* Flush and close the output source.
709
- *
708
+ *
710
709
* @see org.springframework.batch.item.ItemStream#close()
711
710
*/
712
711
@ Override
@@ -727,7 +726,7 @@ public void close() {
727
726
if (restarted && !unclosedHeaderCallbackElements .isEmpty ()) {
728
727
footerCallbackWriter = new UnopenedElementClosingEventWriter (
729
728
delegateEventWriter , bufferedWriter , unclosedHeaderCallbackElements );
730
- }
729
+ }
731
730
footerCallback .write (footerCallbackWriter );
732
731
}
733
732
delegateEventWriter .flush ();
@@ -784,7 +783,7 @@ private void closeStream() {
784
783
785
784
/**
786
785
* Write the value objects and flush them to the file.
787
- *
786
+ *
788
787
* @param items the value object
789
788
*
790
789
* @throws IOException thrown if general error occurs.
@@ -809,18 +808,18 @@ public void write(List<? extends T> items) throws XmlMappingException, IOExcepti
809
808
eventWriter .flush ();
810
809
if (forceSync ) {
811
810
channel .force (false );
812
- }
811
+ }
813
812
}
814
813
catch (XMLStreamException | IOException e ) {
815
814
throw new WriteFailedException ("Failed to flush the events" , e );
816
- }
815
+ }
817
816
}
818
817
819
818
/**
820
819
* Get the restart data.
821
820
*
822
821
* @param executionContext the batch context.
823
- *
822
+ *
824
823
* @see org.springframework.batch.item.ItemStream#update(ExecutionContext)
825
824
*/
826
825
@ Override
@@ -833,14 +832,14 @@ public void update(ExecutionContext executionContext) {
833
832
if (!unclosedHeaderCallbackElements .isEmpty ()) {
834
833
executionContext .put (getExecutionContextKey (UNCLOSED_HEADER_CALLBACK_ELEMENTS_NAME ),
835
834
unclosedHeaderCallbackElements );
836
- }
835
+ }
837
836
}
838
837
}
839
838
840
839
/*
841
840
* Get the actual position in file channel. This method flushes any buffered
842
841
* data before position is read.
843
- *
842
+ *
844
843
* @return byte offset in file channel
845
844
*/
846
845
private long getPosition () {
@@ -855,15 +854,15 @@ private long getPosition() {
855
854
}
856
855
}
857
856
catch (Exception e ) {
858
- throw new DataAccessResourceFailureException ("Unable to write to file resource: [" + resource + "]" , e );
857
+ throw new ItemStreamException ("Unable to write to file resource: [" + resource + "]" , e );
859
858
}
860
859
861
860
return position ;
862
861
}
863
862
864
863
/**
865
864
* Set the file channel position.
866
- *
865
+ *
867
866
* @param newPosition new file channel position
868
867
*/
869
868
private void setPosition (long newPosition ) {
@@ -873,7 +872,7 @@ private void setPosition(long newPosition) {
873
872
channel .position (newPosition );
874
873
}
875
874
catch (IOException e ) {
876
- throw new DataAccessResourceFailureException ("Unable to write to file resource: [" + resource + "]" , e );
875
+ throw new ItemStreamException ("Unable to write to file resource: [" + resource + "]" , e );
877
876
}
878
877
879
878
}
0 commit comments