/* (c) 2016 Open Source Geospatial Foundation - all rights reserved
* This code is licensed under the GPL 2.0 license, available at the root
* application directory.
*/
/*
* Copyright 2006-2007 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.geoserver.backuprestore.writer;
import java.io.File;
import java.io.IOException;
import java.util.List;
import org.geoserver.backuprestore.Backup;
import org.geoserver.config.util.XStreamPersisterFactory;
import org.springframework.batch.core.StepExecution;
import org.springframework.batch.item.ExecutionContext;
import org.springframework.batch.item.ItemStreamException;
import org.springframework.batch.item.file.MultiResourceItemWriter;
import org.springframework.batch.item.file.ResourceAwareItemWriterItemStream;
import org.springframework.batch.item.file.ResourceSuffixCreator;
import org.springframework.batch.item.file.SimpleResourceSuffixCreator;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;
import org.springframework.util.Assert;
/**
* Wraps a {@link ResourceAwareItemWriterItemStream} and creates a new output resource when the count of items written in current resource exceeds
* {@link #setItemCountLimitPerResource(int)}. Suffix creation can be customized with {@link #setResourceSuffixCreator(ResourceSuffixCreator)}.
*
* Note that new resources are created only at chunk boundaries i.e. the number of items written into one resource is between the limit set by
* {@link #setItemCountLimitPerResource(int)} and (limit + chunk size).
*
* Code based on original {@link MultiResourceItemWriter} by Robert Kasanicky.
*
* @param <T> item type
*
* @author Robert Kasanicky
* @author Alessio Fabiani, GeoSolutions
*/
public class CatalogMultiResourceItemWriter<T> extends CatalogWriter<T> {
final static private String RESOURCE_INDEX_KEY = "resource.index";
final static private String CURRENT_RESOURCE_ITEM_COUNT = "resource.item.count";
private Resource resource;
private CatalogWriter<? super T> delegate;
private int itemCountLimitPerResource = Integer.MAX_VALUE;
private int currentResourceItemCount = 0;
private int resourceIndex = 1;
private ResourceSuffixCreator suffixCreator = new SimpleResourceSuffixCreator();
private boolean saveState = true;
private boolean opened = false;
public CatalogMultiResourceItemWriter(Class<T> clazz, Backup backupFacade,
XStreamPersisterFactory xStreamPersisterFactory) {
super(clazz, backupFacade, xStreamPersisterFactory);
}
protected void initialize(StepExecution stepExecution) {
delegate.retrieveInterstepData(stepExecution);
}
@SuppressWarnings("unchecked")
@Override
public void write(List<? extends T> items) throws Exception {
try {
if (!opened) {
File file = setResourceToDelegate();
// create only if write is called
file.createNewFile();
Assert.state(file.canWrite(),
"Output resource " + file.getAbsolutePath() + " must be writable");
delegate.open(new ExecutionContext());
opened = true;
}
delegate.write(items);
currentResourceItemCount += items.size();
if (currentResourceItemCount >= itemCountLimitPerResource) {
delegate.close();
resourceIndex++;
currentResourceItemCount = 0;
setResourceToDelegate();
opened = false;
}
} catch (Exception e) {
logValidationExceptions((T) null, e);
}
}
/**
* Allows customization of the suffix of the created resources based on the index.
*/
public void setResourceSuffixCreator(ResourceSuffixCreator suffixCreator) {
this.suffixCreator = suffixCreator;
}
/**
* After this limit is exceeded the next chunk will be written into newly created resource.
*/
public void setItemCountLimitPerResource(int itemCountLimitPerResource) {
this.itemCountLimitPerResource = itemCountLimitPerResource;
}
/**
* Delegate used for actual writing of the output.
*/
public void setDelegate(CatalogWriter<? super T> delegate) {
this.delegate = delegate;
}
/**
* Prototype for output resources. Actual output files will be created in the same directory and use the same name as this prototype with appended
* suffix (according to {@link #setResourceSuffixCreator(ResourceSuffixCreator)}.
*/
public void setResource(Resource resource) {
this.resource = resource;
}
public void setSaveState(boolean saveState) {
this.saveState = saveState;
}
@SuppressWarnings("unchecked")
@Override
public void close() {
try {
super.close();
resourceIndex = 1;
currentResourceItemCount = 0;
if (opened) {
delegate.close();
}
} catch (ItemStreamException e) {
logValidationExceptions((T) null, e);
}
}
@SuppressWarnings("unchecked")
@Override
public void open(ExecutionContext executionContext) {
try {
super.open(executionContext);
resourceIndex = executionContext.getInt(getExecutionContextKey(RESOURCE_INDEX_KEY), 1);
currentResourceItemCount = executionContext
.getInt(getExecutionContextKey(CURRENT_RESOURCE_ITEM_COUNT), 0);
try {
setResourceToDelegate();
} catch (IOException e) {
throw new ItemStreamException("Couldn't assign resource", e);
}
if (executionContext.containsKey(getExecutionContextKey(CURRENT_RESOURCE_ITEM_COUNT))) {
// It's a restart
delegate.open(executionContext);
} else {
opened = false;
}
} catch (ItemStreamException e) {
logValidationExceptions((T) null, e);
}
}
@SuppressWarnings("unchecked")
@Override
public void update(ExecutionContext executionContext) {
try {
super.update(executionContext);
if (saveState) {
if (opened) {
delegate.update(executionContext);
}
executionContext.putInt(getExecutionContextKey(CURRENT_RESOURCE_ITEM_COUNT),
currentResourceItemCount);
executionContext.putInt(getExecutionContextKey(RESOURCE_INDEX_KEY), resourceIndex);
}
} catch (ItemStreamException e) {
logValidationExceptions((T) null, e);
}
}
/**
* Create output resource (if necessary) and point the delegate to it.
*/
private File setResourceToDelegate() throws IOException {
String path = resource.getFile().getAbsolutePath() + suffixCreator.getSuffix(resourceIndex);
File file = new File(path);
delegate.setResource(new FileSystemResource(file));
return file;
}
@Override
public void afterPropertiesSet() throws Exception {
}
}