CLOSED - issue BATCH-979: Insert Apache license header in Java sources (where missing)
This commit is contained in:
@@ -1,6 +1,19 @@
|
||||
/**
|
||||
*
|
||||
/*
|
||||
* 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.springframework.batch.core;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
@@ -1,6 +1,19 @@
|
||||
/**
|
||||
*
|
||||
/*
|
||||
* 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.springframework.batch.core;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
@@ -1,6 +1,19 @@
|
||||
/**
|
||||
*
|
||||
/*
|
||||
* 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.springframework.batch.core;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
@@ -1,11 +1,27 @@
|
||||
package org.springframework.batch.core;
|
||||
|
||||
/**
|
||||
* Indicates the step's start limit has been exceeded.
|
||||
*/
|
||||
public class StartLimitExceededException extends RuntimeException {
|
||||
|
||||
public StartLimitExceededException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
/*
|
||||
* 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.springframework.batch.core;
|
||||
|
||||
/**
|
||||
* Indicates the step's start limit has been exceeded.
|
||||
*/
|
||||
public class StartLimitExceededException extends RuntimeException {
|
||||
|
||||
public StartLimitExceededException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,19 @@
|
||||
/**
|
||||
*
|
||||
/*
|
||||
* 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.springframework.batch.core.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
* 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.springframework.batch.core.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
|
||||
@@ -1,6 +1,19 @@
|
||||
/**
|
||||
*
|
||||
/*
|
||||
* 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.springframework.batch.core.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
* 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.springframework.batch.core.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
|
||||
@@ -1,6 +1,19 @@
|
||||
/**
|
||||
*
|
||||
/*
|
||||
* 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.springframework.batch.core.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
|
||||
@@ -1,6 +1,19 @@
|
||||
/**
|
||||
*
|
||||
/*
|
||||
* 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.springframework.batch.core.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
* 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.springframework.batch.core.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
* 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.springframework.batch.core.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
* 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.springframework.batch.core.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
* 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.springframework.batch.core.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
* 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.springframework.batch.core.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
* 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.springframework.batch.core.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
|
||||
@@ -1,9 +1,25 @@
|
||||
package org.springframework.batch.core.configuration.support;
|
||||
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
|
||||
public interface ApplicationContextFactory {
|
||||
|
||||
ConfigurableApplicationContext createApplicationContext();
|
||||
|
||||
}
|
||||
/*
|
||||
* 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.springframework.batch.core.configuration.support;
|
||||
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
|
||||
public interface ApplicationContextFactory {
|
||||
|
||||
ConfigurableApplicationContext createApplicationContext();
|
||||
|
||||
}
|
||||
|
||||
@@ -1,68 +1,84 @@
|
||||
package org.springframework.batch.core.configuration.support;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.support.AbstractXmlApplicationContext;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
public class ClassPathXmlApplicationContextFactory implements ApplicationContextFactory, ApplicationContextAware {
|
||||
|
||||
private ConfigurableApplicationContext parent;
|
||||
|
||||
private Resource path;
|
||||
|
||||
/**
|
||||
* Setter for the path to the xml to load to create an
|
||||
* {@link ApplicationContext}. Use imports to centralise the configuration in
|
||||
* one file.
|
||||
*
|
||||
* @param path the resource path to the xml to load for the child context.
|
||||
*/
|
||||
public void setPath(Resource path) {
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter for the parent application context.
|
||||
*
|
||||
* @see org.springframework.context.ApplicationContextAware#setApplicationContext(org.springframework.context.ApplicationContext)
|
||||
*/
|
||||
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
|
||||
Assert.isInstanceOf(ConfigurableApplicationContext.class, applicationContext);
|
||||
parent = (ConfigurableApplicationContext) applicationContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an {@link ApplicationContext} from the provided path.
|
||||
*
|
||||
* @see ApplicationContextFactory#createApplicationContext()
|
||||
*/
|
||||
public ConfigurableApplicationContext createApplicationContext() {
|
||||
if (path == null) {
|
||||
return parent;
|
||||
}
|
||||
return new ResourceXmlApplicationContext(parent);
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
private final class ResourceXmlApplicationContext extends AbstractXmlApplicationContext {
|
||||
/**
|
||||
* @param parent
|
||||
*/
|
||||
private ResourceXmlApplicationContext(ApplicationContext parent) {
|
||||
super(parent);
|
||||
refresh();
|
||||
}
|
||||
|
||||
protected Resource[] getConfigResources() {
|
||||
return new Resource[] {path};
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* 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.springframework.batch.core.configuration.support;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.support.AbstractXmlApplicationContext;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
public class ClassPathXmlApplicationContextFactory implements ApplicationContextFactory, ApplicationContextAware {
|
||||
|
||||
private ConfigurableApplicationContext parent;
|
||||
|
||||
private Resource path;
|
||||
|
||||
/**
|
||||
* Setter for the path to the xml to load to create an
|
||||
* {@link ApplicationContext}. Use imports to centralise the configuration in
|
||||
* one file.
|
||||
*
|
||||
* @param path the resource path to the xml to load for the child context.
|
||||
*/
|
||||
public void setPath(Resource path) {
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter for the parent application context.
|
||||
*
|
||||
* @see org.springframework.context.ApplicationContextAware#setApplicationContext(org.springframework.context.ApplicationContext)
|
||||
*/
|
||||
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
|
||||
Assert.isInstanceOf(ConfigurableApplicationContext.class, applicationContext);
|
||||
parent = (ConfigurableApplicationContext) applicationContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an {@link ApplicationContext} from the provided path.
|
||||
*
|
||||
* @see ApplicationContextFactory#createApplicationContext()
|
||||
*/
|
||||
public ConfigurableApplicationContext createApplicationContext() {
|
||||
if (path == null) {
|
||||
return parent;
|
||||
}
|
||||
return new ResourceXmlApplicationContext(parent);
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
private final class ResourceXmlApplicationContext extends AbstractXmlApplicationContext {
|
||||
/**
|
||||
* @param parent
|
||||
*/
|
||||
private ResourceXmlApplicationContext(ApplicationContext parent) {
|
||||
super(parent);
|
||||
refresh();
|
||||
}
|
||||
|
||||
protected Resource[] getConfigResources() {
|
||||
return new Resource[] {path};
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,6 +1,19 @@
|
||||
/**
|
||||
*
|
||||
/*
|
||||
* 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.springframework.batch.core.configuration.support;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
@@ -1,57 +1,73 @@
|
||||
package org.springframework.batch.core.configuration.support;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.batch.core.configuration.JobFactory;
|
||||
import org.springframework.batch.core.configuration.JobRegistry;
|
||||
|
||||
/**
|
||||
* Generic service that can bind and unbind a {@link JobFactory} in a
|
||||
* {@link JobRegistry}.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public class JobFactoryRegistrationListener {
|
||||
|
||||
private Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private JobRegistry jobRegistry;
|
||||
|
||||
/**
|
||||
* Public setter for a {@link JobRegistry} to use for all the bind and
|
||||
* unbind events.
|
||||
*
|
||||
* @param jobRegistry {@link JobRegistry}
|
||||
*/
|
||||
public void setJobRegistry(JobRegistry jobRegistry) {
|
||||
this.jobRegistry = jobRegistry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Take the {@link JobFactory} provided and register it with the
|
||||
* {@link JobRegistry}.
|
||||
* @param jobFactory a {@link JobFactory}
|
||||
* @param params not needed by this listener.
|
||||
* @throws Exception if there is a problem
|
||||
*/
|
||||
public void bind(JobFactory jobFactory, Map<String, ?> params) throws Exception {
|
||||
logger.info("Binding JobFactory: " + jobFactory.getJobName());
|
||||
jobRegistry.register(jobFactory);
|
||||
}
|
||||
|
||||
/**
|
||||
* Take the {@link JobFactory} provided and unregister it with the
|
||||
* {@link JobRegistry}.
|
||||
* @param jobFactory a {@link JobFactory}
|
||||
* @param params not needed by this listener.
|
||||
* @throws Exception if there is a problem
|
||||
*/
|
||||
public void unbind(JobFactory jobFactory, Map<String, ?> params) throws Exception {
|
||||
logger.info("Unbinding JobFactory: " + jobFactory.getJobName());
|
||||
jobRegistry.unregister(jobFactory.getJobName());
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* 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.springframework.batch.core.configuration.support;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.batch.core.configuration.JobFactory;
|
||||
import org.springframework.batch.core.configuration.JobRegistry;
|
||||
|
||||
/**
|
||||
* Generic service that can bind and unbind a {@link JobFactory} in a
|
||||
* {@link JobRegistry}.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public class JobFactoryRegistrationListener {
|
||||
|
||||
private Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private JobRegistry jobRegistry;
|
||||
|
||||
/**
|
||||
* Public setter for a {@link JobRegistry} to use for all the bind and
|
||||
* unbind events.
|
||||
*
|
||||
* @param jobRegistry {@link JobRegistry}
|
||||
*/
|
||||
public void setJobRegistry(JobRegistry jobRegistry) {
|
||||
this.jobRegistry = jobRegistry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Take the {@link JobFactory} provided and register it with the
|
||||
* {@link JobRegistry}.
|
||||
* @param jobFactory a {@link JobFactory}
|
||||
* @param params not needed by this listener.
|
||||
* @throws Exception if there is a problem
|
||||
*/
|
||||
public void bind(JobFactory jobFactory, Map<String, ?> params) throws Exception {
|
||||
logger.info("Binding JobFactory: " + jobFactory.getJobName());
|
||||
jobRegistry.register(jobFactory);
|
||||
}
|
||||
|
||||
/**
|
||||
* Take the {@link JobFactory} provided and unregister it with the
|
||||
* {@link JobRegistry}.
|
||||
* @param jobFactory a {@link JobFactory}
|
||||
* @param params not needed by this listener.
|
||||
* @throws Exception if there is a problem
|
||||
*/
|
||||
public void unbind(JobFactory jobFactory, Map<String, ?> params) throws Exception {
|
||||
logger.info("Unbinding JobFactory: " + jobFactory.getJobName());
|
||||
jobRegistry.unregister(jobFactory.getJobName());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,74 +1,90 @@
|
||||
package org.springframework.batch.core.configuration.support;
|
||||
|
||||
import org.osgi.framework.BundleContext;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.osgi.context.BundleContextAware;
|
||||
import org.springframework.osgi.context.support.OsgiBundleXmlApplicationContext;
|
||||
|
||||
public class OsgiBundleXmlApplicationContextFactory implements BundleContextAware,
|
||||
ApplicationContextFactory, ApplicationContextAware {
|
||||
|
||||
private BundleContext bundleContext;
|
||||
|
||||
private ApplicationContext parent;
|
||||
|
||||
private String path;
|
||||
|
||||
private String displayName;
|
||||
|
||||
/**
|
||||
* @param path
|
||||
* the resource path to the xml to load for the child context.
|
||||
*/
|
||||
public void setPath(String path) {
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param displayName the display name for the application context created.
|
||||
*/
|
||||
public void setDisplayName(String displayName) {
|
||||
this.displayName = displayName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter for the parent application context.
|
||||
*
|
||||
* @see org.springframework.context.ApplicationContextAware#setApplicationContext(org.springframework.context.ApplicationContext)
|
||||
*/
|
||||
public void setApplicationContext(ApplicationContext applicationContext)
|
||||
throws BeansException {
|
||||
parent = applicationContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stash the {@link BundleContext} for creating a job application context
|
||||
* later.
|
||||
*
|
||||
* @see org.springframework.osgi.context.BundleContextAware#setBundleContext(org.osgi.framework.BundleContext)
|
||||
*/
|
||||
public void setBundleContext(BundleContext context) {
|
||||
this.bundleContext = context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an application context from the provided path, using the current
|
||||
* OSGi {@link BundleContext} and the enclosing Spring
|
||||
* {@link ApplicationContext} as a parent context.
|
||||
*
|
||||
* @see ApplicationContextFactory#createApplicationContext()
|
||||
*/
|
||||
public ConfigurableApplicationContext createApplicationContext() {
|
||||
OsgiBundleXmlApplicationContext context = new OsgiBundleXmlApplicationContext(
|
||||
new String[] { path }, parent);
|
||||
String displayName = bundleContext.getBundle().getSymbolicName() + ":" + this.displayName;
|
||||
context.setDisplayName(displayName);
|
||||
context.setBundleContext(bundleContext);
|
||||
context.refresh();
|
||||
return context;
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* 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.springframework.batch.core.configuration.support;
|
||||
|
||||
import org.osgi.framework.BundleContext;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.osgi.context.BundleContextAware;
|
||||
import org.springframework.osgi.context.support.OsgiBundleXmlApplicationContext;
|
||||
|
||||
public class OsgiBundleXmlApplicationContextFactory implements BundleContextAware,
|
||||
ApplicationContextFactory, ApplicationContextAware {
|
||||
|
||||
private BundleContext bundleContext;
|
||||
|
||||
private ApplicationContext parent;
|
||||
|
||||
private String path;
|
||||
|
||||
private String displayName;
|
||||
|
||||
/**
|
||||
* @param path
|
||||
* the resource path to the xml to load for the child context.
|
||||
*/
|
||||
public void setPath(String path) {
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param displayName the display name for the application context created.
|
||||
*/
|
||||
public void setDisplayName(String displayName) {
|
||||
this.displayName = displayName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter for the parent application context.
|
||||
*
|
||||
* @see org.springframework.context.ApplicationContextAware#setApplicationContext(org.springframework.context.ApplicationContext)
|
||||
*/
|
||||
public void setApplicationContext(ApplicationContext applicationContext)
|
||||
throws BeansException {
|
||||
parent = applicationContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stash the {@link BundleContext} for creating a job application context
|
||||
* later.
|
||||
*
|
||||
* @see org.springframework.osgi.context.BundleContextAware#setBundleContext(org.osgi.framework.BundleContext)
|
||||
*/
|
||||
public void setBundleContext(BundleContext context) {
|
||||
this.bundleContext = context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an application context from the provided path, using the current
|
||||
* OSGi {@link BundleContext} and the enclosing Spring
|
||||
* {@link ApplicationContext} as a parent context.
|
||||
*
|
||||
* @see ApplicationContextFactory#createApplicationContext()
|
||||
*/
|
||||
public ConfigurableApplicationContext createApplicationContext() {
|
||||
OsgiBundleXmlApplicationContext context = new OsgiBundleXmlApplicationContext(
|
||||
new String[] { path }, parent);
|
||||
String displayName = bundleContext.getBundle().getSymbolicName() + ":" + this.displayName;
|
||||
context.setDisplayName(displayName);
|
||||
context.setBundleContext(bundleContext);
|
||||
context.refresh();
|
||||
return context;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,47 +1,63 @@
|
||||
package org.springframework.batch.core.explore.support;
|
||||
|
||||
import org.springframework.batch.core.explore.JobExplorer;
|
||||
import org.springframework.batch.core.repository.dao.JobExecutionDao;
|
||||
import org.springframework.batch.core.repository.dao.JobInstanceDao;
|
||||
import org.springframework.batch.core.repository.dao.StepExecutionDao;
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
|
||||
/**
|
||||
* A {@link FactoryBean} that automates the creation of a
|
||||
* {@link SimpleJobExplorer}. Declares abstract methods for providing DAO
|
||||
* object implementations.
|
||||
*
|
||||
* @see JobExplorerFactoryBean
|
||||
* @see MapJobExplorerFactoryBean
|
||||
*
|
||||
* @author Dave Syer
|
||||
*/
|
||||
public abstract class AbstractJobExplorerFactoryBean implements FactoryBean {
|
||||
|
||||
/**
|
||||
* @return fully configured {@link JobInstanceDao} implementation.
|
||||
*/
|
||||
protected abstract JobInstanceDao createJobInstanceDao() throws Exception;
|
||||
|
||||
/**
|
||||
* @return fully configured {@link JobExecutionDao} implementation.
|
||||
*/
|
||||
protected abstract JobExecutionDao createJobExecutionDao() throws Exception;
|
||||
|
||||
protected abstract StepExecutionDao createStepExecutionDao() throws Exception;
|
||||
|
||||
/**
|
||||
* The type of object to be returned from {@link #getObject()}.
|
||||
*
|
||||
* @return JobExplorer.class
|
||||
* @see org.springframework.beans.factory.FactoryBean#getObjectType()
|
||||
*/
|
||||
public Class<JobExplorer> getObjectType() {
|
||||
return JobExplorer.class;
|
||||
}
|
||||
|
||||
public boolean isSingleton() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* 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.springframework.batch.core.explore.support;
|
||||
|
||||
import org.springframework.batch.core.explore.JobExplorer;
|
||||
import org.springframework.batch.core.repository.dao.JobExecutionDao;
|
||||
import org.springframework.batch.core.repository.dao.JobInstanceDao;
|
||||
import org.springframework.batch.core.repository.dao.StepExecutionDao;
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
|
||||
/**
|
||||
* A {@link FactoryBean} that automates the creation of a
|
||||
* {@link SimpleJobExplorer}. Declares abstract methods for providing DAO
|
||||
* object implementations.
|
||||
*
|
||||
* @see JobExplorerFactoryBean
|
||||
* @see MapJobExplorerFactoryBean
|
||||
*
|
||||
* @author Dave Syer
|
||||
*/
|
||||
public abstract class AbstractJobExplorerFactoryBean implements FactoryBean {
|
||||
|
||||
/**
|
||||
* @return fully configured {@link JobInstanceDao} implementation.
|
||||
*/
|
||||
protected abstract JobInstanceDao createJobInstanceDao() throws Exception;
|
||||
|
||||
/**
|
||||
* @return fully configured {@link JobExecutionDao} implementation.
|
||||
*/
|
||||
protected abstract JobExecutionDao createJobExecutionDao() throws Exception;
|
||||
|
||||
protected abstract StepExecutionDao createStepExecutionDao() throws Exception;
|
||||
|
||||
/**
|
||||
* The type of object to be returned from {@link #getObject()}.
|
||||
*
|
||||
* @return JobExplorer.class
|
||||
* @see org.springframework.beans.factory.FactoryBean#getObjectType()
|
||||
*/
|
||||
public Class<JobExplorer> getObjectType() {
|
||||
return JobExplorer.class;
|
||||
}
|
||||
|
||||
public boolean isSingleton() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,38 +1,54 @@
|
||||
package org.springframework.batch.core.explore.support;
|
||||
|
||||
import org.springframework.batch.core.repository.dao.JobExecutionDao;
|
||||
import org.springframework.batch.core.repository.dao.JobInstanceDao;
|
||||
import org.springframework.batch.core.repository.dao.MapJobExecutionDao;
|
||||
import org.springframework.batch.core.repository.dao.MapJobInstanceDao;
|
||||
import org.springframework.batch.core.repository.dao.MapStepExecutionDao;
|
||||
import org.springframework.batch.core.repository.dao.StepExecutionDao;
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
|
||||
/**
|
||||
* A {@link FactoryBean} that automates the creation of a
|
||||
* {@link SimpleJobExplorer} using in-memory DAO implementations.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*/
|
||||
public class MapJobExplorerFactoryBean extends AbstractJobExplorerFactoryBean {
|
||||
|
||||
@Override
|
||||
protected JobExecutionDao createJobExecutionDao() throws Exception {
|
||||
return new MapJobExecutionDao();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JobInstanceDao createJobInstanceDao() throws Exception {
|
||||
return new MapJobInstanceDao();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected StepExecutionDao createStepExecutionDao() throws Exception {
|
||||
return new MapStepExecutionDao();
|
||||
}
|
||||
|
||||
public Object getObject() throws Exception {
|
||||
return new SimpleJobExplorer(createJobInstanceDao(), createJobExecutionDao(), createStepExecutionDao());
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* 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.springframework.batch.core.explore.support;
|
||||
|
||||
import org.springframework.batch.core.repository.dao.JobExecutionDao;
|
||||
import org.springframework.batch.core.repository.dao.JobInstanceDao;
|
||||
import org.springframework.batch.core.repository.dao.MapJobExecutionDao;
|
||||
import org.springframework.batch.core.repository.dao.MapJobInstanceDao;
|
||||
import org.springframework.batch.core.repository.dao.MapStepExecutionDao;
|
||||
import org.springframework.batch.core.repository.dao.StepExecutionDao;
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
|
||||
/**
|
||||
* A {@link FactoryBean} that automates the creation of a
|
||||
* {@link SimpleJobExplorer} using in-memory DAO implementations.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*/
|
||||
public class MapJobExplorerFactoryBean extends AbstractJobExplorerFactoryBean {
|
||||
|
||||
@Override
|
||||
protected JobExecutionDao createJobExecutionDao() throws Exception {
|
||||
return new MapJobExecutionDao();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JobInstanceDao createJobInstanceDao() throws Exception {
|
||||
return new MapJobInstanceDao();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected StepExecutionDao createStepExecutionDao() throws Exception {
|
||||
return new MapStepExecutionDao();
|
||||
}
|
||||
|
||||
public Object getObject() throws Exception {
|
||||
return new SimpleJobExplorer(createJobInstanceDao(), createJobExecutionDao(), createStepExecutionDao());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
* 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.springframework.batch.core.job.flow.support.state;
|
||||
|
||||
import org.springframework.batch.core.job.flow.FlowExecutor;
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
* 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.springframework.batch.core.job.flow.support.state;
|
||||
|
||||
import org.springframework.batch.core.BatchStatus;
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
* 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.springframework.batch.core.job.flow.support.state;
|
||||
|
||||
import org.springframework.batch.core.Step;
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
* 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.springframework.batch.core.launch.support;
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,38 +1,54 @@
|
||||
package org.springframework.batch.core.partition;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.springframework.batch.core.StepExecution;
|
||||
import org.springframework.batch.item.ExecutionContext;
|
||||
|
||||
/**
|
||||
* Interface defining the responsibilities of controlling the execution of a
|
||||
* partitioned {@link StepExecution}. Implementations will need to create a
|
||||
* partition with the {@link StepExecutionSplitter}, and then use an execution
|
||||
* fabric (grid, etc.), to execute the partitioned step. The results of the
|
||||
* executions can be returned raw from remote workers to be aggregated by the
|
||||
* caller.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public interface PartitionHandler {
|
||||
|
||||
/**
|
||||
* Main entry point for {@link PartitionHandler} interface. The splitter
|
||||
* creates all the executions that need to be farmed out, along with their
|
||||
* input parameters (in the form of their {@link ExecutionContext}). The
|
||||
* master step execution is used to identify the partition and group
|
||||
* together the results logically.
|
||||
*
|
||||
* @param stepSplitter a strategy for generating a collection of
|
||||
* {@link StepExecution} instances
|
||||
* @param stepExecution the master step execution for the whole partition
|
||||
* @return a collection of completed {@link StepExecution} instances
|
||||
* @throws Exception if anything goes wrong. This allows implementations to
|
||||
* be liberal and rely on the caller to translate an exception into a step
|
||||
* failure as necessary.
|
||||
*/
|
||||
Collection<StepExecution> handle(StepExecutionSplitter stepSplitter, StepExecution stepExecution) throws Exception;
|
||||
|
||||
}
|
||||
/*
|
||||
* 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.springframework.batch.core.partition;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.springframework.batch.core.StepExecution;
|
||||
import org.springframework.batch.item.ExecutionContext;
|
||||
|
||||
/**
|
||||
* Interface defining the responsibilities of controlling the execution of a
|
||||
* partitioned {@link StepExecution}. Implementations will need to create a
|
||||
* partition with the {@link StepExecutionSplitter}, and then use an execution
|
||||
* fabric (grid, etc.), to execute the partitioned step. The results of the
|
||||
* executions can be returned raw from remote workers to be aggregated by the
|
||||
* caller.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public interface PartitionHandler {
|
||||
|
||||
/**
|
||||
* Main entry point for {@link PartitionHandler} interface. The splitter
|
||||
* creates all the executions that need to be farmed out, along with their
|
||||
* input parameters (in the form of their {@link ExecutionContext}). The
|
||||
* master step execution is used to identify the partition and group
|
||||
* together the results logically.
|
||||
*
|
||||
* @param stepSplitter a strategy for generating a collection of
|
||||
* {@link StepExecution} instances
|
||||
* @param stepExecution the master step execution for the whole partition
|
||||
* @return a collection of completed {@link StepExecution} instances
|
||||
* @throws Exception if anything goes wrong. This allows implementations to
|
||||
* be liberal and rely on the caller to translate an exception into a step
|
||||
* failure as necessary.
|
||||
*/
|
||||
Collection<StepExecution> handle(StepExecutionSplitter stepSplitter, StepExecution stepExecution) throws Exception;
|
||||
|
||||
}
|
||||
|
||||
@@ -1,47 +1,63 @@
|
||||
package org.springframework.batch.core.partition;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.batch.core.JobExecution;
|
||||
import org.springframework.batch.core.JobExecutionException;
|
||||
import org.springframework.batch.core.StepExecution;
|
||||
|
||||
/**
|
||||
* Strategy interface for generating input contexts for a partitioned step
|
||||
* execution independent from the fabric they are going to run on.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public interface StepExecutionSplitter {
|
||||
|
||||
/**
|
||||
* The name of the step configuration that will be executed remotely. Remote
|
||||
* workers are going to execute a the same step for each execution context
|
||||
* in the partition.
|
||||
* @return the name of the step that will execute the business logic
|
||||
*/
|
||||
String getStepName();
|
||||
|
||||
/**
|
||||
* Partition the provided {@link StepExecution} into a set of parallel
|
||||
* executable instances with the same parent {@link JobExecution}. The grid
|
||||
* size will be treated as a hint for the size of the collection to be
|
||||
* returned. It may or may not correspond to the physical size of an
|
||||
* execution grid.<br/>
|
||||
* <br/>
|
||||
*
|
||||
* On a restart clients of the {@link StepExecutionSplitter} should expect
|
||||
* it to reconstitute the state of the last failed execution and only return
|
||||
* those executions that need to be restarted. Thus the grid size hint will
|
||||
* be ignored on a restart.
|
||||
*
|
||||
* @param stepExecution the {@link StepExecution} to be partitioned.
|
||||
* @param gridSize a hint for the splitter if the size of the grid is known
|
||||
* @return a set of {@link StepExecution} instances for remote processing
|
||||
*
|
||||
* @throws JobExecutionException if the split cannot be made
|
||||
*/
|
||||
Set<StepExecution> split(StepExecution stepExecution, int gridSize) throws JobExecutionException;
|
||||
|
||||
/*
|
||||
* 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.springframework.batch.core.partition;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.batch.core.JobExecution;
|
||||
import org.springframework.batch.core.JobExecutionException;
|
||||
import org.springframework.batch.core.StepExecution;
|
||||
|
||||
/**
|
||||
* Strategy interface for generating input contexts for a partitioned step
|
||||
* execution independent from the fabric they are going to run on.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public interface StepExecutionSplitter {
|
||||
|
||||
/**
|
||||
* The name of the step configuration that will be executed remotely. Remote
|
||||
* workers are going to execute a the same step for each execution context
|
||||
* in the partition.
|
||||
* @return the name of the step that will execute the business logic
|
||||
*/
|
||||
String getStepName();
|
||||
|
||||
/**
|
||||
* Partition the provided {@link StepExecution} into a set of parallel
|
||||
* executable instances with the same parent {@link JobExecution}. The grid
|
||||
* size will be treated as a hint for the size of the collection to be
|
||||
* returned. It may or may not correspond to the physical size of an
|
||||
* execution grid.<br/>
|
||||
* <br/>
|
||||
*
|
||||
* On a restart clients of the {@link StepExecutionSplitter} should expect
|
||||
* it to reconstitute the state of the last failed execution and only return
|
||||
* those executions that need to be restarted. Thus the grid size hint will
|
||||
* be ignored on a restart.
|
||||
*
|
||||
* @param stepExecution the {@link StepExecution} to be partitioned.
|
||||
* @param gridSize a hint for the splitter if the size of the grid is known
|
||||
* @return a set of {@link StepExecution} instances for remote processing
|
||||
*
|
||||
* @throws JobExecutionException if the split cannot be made
|
||||
*/
|
||||
Set<StepExecution> split(StepExecution stepExecution, int gridSize) throws JobExecutionException;
|
||||
|
||||
}
|
||||
@@ -1,86 +1,102 @@
|
||||
package org.springframework.batch.core.partition.support;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.springframework.batch.core.BatchStatus;
|
||||
import org.springframework.batch.core.JobExecutionException;
|
||||
import org.springframework.batch.core.Step;
|
||||
import org.springframework.batch.core.StepExecution;
|
||||
import org.springframework.batch.core.partition.PartitionHandler;
|
||||
import org.springframework.batch.core.partition.StepExecutionSplitter;
|
||||
import org.springframework.batch.core.step.AbstractStep;
|
||||
import org.springframework.batch.item.ExecutionContext;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Implementation of {@link Step} which partitions the execution and spreads the
|
||||
* load using a {@link PartitionHandler}.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public class PartitionStep extends AbstractStep {
|
||||
|
||||
private StepExecutionSplitter stepExecutionSplitter;
|
||||
|
||||
private PartitionHandler partitionHandler;
|
||||
|
||||
private StepExecutionAggregator aggregator = new StepExecutionAggregator();
|
||||
|
||||
/**
|
||||
* Public setter for mandatory property {@link PartitionHandler}.
|
||||
* @param partitionHandler the {@link PartitionHandler} to set
|
||||
*/
|
||||
public void setPartitionHandler(PartitionHandler partitionHandler) {
|
||||
this.partitionHandler = partitionHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Public setter for mandatory property {@link StepExecutionSplitter}.
|
||||
* @param stepExecutionSplitter the {@link StepExecutionSplitter} to set
|
||||
*/
|
||||
public void setStepExecutionSplitter(StepExecutionSplitter stepExecutionSplitter) {
|
||||
this.stepExecutionSplitter = stepExecutionSplitter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert that mandatory properties are set (stepExecutionSplitter,
|
||||
* partitionHandler) and delegate top superclass.
|
||||
*
|
||||
* @see AbstractStep#afterPropertiesSet()
|
||||
*/
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
Assert.notNull(stepExecutionSplitter, "StepExecutionSplitter must be provided");
|
||||
Assert.notNull(partitionHandler, "PartitionHandler must be provided");
|
||||
super.afterPropertiesSet();
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate execution to the {@link PartitionHandler} provided. The
|
||||
* {@link StepExecution} passed in here becomes the parent or master
|
||||
* execution for the partition, summarising the status on exit of the
|
||||
* logical grouping of work carried out by the {@link PartitionHandler}. The
|
||||
* individual step executions and their input parameters (through
|
||||
* {@link ExecutionContext}) for the partition elements are provided by the
|
||||
* {@link StepExecutionSplitter}.
|
||||
*
|
||||
* @param stepExecution the master step execution for the partition
|
||||
*
|
||||
* @see Step#execute(StepExecution)
|
||||
*/
|
||||
@Override
|
||||
protected void doExecute(StepExecution stepExecution) throws Exception {
|
||||
|
||||
// Wait for task completion and then aggregate the results
|
||||
Collection<StepExecution> executions = partitionHandler.handle(stepExecutionSplitter, stepExecution);
|
||||
stepExecution.upgradeStatus(BatchStatus.COMPLETED);
|
||||
aggregator.aggregate(stepExecution, executions);
|
||||
|
||||
// If anything failed or had a problem we need to crap out
|
||||
if (stepExecution.getStatus().isUnsuccessful()) {
|
||||
throw new JobExecutionException("Partition handler returned an unsuccessful step");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* 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.springframework.batch.core.partition.support;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.springframework.batch.core.BatchStatus;
|
||||
import org.springframework.batch.core.JobExecutionException;
|
||||
import org.springframework.batch.core.Step;
|
||||
import org.springframework.batch.core.StepExecution;
|
||||
import org.springframework.batch.core.partition.PartitionHandler;
|
||||
import org.springframework.batch.core.partition.StepExecutionSplitter;
|
||||
import org.springframework.batch.core.step.AbstractStep;
|
||||
import org.springframework.batch.item.ExecutionContext;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Implementation of {@link Step} which partitions the execution and spreads the
|
||||
* load using a {@link PartitionHandler}.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public class PartitionStep extends AbstractStep {
|
||||
|
||||
private StepExecutionSplitter stepExecutionSplitter;
|
||||
|
||||
private PartitionHandler partitionHandler;
|
||||
|
||||
private StepExecutionAggregator aggregator = new StepExecutionAggregator();
|
||||
|
||||
/**
|
||||
* Public setter for mandatory property {@link PartitionHandler}.
|
||||
* @param partitionHandler the {@link PartitionHandler} to set
|
||||
*/
|
||||
public void setPartitionHandler(PartitionHandler partitionHandler) {
|
||||
this.partitionHandler = partitionHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Public setter for mandatory property {@link StepExecutionSplitter}.
|
||||
* @param stepExecutionSplitter the {@link StepExecutionSplitter} to set
|
||||
*/
|
||||
public void setStepExecutionSplitter(StepExecutionSplitter stepExecutionSplitter) {
|
||||
this.stepExecutionSplitter = stepExecutionSplitter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert that mandatory properties are set (stepExecutionSplitter,
|
||||
* partitionHandler) and delegate top superclass.
|
||||
*
|
||||
* @see AbstractStep#afterPropertiesSet()
|
||||
*/
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
Assert.notNull(stepExecutionSplitter, "StepExecutionSplitter must be provided");
|
||||
Assert.notNull(partitionHandler, "PartitionHandler must be provided");
|
||||
super.afterPropertiesSet();
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate execution to the {@link PartitionHandler} provided. The
|
||||
* {@link StepExecution} passed in here becomes the parent or master
|
||||
* execution for the partition, summarising the status on exit of the
|
||||
* logical grouping of work carried out by the {@link PartitionHandler}. The
|
||||
* individual step executions and their input parameters (through
|
||||
* {@link ExecutionContext}) for the partition elements are provided by the
|
||||
* {@link StepExecutionSplitter}.
|
||||
*
|
||||
* @param stepExecution the master step execution for the partition
|
||||
*
|
||||
* @see Step#execute(StepExecution)
|
||||
*/
|
||||
@Override
|
||||
protected void doExecute(StepExecution stepExecution) throws Exception {
|
||||
|
||||
// Wait for task completion and then aggregate the results
|
||||
Collection<StepExecution> executions = partitionHandler.handle(stepExecutionSplitter, stepExecution);
|
||||
stepExecution.upgradeStatus(BatchStatus.COMPLETED);
|
||||
aggregator.aggregate(stepExecution, executions);
|
||||
|
||||
// If anything failed or had a problem we need to crap out
|
||||
if (stepExecution.getStatus().isUnsuccessful()) {
|
||||
throw new JobExecutionException("Partition handler returned an unsuccessful step");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,29 +1,45 @@
|
||||
package org.springframework.batch.core.partition.support;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.batch.item.ExecutionContext;
|
||||
|
||||
/**
|
||||
* Central strategy interface for creating input parameters for a partitioned
|
||||
* step in the form of {@link ExecutionContext} instances. The usual aim is to
|
||||
* create a set of distinct input values, e.g. a set of non-overlapping primary
|
||||
* key ranges, or a set of unique filenames.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public interface Partitioner {
|
||||
|
||||
/**
|
||||
* Create a set of distinct {@link ExecutionContext} instances together with
|
||||
* a unique identifier for each one. The identifiers should be short,
|
||||
* mnemonic values, and only have to be unique within the return value (e.g.
|
||||
* use an incrementer).
|
||||
*
|
||||
* @param gridSize the size of the map to return
|
||||
* @return a map from identifier to input parameters
|
||||
*/
|
||||
Map<String, ExecutionContext> partition(int gridSize);
|
||||
|
||||
}
|
||||
/*
|
||||
* 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.springframework.batch.core.partition.support;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.batch.item.ExecutionContext;
|
||||
|
||||
/**
|
||||
* Central strategy interface for creating input parameters for a partitioned
|
||||
* step in the form of {@link ExecutionContext} instances. The usual aim is to
|
||||
* create a set of distinct input values, e.g. a set of non-overlapping primary
|
||||
* key ranges, or a set of unique filenames.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public interface Partitioner {
|
||||
|
||||
/**
|
||||
* Create a set of distinct {@link ExecutionContext} instances together with
|
||||
* a unique identifier for each one. The identifiers should be short,
|
||||
* mnemonic values, and only have to be unique within the return value (e.g.
|
||||
* use an incrementer).
|
||||
*
|
||||
* @param gridSize the size of the map to return
|
||||
* @return a map from identifier to input parameters
|
||||
*/
|
||||
Map<String, ExecutionContext> partition(int gridSize);
|
||||
|
||||
}
|
||||
|
||||
@@ -1,29 +1,45 @@
|
||||
package org.springframework.batch.core.partition.support;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.batch.item.ExecutionContext;
|
||||
|
||||
/**
|
||||
* Simplest possible implementation of {@link Partitioner}. Just creates a set
|
||||
* of empty {@link ExecutionContext} instances, and labels them as
|
||||
* <code>{partition0, partition1, ..., partitionN}</code>, where <code>N</code> is the grid
|
||||
* size.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public class SimplePartitioner implements Partitioner {
|
||||
|
||||
private static final String PARTITION_KEY = "partition";
|
||||
|
||||
public Map<String, ExecutionContext> partition(int gridSize) {
|
||||
Map<String, ExecutionContext> map = new HashMap<String, ExecutionContext>(gridSize);
|
||||
for (int i = 0; i < gridSize; i++) {
|
||||
map.put(PARTITION_KEY + i, new ExecutionContext());
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* 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.springframework.batch.core.partition.support;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.batch.item.ExecutionContext;
|
||||
|
||||
/**
|
||||
* Simplest possible implementation of {@link Partitioner}. Just creates a set
|
||||
* of empty {@link ExecutionContext} instances, and labels them as
|
||||
* <code>{partition0, partition1, ..., partitionN}</code>, where <code>N</code> is the grid
|
||||
* size.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public class SimplePartitioner implements Partitioner {
|
||||
|
||||
private static final String PARTITION_KEY = "partition";
|
||||
|
||||
public Map<String, ExecutionContext> partition(int gridSize) {
|
||||
Map<String, ExecutionContext> map = new HashMap<String, ExecutionContext>(gridSize);
|
||||
for (int i = 0; i < gridSize; i++) {
|
||||
map.put(PARTITION_KEY + i, new ExecutionContext());
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,159 +1,175 @@
|
||||
package org.springframework.batch.core.partition.support;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import org.springframework.batch.core.BatchStatus;
|
||||
import org.springframework.batch.core.JobExecution;
|
||||
import org.springframework.batch.core.JobExecutionException;
|
||||
import org.springframework.batch.core.JobInstance;
|
||||
import org.springframework.batch.core.Step;
|
||||
import org.springframework.batch.core.StepExecution;
|
||||
import org.springframework.batch.core.partition.StepExecutionSplitter;
|
||||
import org.springframework.batch.core.repository.JobRepository;
|
||||
import org.springframework.batch.item.ExecutionContext;
|
||||
|
||||
/**
|
||||
* Generic implementation of {@link StepExecutionSplitter} that delegates to a
|
||||
* {@link Partitioner} to generate {@link ExecutionContext} instances. Takes
|
||||
* care of restartability and identifying the step executions from previous runs
|
||||
* of the same job. The generated {@link StepExecution} instances have names
|
||||
* that identify them uniquely in the partition. The name is constructed from a
|
||||
* base (name of the target step) plus a suffix taken from the
|
||||
* {@link Partitioner} identifiers, separated by a colon, e.g.
|
||||
* <code>{step1:partition0, step1:partition1, ...}</code>.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public class SimpleStepExecutionSplitter implements StepExecutionSplitter {
|
||||
|
||||
private static final String STEP_NAME_SEPARATOR = ":";
|
||||
|
||||
private final String stepName;
|
||||
|
||||
private final Partitioner partitioner;
|
||||
|
||||
private final Step step;
|
||||
|
||||
private final JobRepository jobRepository;
|
||||
|
||||
public SimpleStepExecutionSplitter(JobRepository jobRepository, Step step) {
|
||||
this(jobRepository, step, new SimplePartitioner());
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a {@link SimpleStepExecutionSplitter} from its mandatory
|
||||
* properties.
|
||||
*
|
||||
* @param jobRepository the {@link JobRepository}
|
||||
* @param step the target step (a local version of it)
|
||||
* @param partitioner a {@link Partitioner} to use for generating input
|
||||
* parameters
|
||||
*/
|
||||
public SimpleStepExecutionSplitter(JobRepository jobRepository, Step step, Partitioner partitioner) {
|
||||
this.jobRepository = jobRepository;
|
||||
this.step = step;
|
||||
this.partitioner = partitioner;
|
||||
this.stepName = step.getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see StepExecutionSplitter#getStepName()
|
||||
*/
|
||||
public String getStepName() {
|
||||
return this.stepName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see StepExecutionSplitter#split(StepExecution, int)
|
||||
*/
|
||||
public Set<StepExecution> split(StepExecution stepExecution, int gridSize) throws JobExecutionException {
|
||||
|
||||
JobExecution jobExecution = stepExecution.getJobExecution();
|
||||
|
||||
// If this is a restart we must retain the same grid size, ignoring the
|
||||
// one passed in...
|
||||
int splitSize = getSplitSize(stepExecution, gridSize);
|
||||
|
||||
Map<String, ExecutionContext> contexts = partitioner.partition(splitSize);
|
||||
Set<StepExecution> set = new HashSet<StepExecution>(contexts.size());
|
||||
|
||||
for (Entry<String, ExecutionContext> context : contexts.entrySet()) {
|
||||
|
||||
// Make the step execution name unique and repeatable
|
||||
String stepName = this.stepName + STEP_NAME_SEPARATOR + context.getKey();
|
||||
|
||||
StepExecution currentStepExecution = jobExecution.createStepExecution(stepName);
|
||||
|
||||
boolean startable = getStartable(currentStepExecution, context.getValue());
|
||||
|
||||
if (startable) {
|
||||
jobRepository.add(currentStepExecution);
|
||||
set.add(currentStepExecution);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return set;
|
||||
|
||||
}
|
||||
|
||||
private int getSplitSize(StepExecution stepExecution, int gridSize) {
|
||||
ExecutionContext context = stepExecution.getExecutionContext();
|
||||
String key = SimpleStepExecutionSplitter.class.getSimpleName() + ".GRID_SIZE";
|
||||
int result = (int) context.getLong(key, gridSize);
|
||||
context.putLong(key, result);
|
||||
if (context.isDirty()) {
|
||||
jobRepository.updateExecutionContext(stepExecution);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private boolean getStartable(StepExecution stepExecution, ExecutionContext context) throws JobExecutionException {
|
||||
|
||||
JobInstance jobInstance = stepExecution.getJobExecution().getJobInstance();
|
||||
String stepName = stepExecution.getStepName();
|
||||
StepExecution lastStepExecution = jobRepository.getLastStepExecution(jobInstance, stepName);
|
||||
|
||||
boolean isRestart = (lastStepExecution != null && lastStepExecution.getStatus() != BatchStatus.COMPLETED) ? true
|
||||
: false;
|
||||
|
||||
if (isRestart) {
|
||||
stepExecution.setExecutionContext(lastStepExecution.getExecutionContext());
|
||||
}
|
||||
else {
|
||||
stepExecution.setExecutionContext(context);
|
||||
}
|
||||
|
||||
return shouldStart(step, lastStepExecution) || isRestart;
|
||||
|
||||
}
|
||||
|
||||
private boolean shouldStart(Step step, StepExecution lastStepExecution) throws JobExecutionException {
|
||||
|
||||
if (lastStepExecution == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
BatchStatus stepStatus = lastStepExecution.getStatus();
|
||||
|
||||
if (stepStatus == BatchStatus.UNKNOWN) {
|
||||
throw new JobExecutionException("Cannot restart step from UNKNOWN status. "
|
||||
+ "The last execution ended with a failure that could not be rolled back, "
|
||||
+ "so it may be dangerous to proceed. " + "Manual intervention is probably necessary.");
|
||||
}
|
||||
|
||||
if (stepStatus == BatchStatus.COMPLETED && step.isAllowStartIfComplete() == false) {
|
||||
// step is complete, false should be returned, indicating that the
|
||||
// step should not be started
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* 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.springframework.batch.core.partition.support;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import org.springframework.batch.core.BatchStatus;
|
||||
import org.springframework.batch.core.JobExecution;
|
||||
import org.springframework.batch.core.JobExecutionException;
|
||||
import org.springframework.batch.core.JobInstance;
|
||||
import org.springframework.batch.core.Step;
|
||||
import org.springframework.batch.core.StepExecution;
|
||||
import org.springframework.batch.core.partition.StepExecutionSplitter;
|
||||
import org.springframework.batch.core.repository.JobRepository;
|
||||
import org.springframework.batch.item.ExecutionContext;
|
||||
|
||||
/**
|
||||
* Generic implementation of {@link StepExecutionSplitter} that delegates to a
|
||||
* {@link Partitioner} to generate {@link ExecutionContext} instances. Takes
|
||||
* care of restartability and identifying the step executions from previous runs
|
||||
* of the same job. The generated {@link StepExecution} instances have names
|
||||
* that identify them uniquely in the partition. The name is constructed from a
|
||||
* base (name of the target step) plus a suffix taken from the
|
||||
* {@link Partitioner} identifiers, separated by a colon, e.g.
|
||||
* <code>{step1:partition0, step1:partition1, ...}</code>.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public class SimpleStepExecutionSplitter implements StepExecutionSplitter {
|
||||
|
||||
private static final String STEP_NAME_SEPARATOR = ":";
|
||||
|
||||
private final String stepName;
|
||||
|
||||
private final Partitioner partitioner;
|
||||
|
||||
private final Step step;
|
||||
|
||||
private final JobRepository jobRepository;
|
||||
|
||||
public SimpleStepExecutionSplitter(JobRepository jobRepository, Step step) {
|
||||
this(jobRepository, step, new SimplePartitioner());
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a {@link SimpleStepExecutionSplitter} from its mandatory
|
||||
* properties.
|
||||
*
|
||||
* @param jobRepository the {@link JobRepository}
|
||||
* @param step the target step (a local version of it)
|
||||
* @param partitioner a {@link Partitioner} to use for generating input
|
||||
* parameters
|
||||
*/
|
||||
public SimpleStepExecutionSplitter(JobRepository jobRepository, Step step, Partitioner partitioner) {
|
||||
this.jobRepository = jobRepository;
|
||||
this.step = step;
|
||||
this.partitioner = partitioner;
|
||||
this.stepName = step.getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see StepExecutionSplitter#getStepName()
|
||||
*/
|
||||
public String getStepName() {
|
||||
return this.stepName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see StepExecutionSplitter#split(StepExecution, int)
|
||||
*/
|
||||
public Set<StepExecution> split(StepExecution stepExecution, int gridSize) throws JobExecutionException {
|
||||
|
||||
JobExecution jobExecution = stepExecution.getJobExecution();
|
||||
|
||||
// If this is a restart we must retain the same grid size, ignoring the
|
||||
// one passed in...
|
||||
int splitSize = getSplitSize(stepExecution, gridSize);
|
||||
|
||||
Map<String, ExecutionContext> contexts = partitioner.partition(splitSize);
|
||||
Set<StepExecution> set = new HashSet<StepExecution>(contexts.size());
|
||||
|
||||
for (Entry<String, ExecutionContext> context : contexts.entrySet()) {
|
||||
|
||||
// Make the step execution name unique and repeatable
|
||||
String stepName = this.stepName + STEP_NAME_SEPARATOR + context.getKey();
|
||||
|
||||
StepExecution currentStepExecution = jobExecution.createStepExecution(stepName);
|
||||
|
||||
boolean startable = getStartable(currentStepExecution, context.getValue());
|
||||
|
||||
if (startable) {
|
||||
jobRepository.add(currentStepExecution);
|
||||
set.add(currentStepExecution);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return set;
|
||||
|
||||
}
|
||||
|
||||
private int getSplitSize(StepExecution stepExecution, int gridSize) {
|
||||
ExecutionContext context = stepExecution.getExecutionContext();
|
||||
String key = SimpleStepExecutionSplitter.class.getSimpleName() + ".GRID_SIZE";
|
||||
int result = (int) context.getLong(key, gridSize);
|
||||
context.putLong(key, result);
|
||||
if (context.isDirty()) {
|
||||
jobRepository.updateExecutionContext(stepExecution);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private boolean getStartable(StepExecution stepExecution, ExecutionContext context) throws JobExecutionException {
|
||||
|
||||
JobInstance jobInstance = stepExecution.getJobExecution().getJobInstance();
|
||||
String stepName = stepExecution.getStepName();
|
||||
StepExecution lastStepExecution = jobRepository.getLastStepExecution(jobInstance, stepName);
|
||||
|
||||
boolean isRestart = (lastStepExecution != null && lastStepExecution.getStatus() != BatchStatus.COMPLETED) ? true
|
||||
: false;
|
||||
|
||||
if (isRestart) {
|
||||
stepExecution.setExecutionContext(lastStepExecution.getExecutionContext());
|
||||
}
|
||||
else {
|
||||
stepExecution.setExecutionContext(context);
|
||||
}
|
||||
|
||||
return shouldStart(step, lastStepExecution) || isRestart;
|
||||
|
||||
}
|
||||
|
||||
private boolean shouldStart(Step step, StepExecution lastStepExecution) throws JobExecutionException {
|
||||
|
||||
if (lastStepExecution == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
BatchStatus stepStatus = lastStepExecution.getStatus();
|
||||
|
||||
if (stepStatus == BatchStatus.UNKNOWN) {
|
||||
throw new JobExecutionException("Cannot restart step from UNKNOWN status. "
|
||||
+ "The last execution ended with a failure that could not be rolled back, "
|
||||
+ "so it may be dangerous to proceed. " + "Manual intervention is probably necessary.");
|
||||
}
|
||||
|
||||
if (stepStatus == BatchStatus.COMPLETED && step.isAllowStartIfComplete() == false) {
|
||||
// step is complete, false should be returned, indicating that the
|
||||
// step should not be started
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,49 +1,65 @@
|
||||
package org.springframework.batch.core.partition.support;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.springframework.batch.core.BatchStatus;
|
||||
import org.springframework.batch.core.ExitStatus;
|
||||
import org.springframework.batch.core.StepExecution;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Convenience class for aggregating a set of {@link StepExecution} instances
|
||||
* into a single result.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public class StepExecutionAggregator {
|
||||
|
||||
/**
|
||||
* Take the inputs and aggregate certain fields, putting the aggregates into
|
||||
* the result. The aggregated fields are
|
||||
* <ul>
|
||||
* <li>status - choosing the highest value using {@link BatchStatus#max(BatchStatus, BatchStatus)}</li>
|
||||
* <li>exitStatus - using {@link ExitStatus#and(ExitStatus)}</li>
|
||||
* <li>commitCount, rollbackCount, etc. - by arithmetic sum</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param result the result to overwrite
|
||||
* @param executions the inputs
|
||||
*/
|
||||
public void aggregate(StepExecution result, Collection<StepExecution> executions) {
|
||||
Assert.notNull(result, "To aggregate into a result it must be non-null.");
|
||||
if (executions == null || executions.isEmpty()) {
|
||||
throw new IllegalArgumentException("Cannot aggregate empty or null executions: " + executions);
|
||||
}
|
||||
for (StepExecution stepExecution : executions) {
|
||||
BatchStatus status = stepExecution.getStatus();
|
||||
result.setStatus(BatchStatus.max(result.getStatus(), status));
|
||||
result.setExitStatus(result.getExitStatus().and(stepExecution.getExitStatus()));
|
||||
result.setCommitCount(result.getCommitCount() + stepExecution.getCommitCount());
|
||||
result.setRollbackCount(result.getRollbackCount() + stepExecution.getRollbackCount());
|
||||
result.setReadCount(result.getReadCount() + stepExecution.getReadCount());
|
||||
result.setReadSkipCount(result.getReadSkipCount() + stepExecution.getReadSkipCount());
|
||||
result.setWriteCount(result.getWriteCount() + stepExecution.getWriteCount());
|
||||
result.setWriteSkipCount(result.getWriteSkipCount() + stepExecution.getWriteSkipCount());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* 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.springframework.batch.core.partition.support;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.springframework.batch.core.BatchStatus;
|
||||
import org.springframework.batch.core.ExitStatus;
|
||||
import org.springframework.batch.core.StepExecution;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Convenience class for aggregating a set of {@link StepExecution} instances
|
||||
* into a single result.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public class StepExecutionAggregator {
|
||||
|
||||
/**
|
||||
* Take the inputs and aggregate certain fields, putting the aggregates into
|
||||
* the result. The aggregated fields are
|
||||
* <ul>
|
||||
* <li>status - choosing the highest value using {@link BatchStatus#max(BatchStatus, BatchStatus)}</li>
|
||||
* <li>exitStatus - using {@link ExitStatus#and(ExitStatus)}</li>
|
||||
* <li>commitCount, rollbackCount, etc. - by arithmetic sum</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param result the result to overwrite
|
||||
* @param executions the inputs
|
||||
*/
|
||||
public void aggregate(StepExecution result, Collection<StepExecution> executions) {
|
||||
Assert.notNull(result, "To aggregate into a result it must be non-null.");
|
||||
if (executions == null || executions.isEmpty()) {
|
||||
throw new IllegalArgumentException("Cannot aggregate empty or null executions: " + executions);
|
||||
}
|
||||
for (StepExecution stepExecution : executions) {
|
||||
BatchStatus status = stepExecution.getStatus();
|
||||
result.setStatus(BatchStatus.max(result.getStatus(), status));
|
||||
result.setExitStatus(result.getExitStatus().and(stepExecution.getExitStatus()));
|
||||
result.setCommitCount(result.getCommitCount() + stepExecution.getCommitCount());
|
||||
result.setRollbackCount(result.getRollbackCount() + stepExecution.getRollbackCount());
|
||||
result.setReadCount(result.getReadCount() + stepExecution.getReadCount());
|
||||
result.setReadSkipCount(result.getReadSkipCount() + stepExecution.getReadSkipCount());
|
||||
result.setWriteCount(result.getWriteCount() + stepExecution.getWriteCount());
|
||||
result.setWriteSkipCount(result.getWriteSkipCount() + stepExecution.getWriteSkipCount());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,92 +1,108 @@
|
||||
package org.springframework.batch.core.partition.support;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.FutureTask;
|
||||
|
||||
import org.springframework.batch.core.BatchStatus;
|
||||
import org.springframework.batch.core.ExitStatus;
|
||||
import org.springframework.batch.core.Step;
|
||||
import org.springframework.batch.core.StepExecution;
|
||||
import org.springframework.batch.core.partition.PartitionHandler;
|
||||
import org.springframework.batch.core.partition.StepExecutionSplitter;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.core.task.SyncTaskExecutor;
|
||||
import org.springframework.core.task.TaskExecutor;
|
||||
import org.springframework.core.task.TaskRejectedException;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
public class TaskExecutorPartitionHandler implements PartitionHandler, InitializingBean {
|
||||
|
||||
private int gridSize = 1;
|
||||
|
||||
private TaskExecutor taskExecutor = new SyncTaskExecutor();
|
||||
|
||||
private Step step;
|
||||
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
Assert.notNull(step, "A Step must be provided.");
|
||||
}
|
||||
|
||||
public void setGridSize(int gridSize) {
|
||||
this.gridSize = gridSize;
|
||||
}
|
||||
|
||||
public void setTaskExecutor(TaskExecutor taskExecutor) {
|
||||
this.taskExecutor = taskExecutor;
|
||||
}
|
||||
|
||||
public void setStep(Step step) {
|
||||
this.step = step;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see PartitionHandler#handle(StepExecutionSplitter, StepExecution)
|
||||
*/
|
||||
public Collection<StepExecution> handle(StepExecutionSplitter stepExecutionSplitter,
|
||||
StepExecution masterStepExecution) throws Exception {
|
||||
|
||||
Set<Future<StepExecution>> tasks = new HashSet<Future<StepExecution>>(gridSize);
|
||||
|
||||
Collection<StepExecution> result = new ArrayList<StepExecution>();
|
||||
|
||||
for (final StepExecution stepExecution : stepExecutionSplitter.split(masterStepExecution, gridSize)) {
|
||||
|
||||
final FutureTask<StepExecution> task = new FutureTask<StepExecution>(new Callable<StepExecution>() {
|
||||
public StepExecution call() throws Exception {
|
||||
step.execute(stepExecution);
|
||||
return stepExecution;
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
taskExecutor.execute(task);
|
||||
tasks.add(task);
|
||||
}
|
||||
catch (TaskRejectedException e) {
|
||||
// couldn't execute one of the tasks
|
||||
ExitStatus exitStatus = ExitStatus.FAILED
|
||||
.addExitDescription("TaskExecutor rejected the task for this step.");
|
||||
/*
|
||||
* Set the status in case the caller is tracking it through the
|
||||
* JobExecution.
|
||||
*/
|
||||
stepExecution.setStatus(BatchStatus.FAILED);
|
||||
stepExecution.setExitStatus(exitStatus);
|
||||
result.add(stepExecution);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
for (Future<StepExecution> task : tasks) {
|
||||
result.add(task.get());
|
||||
}
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* 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.springframework.batch.core.partition.support;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.FutureTask;
|
||||
|
||||
import org.springframework.batch.core.BatchStatus;
|
||||
import org.springframework.batch.core.ExitStatus;
|
||||
import org.springframework.batch.core.Step;
|
||||
import org.springframework.batch.core.StepExecution;
|
||||
import org.springframework.batch.core.partition.PartitionHandler;
|
||||
import org.springframework.batch.core.partition.StepExecutionSplitter;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.core.task.SyncTaskExecutor;
|
||||
import org.springframework.core.task.TaskExecutor;
|
||||
import org.springframework.core.task.TaskRejectedException;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
public class TaskExecutorPartitionHandler implements PartitionHandler, InitializingBean {
|
||||
|
||||
private int gridSize = 1;
|
||||
|
||||
private TaskExecutor taskExecutor = new SyncTaskExecutor();
|
||||
|
||||
private Step step;
|
||||
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
Assert.notNull(step, "A Step must be provided.");
|
||||
}
|
||||
|
||||
public void setGridSize(int gridSize) {
|
||||
this.gridSize = gridSize;
|
||||
}
|
||||
|
||||
public void setTaskExecutor(TaskExecutor taskExecutor) {
|
||||
this.taskExecutor = taskExecutor;
|
||||
}
|
||||
|
||||
public void setStep(Step step) {
|
||||
this.step = step;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see PartitionHandler#handle(StepExecutionSplitter, StepExecution)
|
||||
*/
|
||||
public Collection<StepExecution> handle(StepExecutionSplitter stepExecutionSplitter,
|
||||
StepExecution masterStepExecution) throws Exception {
|
||||
|
||||
Set<Future<StepExecution>> tasks = new HashSet<Future<StepExecution>>(gridSize);
|
||||
|
||||
Collection<StepExecution> result = new ArrayList<StepExecution>();
|
||||
|
||||
for (final StepExecution stepExecution : stepExecutionSplitter.split(masterStepExecution, gridSize)) {
|
||||
|
||||
final FutureTask<StepExecution> task = new FutureTask<StepExecution>(new Callable<StepExecution>() {
|
||||
public StepExecution call() throws Exception {
|
||||
step.execute(stepExecution);
|
||||
return stepExecution;
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
taskExecutor.execute(task);
|
||||
tasks.add(task);
|
||||
}
|
||||
catch (TaskRejectedException e) {
|
||||
// couldn't execute one of the tasks
|
||||
ExitStatus exitStatus = ExitStatus.FAILED
|
||||
.addExitDescription("TaskExecutor rejected the task for this step.");
|
||||
/*
|
||||
* Set the status in case the caller is tracking it through the
|
||||
* JobExecution.
|
||||
*/
|
||||
stepExecution.setStatus(BatchStatus.FAILED);
|
||||
stepExecution.setExitStatus(exitStatus);
|
||||
result.add(stepExecution);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
for (Future<StepExecution> task : tasks) {
|
||||
result.add(task.get());
|
||||
}
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
* 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.springframework.batch.core.repository.dao;
|
||||
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
|
||||
@@ -1,37 +1,53 @@
|
||||
package org.springframework.batch.core.repository.dao;
|
||||
|
||||
import org.springframework.batch.core.JobExecution;
|
||||
import org.springframework.batch.core.StepExecution;
|
||||
import org.springframework.batch.item.ExecutionContext;
|
||||
|
||||
/**
|
||||
* DAO interface for persisting and retrieving {@link ExecutionContext}s.
|
||||
*
|
||||
* @author Robert Kasanicky
|
||||
*/
|
||||
public interface ExecutionContextDao {
|
||||
|
||||
/**
|
||||
* @param jobExecution
|
||||
* @return execution context associated with the given jobExecution
|
||||
*/
|
||||
ExecutionContext getExecutionContext(JobExecution jobExecution);
|
||||
|
||||
/**
|
||||
* @param stepExecution
|
||||
* @return execution context associated with the given stepExecution
|
||||
*/
|
||||
ExecutionContext getExecutionContext(StepExecution stepExecution);
|
||||
|
||||
/**
|
||||
* Persist the execution context associated with the given jobExecution
|
||||
* @param jobExecution
|
||||
*/
|
||||
void persistExecutionContext(final JobExecution jobExecution);
|
||||
|
||||
/**
|
||||
* Persist the execution context associated with the given stepExecution
|
||||
* @param stepExecution
|
||||
*/
|
||||
void persistExecutionContext(final StepExecution stepExecution);
|
||||
}
|
||||
/*
|
||||
* 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.springframework.batch.core.repository.dao;
|
||||
|
||||
import org.springframework.batch.core.JobExecution;
|
||||
import org.springframework.batch.core.StepExecution;
|
||||
import org.springframework.batch.item.ExecutionContext;
|
||||
|
||||
/**
|
||||
* DAO interface for persisting and retrieving {@link ExecutionContext}s.
|
||||
*
|
||||
* @author Robert Kasanicky
|
||||
*/
|
||||
public interface ExecutionContextDao {
|
||||
|
||||
/**
|
||||
* @param jobExecution
|
||||
* @return execution context associated with the given jobExecution
|
||||
*/
|
||||
ExecutionContext getExecutionContext(JobExecution jobExecution);
|
||||
|
||||
/**
|
||||
* @param stepExecution
|
||||
* @return execution context associated with the given stepExecution
|
||||
*/
|
||||
ExecutionContext getExecutionContext(StepExecution stepExecution);
|
||||
|
||||
/**
|
||||
* Persist the execution context associated with the given jobExecution
|
||||
* @param jobExecution
|
||||
*/
|
||||
void persistExecutionContext(final JobExecution jobExecution);
|
||||
|
||||
/**
|
||||
* Persist the execution context associated with the given stepExecution
|
||||
* @param stepExecution
|
||||
*/
|
||||
void persistExecutionContext(final StepExecution stepExecution);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
* 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.springframework.batch.core.repository.dao;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
* 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.springframework.batch.core.repository.dao;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
* 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.springframework.batch.core.repository.dao;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
* 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.springframework.batch.core.repository.dao;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
* 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.springframework.batch.core.repository.dao;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -1,44 +1,60 @@
|
||||
package org.springframework.batch.core.repository.dao;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.commons.lang.SerializationUtils;
|
||||
import org.springframework.batch.core.JobExecution;
|
||||
import org.springframework.batch.core.StepExecution;
|
||||
import org.springframework.batch.item.ExecutionContext;
|
||||
import org.springframework.batch.support.transaction.TransactionAwareProxyFactory;
|
||||
|
||||
public class MapExecutionContextDao implements ExecutionContextDao {
|
||||
|
||||
private static Map<Long, ExecutionContext> contextsByStepExecutionId = TransactionAwareProxyFactory.createTransactionalMap();
|
||||
|
||||
private static Map<Long, ExecutionContext> contextsByJobExecutionId = TransactionAwareProxyFactory.createTransactionalMap();
|
||||
|
||||
public static void clear() {
|
||||
contextsByJobExecutionId.clear();
|
||||
contextsByStepExecutionId.clear();
|
||||
}
|
||||
|
||||
private static ExecutionContext copy(ExecutionContext original) {
|
||||
return (ExecutionContext) SerializationUtils.deserialize(SerializationUtils.serialize(original));
|
||||
}
|
||||
|
||||
|
||||
public ExecutionContext getExecutionContext(StepExecution stepExecution) {
|
||||
return copy(contextsByStepExecutionId.get(stepExecution.getId()));
|
||||
}
|
||||
|
||||
public void persistExecutionContext(StepExecution stepExecution) {
|
||||
contextsByStepExecutionId.put(stepExecution.getId(), copy(stepExecution.getExecutionContext()));
|
||||
}
|
||||
|
||||
public ExecutionContext getExecutionContext(JobExecution jobExecution) {
|
||||
return copy(contextsByJobExecutionId.get(jobExecution.getId()));
|
||||
}
|
||||
|
||||
public void persistExecutionContext(JobExecution jobExecution) {
|
||||
contextsByJobExecutionId.put(jobExecution.getId(), copy(jobExecution.getExecutionContext()));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* 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.springframework.batch.core.repository.dao;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.commons.lang.SerializationUtils;
|
||||
import org.springframework.batch.core.JobExecution;
|
||||
import org.springframework.batch.core.StepExecution;
|
||||
import org.springframework.batch.item.ExecutionContext;
|
||||
import org.springframework.batch.support.transaction.TransactionAwareProxyFactory;
|
||||
|
||||
public class MapExecutionContextDao implements ExecutionContextDao {
|
||||
|
||||
private static Map<Long, ExecutionContext> contextsByStepExecutionId = TransactionAwareProxyFactory.createTransactionalMap();
|
||||
|
||||
private static Map<Long, ExecutionContext> contextsByJobExecutionId = TransactionAwareProxyFactory.createTransactionalMap();
|
||||
|
||||
public static void clear() {
|
||||
contextsByJobExecutionId.clear();
|
||||
contextsByStepExecutionId.clear();
|
||||
}
|
||||
|
||||
private static ExecutionContext copy(ExecutionContext original) {
|
||||
return (ExecutionContext) SerializationUtils.deserialize(SerializationUtils.serialize(original));
|
||||
}
|
||||
|
||||
|
||||
public ExecutionContext getExecutionContext(StepExecution stepExecution) {
|
||||
return copy(contextsByStepExecutionId.get(stepExecution.getId()));
|
||||
}
|
||||
|
||||
public void persistExecutionContext(StepExecution stepExecution) {
|
||||
contextsByStepExecutionId.put(stepExecution.getId(), copy(stepExecution.getExecutionContext()));
|
||||
}
|
||||
|
||||
public ExecutionContext getExecutionContext(JobExecution jobExecution) {
|
||||
return copy(contextsByJobExecutionId.get(jobExecution.getId()));
|
||||
}
|
||||
|
||||
public void persistExecutionContext(JobExecution jobExecution) {
|
||||
contextsByJobExecutionId.put(jobExecution.getId(), copy(jobExecution.getExecutionContext()));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
* 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.springframework.batch.core.repository.dao;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
* 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.springframework.batch.core.repository.dao;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
* 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.springframework.batch.core.repository.dao;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -1,134 +1,150 @@
|
||||
package org.springframework.batch.core.repository.support;
|
||||
|
||||
import org.springframework.aop.framework.ProxyFactory;
|
||||
import org.springframework.aop.support.DefaultPointcutAdvisor;
|
||||
import org.springframework.aop.support.NameMatchMethodPointcut;
|
||||
import org.springframework.batch.core.repository.JobRepository;
|
||||
import org.springframework.batch.core.repository.dao.ExecutionContextDao;
|
||||
import org.springframework.batch.core.repository.dao.JobExecutionDao;
|
||||
import org.springframework.batch.core.repository.dao.JobInstanceDao;
|
||||
import org.springframework.batch.core.repository.dao.StepExecutionDao;
|
||||
import org.springframework.batch.support.PropertiesConverter;
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.transaction.interceptor.TransactionInterceptor;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A {@link FactoryBean} that automates the creation of a
|
||||
* {@link SimpleJobRepository}. Declares abstract methods for providing DAO
|
||||
* object implementations.
|
||||
*
|
||||
* @see JobRepositoryFactoryBean
|
||||
* @see MapJobRepositoryFactoryBean
|
||||
*
|
||||
* @author Ben Hale
|
||||
* @author Lucas Ward
|
||||
* @author Robert Kasanicky
|
||||
*/
|
||||
public abstract class AbstractJobRepositoryFactoryBean implements FactoryBean, InitializingBean {
|
||||
|
||||
private PlatformTransactionManager transactionManager;
|
||||
|
||||
private ProxyFactory proxyFactory;
|
||||
|
||||
private String isolationLevelForCreate = DEFAULT_ISOLATION_LEVEL;
|
||||
|
||||
/**
|
||||
* Default value for isolation level in create* method.
|
||||
*/
|
||||
private static final String DEFAULT_ISOLATION_LEVEL = "ISOLATION_SERIALIZABLE";
|
||||
|
||||
/**
|
||||
* @return fully configured {@link JobInstanceDao} implementation.
|
||||
*/
|
||||
protected abstract JobInstanceDao createJobInstanceDao() throws Exception;
|
||||
|
||||
/**
|
||||
* @return fully configured {@link JobExecutionDao} implementation.
|
||||
*/
|
||||
protected abstract JobExecutionDao createJobExecutionDao() throws Exception;
|
||||
|
||||
/**
|
||||
* @return fully configured {@link StepExecutionDao} implementation.
|
||||
*/
|
||||
protected abstract StepExecutionDao createStepExecutionDao() throws Exception;
|
||||
|
||||
/**
|
||||
* @return fully configured {@link ExecutionContextDao} implementation.
|
||||
*/
|
||||
protected abstract ExecutionContextDao createExecutionContextDao() throws Exception;
|
||||
|
||||
/**
|
||||
* The type of object to be returned from {@link #getObject()}.
|
||||
*
|
||||
* @return JobRepository.class
|
||||
* @see org.springframework.beans.factory.FactoryBean#getObjectType()
|
||||
*/
|
||||
public Class<JobRepository> getObjectType() {
|
||||
return JobRepository.class;
|
||||
}
|
||||
|
||||
public boolean isSingleton() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* public setter for the isolation level to be used for the transaction when
|
||||
* job execution entities are initially created. The default is
|
||||
* ISOLATION_SERIALIZABLE, which prevents accidental concurrent execution of
|
||||
* the same job (ISOLATION_REPEATABLE_READ would work as well).
|
||||
*
|
||||
* @param isolationLevelForCreate the isolation level name to set
|
||||
*
|
||||
* @see SimpleJobRepository#createJobExecution(String,
|
||||
* org.springframework.batch.core.JobParameters)
|
||||
*/
|
||||
public void setIsolationLevelForCreate(String isolationLevelForCreate) {
|
||||
this.isolationLevelForCreate = isolationLevelForCreate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Public setter for the {@link PlatformTransactionManager}.
|
||||
* @param transactionManager the transactionManager to set
|
||||
*/
|
||||
public void setTransactionManager(PlatformTransactionManager transactionManager) {
|
||||
this.transactionManager = transactionManager;
|
||||
}
|
||||
|
||||
private void initializeProxy() throws Exception {
|
||||
proxyFactory = new ProxyFactory();
|
||||
TransactionInterceptor advice = new TransactionInterceptor(transactionManager, PropertiesConverter
|
||||
.stringToProperties("create*=PROPAGATION_REQUIRES_NEW," + isolationLevelForCreate
|
||||
+ "\ngetLastJobExecution*=PROPAGATION_REQUIRES_NEW," + isolationLevelForCreate
|
||||
+ "\n*=PROPAGATION_REQUIRED"));
|
||||
DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(advice);
|
||||
NameMatchMethodPointcut pointcut = new NameMatchMethodPointcut();
|
||||
pointcut.addMethodName("*");
|
||||
advisor.setPointcut(pointcut);
|
||||
proxyFactory.addAdvisor(advisor);
|
||||
proxyFactory.setProxyTargetClass(false);
|
||||
proxyFactory.addInterface(JobRepository.class);
|
||||
proxyFactory.setTarget(getTarget());
|
||||
}
|
||||
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
Assert.notNull(transactionManager, "TransactionManager must not be null.");
|
||||
|
||||
initializeProxy();
|
||||
}
|
||||
|
||||
private Object getTarget() throws Exception {
|
||||
return new SimpleJobRepository(createJobInstanceDao(), createJobExecutionDao(), createStepExecutionDao(),
|
||||
createExecutionContextDao());
|
||||
}
|
||||
|
||||
public Object getObject() throws Exception {
|
||||
if (proxyFactory == null) {
|
||||
afterPropertiesSet();
|
||||
}
|
||||
return proxyFactory.getProxy();
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* 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.springframework.batch.core.repository.support;
|
||||
|
||||
import org.springframework.aop.framework.ProxyFactory;
|
||||
import org.springframework.aop.support.DefaultPointcutAdvisor;
|
||||
import org.springframework.aop.support.NameMatchMethodPointcut;
|
||||
import org.springframework.batch.core.repository.JobRepository;
|
||||
import org.springframework.batch.core.repository.dao.ExecutionContextDao;
|
||||
import org.springframework.batch.core.repository.dao.JobExecutionDao;
|
||||
import org.springframework.batch.core.repository.dao.JobInstanceDao;
|
||||
import org.springframework.batch.core.repository.dao.StepExecutionDao;
|
||||
import org.springframework.batch.support.PropertiesConverter;
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.transaction.interceptor.TransactionInterceptor;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A {@link FactoryBean} that automates the creation of a
|
||||
* {@link SimpleJobRepository}. Declares abstract methods for providing DAO
|
||||
* object implementations.
|
||||
*
|
||||
* @see JobRepositoryFactoryBean
|
||||
* @see MapJobRepositoryFactoryBean
|
||||
*
|
||||
* @author Ben Hale
|
||||
* @author Lucas Ward
|
||||
* @author Robert Kasanicky
|
||||
*/
|
||||
public abstract class AbstractJobRepositoryFactoryBean implements FactoryBean, InitializingBean {
|
||||
|
||||
private PlatformTransactionManager transactionManager;
|
||||
|
||||
private ProxyFactory proxyFactory;
|
||||
|
||||
private String isolationLevelForCreate = DEFAULT_ISOLATION_LEVEL;
|
||||
|
||||
/**
|
||||
* Default value for isolation level in create* method.
|
||||
*/
|
||||
private static final String DEFAULT_ISOLATION_LEVEL = "ISOLATION_SERIALIZABLE";
|
||||
|
||||
/**
|
||||
* @return fully configured {@link JobInstanceDao} implementation.
|
||||
*/
|
||||
protected abstract JobInstanceDao createJobInstanceDao() throws Exception;
|
||||
|
||||
/**
|
||||
* @return fully configured {@link JobExecutionDao} implementation.
|
||||
*/
|
||||
protected abstract JobExecutionDao createJobExecutionDao() throws Exception;
|
||||
|
||||
/**
|
||||
* @return fully configured {@link StepExecutionDao} implementation.
|
||||
*/
|
||||
protected abstract StepExecutionDao createStepExecutionDao() throws Exception;
|
||||
|
||||
/**
|
||||
* @return fully configured {@link ExecutionContextDao} implementation.
|
||||
*/
|
||||
protected abstract ExecutionContextDao createExecutionContextDao() throws Exception;
|
||||
|
||||
/**
|
||||
* The type of object to be returned from {@link #getObject()}.
|
||||
*
|
||||
* @return JobRepository.class
|
||||
* @see org.springframework.beans.factory.FactoryBean#getObjectType()
|
||||
*/
|
||||
public Class<JobRepository> getObjectType() {
|
||||
return JobRepository.class;
|
||||
}
|
||||
|
||||
public boolean isSingleton() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* public setter for the isolation level to be used for the transaction when
|
||||
* job execution entities are initially created. The default is
|
||||
* ISOLATION_SERIALIZABLE, which prevents accidental concurrent execution of
|
||||
* the same job (ISOLATION_REPEATABLE_READ would work as well).
|
||||
*
|
||||
* @param isolationLevelForCreate the isolation level name to set
|
||||
*
|
||||
* @see SimpleJobRepository#createJobExecution(String,
|
||||
* org.springframework.batch.core.JobParameters)
|
||||
*/
|
||||
public void setIsolationLevelForCreate(String isolationLevelForCreate) {
|
||||
this.isolationLevelForCreate = isolationLevelForCreate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Public setter for the {@link PlatformTransactionManager}.
|
||||
* @param transactionManager the transactionManager to set
|
||||
*/
|
||||
public void setTransactionManager(PlatformTransactionManager transactionManager) {
|
||||
this.transactionManager = transactionManager;
|
||||
}
|
||||
|
||||
private void initializeProxy() throws Exception {
|
||||
proxyFactory = new ProxyFactory();
|
||||
TransactionInterceptor advice = new TransactionInterceptor(transactionManager, PropertiesConverter
|
||||
.stringToProperties("create*=PROPAGATION_REQUIRES_NEW," + isolationLevelForCreate
|
||||
+ "\ngetLastJobExecution*=PROPAGATION_REQUIRES_NEW," + isolationLevelForCreate
|
||||
+ "\n*=PROPAGATION_REQUIRED"));
|
||||
DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(advice);
|
||||
NameMatchMethodPointcut pointcut = new NameMatchMethodPointcut();
|
||||
pointcut.addMethodName("*");
|
||||
advisor.setPointcut(pointcut);
|
||||
proxyFactory.addAdvisor(advisor);
|
||||
proxyFactory.setProxyTargetClass(false);
|
||||
proxyFactory.addInterface(JobRepository.class);
|
||||
proxyFactory.setTarget(getTarget());
|
||||
}
|
||||
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
Assert.notNull(transactionManager, "TransactionManager must not be null.");
|
||||
|
||||
initializeProxy();
|
||||
}
|
||||
|
||||
private Object getTarget() throws Exception {
|
||||
return new SimpleJobRepository(createJobInstanceDao(), createJobExecutionDao(), createStepExecutionDao(),
|
||||
createExecutionContextDao());
|
||||
}
|
||||
|
||||
public Object getObject() throws Exception {
|
||||
if (proxyFactory == null) {
|
||||
afterPropertiesSet();
|
||||
}
|
||||
return proxyFactory.getProxy();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,57 +1,73 @@
|
||||
package org.springframework.batch.core.repository.support;
|
||||
|
||||
import org.springframework.batch.core.repository.dao.ExecutionContextDao;
|
||||
import org.springframework.batch.core.repository.dao.JobExecutionDao;
|
||||
import org.springframework.batch.core.repository.dao.JobInstanceDao;
|
||||
import org.springframework.batch.core.repository.dao.MapExecutionContextDao;
|
||||
import org.springframework.batch.core.repository.dao.MapJobExecutionDao;
|
||||
import org.springframework.batch.core.repository.dao.MapJobInstanceDao;
|
||||
import org.springframework.batch.core.repository.dao.MapStepExecutionDao;
|
||||
import org.springframework.batch.core.repository.dao.StepExecutionDao;
|
||||
import org.springframework.batch.support.transaction.ResourcelessTransactionManager;
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
|
||||
/**
|
||||
* A {@link FactoryBean} that automates the creation of a
|
||||
* {@link SimpleJobRepository} using non-persistent in-memory DAO
|
||||
* implementations. This repository is only really intended for use in testing
|
||||
* and rapid prototyping. In such settings you might find that
|
||||
* {@link ResourcelessTransactionManager} is useful (as long as your business
|
||||
* logic does not use a relational database).
|
||||
*
|
||||
* @author Robert Kasanicky
|
||||
*/
|
||||
public class MapJobRepositoryFactoryBean extends AbstractJobRepositoryFactoryBean {
|
||||
|
||||
/**
|
||||
* Convenience method to clear all the map daos globally, removing all
|
||||
* entities.
|
||||
*/
|
||||
public static void clear() {
|
||||
MapJobInstanceDao.clear();
|
||||
MapJobExecutionDao.clear();
|
||||
MapStepExecutionDao.clear();
|
||||
MapExecutionContextDao.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JobExecutionDao createJobExecutionDao() throws Exception {
|
||||
return new MapJobExecutionDao();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JobInstanceDao createJobInstanceDao() throws Exception {
|
||||
return new MapJobInstanceDao();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected StepExecutionDao createStepExecutionDao() throws Exception {
|
||||
return new MapStepExecutionDao();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ExecutionContextDao createExecutionContextDao() throws Exception {
|
||||
return new MapExecutionContextDao();
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* 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.springframework.batch.core.repository.support;
|
||||
|
||||
import org.springframework.batch.core.repository.dao.ExecutionContextDao;
|
||||
import org.springframework.batch.core.repository.dao.JobExecutionDao;
|
||||
import org.springframework.batch.core.repository.dao.JobInstanceDao;
|
||||
import org.springframework.batch.core.repository.dao.MapExecutionContextDao;
|
||||
import org.springframework.batch.core.repository.dao.MapJobExecutionDao;
|
||||
import org.springframework.batch.core.repository.dao.MapJobInstanceDao;
|
||||
import org.springframework.batch.core.repository.dao.MapStepExecutionDao;
|
||||
import org.springframework.batch.core.repository.dao.StepExecutionDao;
|
||||
import org.springframework.batch.support.transaction.ResourcelessTransactionManager;
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
|
||||
/**
|
||||
* A {@link FactoryBean} that automates the creation of a
|
||||
* {@link SimpleJobRepository} using non-persistent in-memory DAO
|
||||
* implementations. This repository is only really intended for use in testing
|
||||
* and rapid prototyping. In such settings you might find that
|
||||
* {@link ResourcelessTransactionManager} is useful (as long as your business
|
||||
* logic does not use a relational database).
|
||||
*
|
||||
* @author Robert Kasanicky
|
||||
*/
|
||||
public class MapJobRepositoryFactoryBean extends AbstractJobRepositoryFactoryBean {
|
||||
|
||||
/**
|
||||
* Convenience method to clear all the map daos globally, removing all
|
||||
* entities.
|
||||
*/
|
||||
public static void clear() {
|
||||
MapJobInstanceDao.clear();
|
||||
MapJobExecutionDao.clear();
|
||||
MapStepExecutionDao.clear();
|
||||
MapExecutionContextDao.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JobExecutionDao createJobExecutionDao() throws Exception {
|
||||
return new MapJobExecutionDao();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JobInstanceDao createJobInstanceDao() throws Exception {
|
||||
return new MapJobInstanceDao();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected StepExecutionDao createStepExecutionDao() throws Exception {
|
||||
return new MapStepExecutionDao();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ExecutionContextDao createExecutionContextDao() throws Exception {
|
||||
return new MapExecutionContextDao();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
* 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.springframework.batch.core.scope.context;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
@@ -1,31 +1,47 @@
|
||||
package org.springframework.batch.core.scope.context;
|
||||
|
||||
import org.aspectj.lang.annotation.Around;
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
import org.springframework.batch.core.JobInterruptedException;
|
||||
import org.springframework.batch.core.Step;
|
||||
import org.springframework.batch.core.StepExecution;
|
||||
|
||||
/**
|
||||
* Convenient aspect to wrap a single threaded step execution, where the
|
||||
* implementation of the {@link Step} is not step scope aware (i.e. not the ones
|
||||
* provided by the framework).
|
||||
*
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
@Aspect
|
||||
public class StepScopeManager {
|
||||
|
||||
@Around("execution(void org.springframework.batch.core.Step+.execute(*)) && target(step) && args(stepExecution)")
|
||||
public void execute(Step step, StepExecution stepExecution) throws JobInterruptedException {
|
||||
StepSynchronizationManager.register(stepExecution);
|
||||
try {
|
||||
step.execute(stepExecution);
|
||||
}
|
||||
finally {
|
||||
StepSynchronizationManager.release();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* 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.springframework.batch.core.scope.context;
|
||||
|
||||
import org.aspectj.lang.annotation.Around;
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
import org.springframework.batch.core.JobInterruptedException;
|
||||
import org.springframework.batch.core.Step;
|
||||
import org.springframework.batch.core.StepExecution;
|
||||
|
||||
/**
|
||||
* Convenient aspect to wrap a single threaded step execution, where the
|
||||
* implementation of the {@link Step} is not step scope aware (i.e. not the ones
|
||||
* provided by the framework).
|
||||
*
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
@Aspect
|
||||
public class StepScopeManager {
|
||||
|
||||
@Around("execution(void org.springframework.batch.core.Step+.execute(*)) && target(step) && args(stepExecution)")
|
||||
public void execute(Step step, StepExecution stepExecution) throws JobInterruptedException {
|
||||
StepSynchronizationManager.register(stepExecution);
|
||||
try {
|
||||
step.execute(stepExecution);
|
||||
}
|
||||
finally {
|
||||
StepSynchronizationManager.release();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,22 +1,38 @@
|
||||
package org.springframework.batch.core.scope.util;
|
||||
|
||||
/**
|
||||
* Interface to allow the context root for placeholder resolution to be switched
|
||||
* at runtime. Useful for testing.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public interface ContextFactory {
|
||||
|
||||
/**
|
||||
* @return a root object to which placeholders resolve relatively
|
||||
*/
|
||||
Object getContext();
|
||||
|
||||
/**
|
||||
* @return a unique identifier for this context
|
||||
*/
|
||||
String getContextId();
|
||||
|
||||
}
|
||||
/*
|
||||
* 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.springframework.batch.core.scope.util;
|
||||
|
||||
/**
|
||||
* Interface to allow the context root for placeholder resolution to be switched
|
||||
* at runtime. Useful for testing.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public interface ContextFactory {
|
||||
|
||||
/**
|
||||
* @return a root object to which placeholders resolve relatively
|
||||
*/
|
||||
Object getContext();
|
||||
|
||||
/**
|
||||
* @return a unique identifier for this context
|
||||
*/
|
||||
String getContextId();
|
||||
|
||||
}
|
||||
|
||||
@@ -1,166 +1,182 @@
|
||||
package org.springframework.batch.core.scope.util;
|
||||
|
||||
import java.lang.reflect.Modifier;
|
||||
|
||||
import org.springframework.aop.framework.AopInfrastructureBean;
|
||||
import org.springframework.aop.framework.ProxyConfig;
|
||||
import org.springframework.aop.framework.ProxyFactory;
|
||||
import org.springframework.aop.framework.autoproxy.AutoProxyUtils;
|
||||
import org.springframework.aop.scope.DefaultScopedObject;
|
||||
import org.springframework.aop.scope.ScopedObject;
|
||||
import org.springframework.aop.scope.ScopedProxyFactoryBean;
|
||||
import org.springframework.aop.support.DelegatingIntroductionInterceptor;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.FactoryBeanNotInitializedException;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.BeanDefinitionHolder;
|
||||
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* Factory bean for proxies that can replace placeholders in their target. Just
|
||||
* a specialisation of {@link ScopedProxyFactoryBean}, with a different target
|
||||
* source type.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public class PlaceholderProxyFactoryBean extends ProxyConfig implements FactoryBean, BeanFactoryAware {
|
||||
|
||||
/** The TargetSource that manages scoping */
|
||||
private final PlaceholderTargetSource scopedTargetSource = new PlaceholderTargetSource();
|
||||
|
||||
/** The name of the target bean */
|
||||
private String targetBeanName;
|
||||
|
||||
/** The cached singleton proxy */
|
||||
private Object proxy;
|
||||
|
||||
private final ContextFactory contextFactory;
|
||||
|
||||
/**
|
||||
* Create a new FactoryBean instance.
|
||||
*/
|
||||
public PlaceholderProxyFactoryBean(ContextFactory contextFactory) {
|
||||
this.contextFactory = contextFactory;
|
||||
// setProxyTargetClass(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the name of the bean that is to be scoped.
|
||||
*/
|
||||
public void setTargetBeanName(String targetBeanName) {
|
||||
this.targetBeanName = targetBeanName;
|
||||
this.scopedTargetSource.setTargetBeanName(targetBeanName);
|
||||
}
|
||||
|
||||
public void setBeanFactory(BeanFactory beanFactory) {
|
||||
if (!(beanFactory instanceof ConfigurableBeanFactory)) {
|
||||
throw new IllegalStateException("Not running in a ConfigurableBeanFactory: " + beanFactory);
|
||||
}
|
||||
ConfigurableBeanFactory cbf = (ConfigurableBeanFactory) beanFactory;
|
||||
|
||||
this.scopedTargetSource.setBeanFactory(beanFactory);
|
||||
this.scopedTargetSource.setContextFactory(contextFactory);
|
||||
|
||||
ProxyFactory pf = new ProxyFactory();
|
||||
pf.copyFrom(this);
|
||||
pf.setTargetSource(this.scopedTargetSource);
|
||||
|
||||
Class<?> beanType = beanFactory.getType(this.targetBeanName);
|
||||
if (beanType == null) {
|
||||
throw new IllegalStateException("Cannot create scoped proxy for bean '" + this.targetBeanName
|
||||
+ "': Target type could not be determined at the time of proxy creation.");
|
||||
}
|
||||
if (!isProxyTargetClass() || beanType.isInterface() || Modifier.isPrivate(beanType.getModifiers())) {
|
||||
pf.setInterfaces(ClassUtils.getAllInterfacesForClass(beanType, cbf.getBeanClassLoader()));
|
||||
}
|
||||
|
||||
// Add an introduction that implements only the methods on ScopedObject.
|
||||
ScopedObject scopedObject = new DefaultScopedObject(cbf, this.scopedTargetSource.getTargetBeanName());
|
||||
pf.addAdvice(new DelegatingIntroductionInterceptor(scopedObject));
|
||||
|
||||
// Add the AopInfrastructureBean marker to indicate that the scoped
|
||||
// proxy itself is not subject to auto-proxying! Only its target bean
|
||||
// is.
|
||||
pf.addInterface(AopInfrastructureBean.class);
|
||||
|
||||
this.proxy = pf.getProxy(cbf.getBeanClassLoader());
|
||||
}
|
||||
|
||||
public Object getObject() {
|
||||
if (this.proxy == null) {
|
||||
throw new FactoryBeanNotInitializedException();
|
||||
}
|
||||
return this.proxy;
|
||||
}
|
||||
|
||||
public Class<?> getObjectType() {
|
||||
if (this.proxy != null) {
|
||||
return this.proxy.getClass();
|
||||
}
|
||||
if (this.scopedTargetSource != null) {
|
||||
return this.scopedTargetSource.getTargetClass();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean isSingleton() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method to create a {@link BeanDefinition} for a target
|
||||
* wrapped in a placeholder tarrget source, able to defer binding of
|
||||
* placeholders until the bean is used.
|
||||
*
|
||||
* @param definition a target bean definition
|
||||
* @param registry a {@link BeanDefinitionRegistry}
|
||||
* @param proxyTargetClass true if we need to use CGlib to create the
|
||||
* proxies
|
||||
* @return a {@link BeanDefinitionHolder} for a
|
||||
* {@link PlaceholderProxyFactoryBean}
|
||||
*/
|
||||
public static BeanDefinitionHolder createScopedProxy(BeanDefinitionHolder definition,
|
||||
BeanDefinitionRegistry registry, boolean proxyTargetClass) {
|
||||
|
||||
String originalBeanName = definition.getBeanName();
|
||||
BeanDefinition targetDefinition = definition.getBeanDefinition();
|
||||
|
||||
// Create a proxy definition for the original bean name,
|
||||
// "hiding" the target bean in an internal target definition.
|
||||
RootBeanDefinition proxyDefinition = new RootBeanDefinition(PlaceholderProxyFactoryBean.class);
|
||||
proxyDefinition.getConstructorArgumentValues().addGenericArgumentValue(new StepContextFactory());
|
||||
proxyDefinition.setOriginatingBeanDefinition(definition.getBeanDefinition());
|
||||
proxyDefinition.setSource(definition.getSource());
|
||||
proxyDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
|
||||
String targetBeanName = "lazyBindingProxy." + originalBeanName;
|
||||
proxyDefinition.getPropertyValues().addPropertyValue("targetBeanName", targetBeanName);
|
||||
|
||||
if (proxyTargetClass) {
|
||||
targetDefinition.setAttribute(AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE, Boolean.TRUE);
|
||||
// ProxyFactoryBean's "proxyTargetClass" default is TRUE, so we
|
||||
// don't need to set it explicitly here.
|
||||
}
|
||||
else {
|
||||
proxyDefinition.getPropertyValues().addPropertyValue("proxyTargetClass", Boolean.FALSE);
|
||||
}
|
||||
|
||||
proxyDefinition.setAutowireCandidate(targetDefinition.isAutowireCandidate());
|
||||
// The target bean should be ignored in favor of the proxy.
|
||||
targetDefinition.setAutowireCandidate(false);
|
||||
|
||||
// Register the target bean as separate bean in the factory.
|
||||
registry.registerBeanDefinition(targetBeanName, targetDefinition);
|
||||
|
||||
// Return the scoped proxy definition as primary bean definition
|
||||
// (potentially an inner bean).
|
||||
return new BeanDefinitionHolder(proxyDefinition, originalBeanName, definition.getAliases());
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* 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.springframework.batch.core.scope.util;
|
||||
|
||||
import java.lang.reflect.Modifier;
|
||||
|
||||
import org.springframework.aop.framework.AopInfrastructureBean;
|
||||
import org.springframework.aop.framework.ProxyConfig;
|
||||
import org.springframework.aop.framework.ProxyFactory;
|
||||
import org.springframework.aop.framework.autoproxy.AutoProxyUtils;
|
||||
import org.springframework.aop.scope.DefaultScopedObject;
|
||||
import org.springframework.aop.scope.ScopedObject;
|
||||
import org.springframework.aop.scope.ScopedProxyFactoryBean;
|
||||
import org.springframework.aop.support.DelegatingIntroductionInterceptor;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.FactoryBeanNotInitializedException;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.BeanDefinitionHolder;
|
||||
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* Factory bean for proxies that can replace placeholders in their target. Just
|
||||
* a specialisation of {@link ScopedProxyFactoryBean}, with a different target
|
||||
* source type.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public class PlaceholderProxyFactoryBean extends ProxyConfig implements FactoryBean, BeanFactoryAware {
|
||||
|
||||
/** The TargetSource that manages scoping */
|
||||
private final PlaceholderTargetSource scopedTargetSource = new PlaceholderTargetSource();
|
||||
|
||||
/** The name of the target bean */
|
||||
private String targetBeanName;
|
||||
|
||||
/** The cached singleton proxy */
|
||||
private Object proxy;
|
||||
|
||||
private final ContextFactory contextFactory;
|
||||
|
||||
/**
|
||||
* Create a new FactoryBean instance.
|
||||
*/
|
||||
public PlaceholderProxyFactoryBean(ContextFactory contextFactory) {
|
||||
this.contextFactory = contextFactory;
|
||||
// setProxyTargetClass(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the name of the bean that is to be scoped.
|
||||
*/
|
||||
public void setTargetBeanName(String targetBeanName) {
|
||||
this.targetBeanName = targetBeanName;
|
||||
this.scopedTargetSource.setTargetBeanName(targetBeanName);
|
||||
}
|
||||
|
||||
public void setBeanFactory(BeanFactory beanFactory) {
|
||||
if (!(beanFactory instanceof ConfigurableBeanFactory)) {
|
||||
throw new IllegalStateException("Not running in a ConfigurableBeanFactory: " + beanFactory);
|
||||
}
|
||||
ConfigurableBeanFactory cbf = (ConfigurableBeanFactory) beanFactory;
|
||||
|
||||
this.scopedTargetSource.setBeanFactory(beanFactory);
|
||||
this.scopedTargetSource.setContextFactory(contextFactory);
|
||||
|
||||
ProxyFactory pf = new ProxyFactory();
|
||||
pf.copyFrom(this);
|
||||
pf.setTargetSource(this.scopedTargetSource);
|
||||
|
||||
Class<?> beanType = beanFactory.getType(this.targetBeanName);
|
||||
if (beanType == null) {
|
||||
throw new IllegalStateException("Cannot create scoped proxy for bean '" + this.targetBeanName
|
||||
+ "': Target type could not be determined at the time of proxy creation.");
|
||||
}
|
||||
if (!isProxyTargetClass() || beanType.isInterface() || Modifier.isPrivate(beanType.getModifiers())) {
|
||||
pf.setInterfaces(ClassUtils.getAllInterfacesForClass(beanType, cbf.getBeanClassLoader()));
|
||||
}
|
||||
|
||||
// Add an introduction that implements only the methods on ScopedObject.
|
||||
ScopedObject scopedObject = new DefaultScopedObject(cbf, this.scopedTargetSource.getTargetBeanName());
|
||||
pf.addAdvice(new DelegatingIntroductionInterceptor(scopedObject));
|
||||
|
||||
// Add the AopInfrastructureBean marker to indicate that the scoped
|
||||
// proxy itself is not subject to auto-proxying! Only its target bean
|
||||
// is.
|
||||
pf.addInterface(AopInfrastructureBean.class);
|
||||
|
||||
this.proxy = pf.getProxy(cbf.getBeanClassLoader());
|
||||
}
|
||||
|
||||
public Object getObject() {
|
||||
if (this.proxy == null) {
|
||||
throw new FactoryBeanNotInitializedException();
|
||||
}
|
||||
return this.proxy;
|
||||
}
|
||||
|
||||
public Class<?> getObjectType() {
|
||||
if (this.proxy != null) {
|
||||
return this.proxy.getClass();
|
||||
}
|
||||
if (this.scopedTargetSource != null) {
|
||||
return this.scopedTargetSource.getTargetClass();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean isSingleton() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method to create a {@link BeanDefinition} for a target
|
||||
* wrapped in a placeholder tarrget source, able to defer binding of
|
||||
* placeholders until the bean is used.
|
||||
*
|
||||
* @param definition a target bean definition
|
||||
* @param registry a {@link BeanDefinitionRegistry}
|
||||
* @param proxyTargetClass true if we need to use CGlib to create the
|
||||
* proxies
|
||||
* @return a {@link BeanDefinitionHolder} for a
|
||||
* {@link PlaceholderProxyFactoryBean}
|
||||
*/
|
||||
public static BeanDefinitionHolder createScopedProxy(BeanDefinitionHolder definition,
|
||||
BeanDefinitionRegistry registry, boolean proxyTargetClass) {
|
||||
|
||||
String originalBeanName = definition.getBeanName();
|
||||
BeanDefinition targetDefinition = definition.getBeanDefinition();
|
||||
|
||||
// Create a proxy definition for the original bean name,
|
||||
// "hiding" the target bean in an internal target definition.
|
||||
RootBeanDefinition proxyDefinition = new RootBeanDefinition(PlaceholderProxyFactoryBean.class);
|
||||
proxyDefinition.getConstructorArgumentValues().addGenericArgumentValue(new StepContextFactory());
|
||||
proxyDefinition.setOriginatingBeanDefinition(definition.getBeanDefinition());
|
||||
proxyDefinition.setSource(definition.getSource());
|
||||
proxyDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
|
||||
String targetBeanName = "lazyBindingProxy." + originalBeanName;
|
||||
proxyDefinition.getPropertyValues().addPropertyValue("targetBeanName", targetBeanName);
|
||||
|
||||
if (proxyTargetClass) {
|
||||
targetDefinition.setAttribute(AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE, Boolean.TRUE);
|
||||
// ProxyFactoryBean's "proxyTargetClass" default is TRUE, so we
|
||||
// don't need to set it explicitly here.
|
||||
}
|
||||
else {
|
||||
proxyDefinition.getPropertyValues().addPropertyValue("proxyTargetClass", Boolean.FALSE);
|
||||
}
|
||||
|
||||
proxyDefinition.setAutowireCandidate(targetDefinition.isAutowireCandidate());
|
||||
// The target bean should be ignored in favor of the proxy.
|
||||
targetDefinition.setAutowireCandidate(false);
|
||||
|
||||
// Register the target bean as separate bean in the factory.
|
||||
registry.registerBeanDefinition(targetBeanName, targetDefinition);
|
||||
|
||||
// Return the scoped proxy definition as primary bean definition
|
||||
// (potentially an inner bean).
|
||||
return new BeanDefinitionHolder(proxyDefinition, originalBeanName, definition.getAliases());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,23 +1,39 @@
|
||||
package org.springframework.batch.core.scope.util;
|
||||
|
||||
import org.springframework.batch.core.scope.context.StepContext;
|
||||
import org.springframework.batch.core.scope.context.StepSynchronizationManager;
|
||||
|
||||
/**
|
||||
* Implementation of {@link ContextFactory} that provides the current
|
||||
* {@link StepContext} as a contxt object.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public class StepContextFactory implements ContextFactory {
|
||||
|
||||
public Object getContext() {
|
||||
return StepSynchronizationManager.getContext();
|
||||
}
|
||||
|
||||
public String getContextId() {
|
||||
return (String) StepSynchronizationManager.getContext().getId();
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* 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.springframework.batch.core.scope.util;
|
||||
|
||||
import org.springframework.batch.core.scope.context.StepContext;
|
||||
import org.springframework.batch.core.scope.context.StepSynchronizationManager;
|
||||
|
||||
/**
|
||||
* Implementation of {@link ContextFactory} that provides the current
|
||||
* {@link StepContext} as a contxt object.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public class StepContextFactory implements ContextFactory {
|
||||
|
||||
public Object getContext() {
|
||||
return StepSynchronizationManager.getContext();
|
||||
}
|
||||
|
||||
public String getContextId() {
|
||||
return (String) StepSynchronizationManager.getContext().getId();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,254 +1,270 @@
|
||||
package org.springframework.batch.core.step.item;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.batch.retry.ExhaustedRetryException;
|
||||
import org.springframework.batch.retry.RecoveryCallback;
|
||||
import org.springframework.batch.retry.RetryCallback;
|
||||
import org.springframework.batch.retry.RetryContext;
|
||||
import org.springframework.batch.retry.RetryListener;
|
||||
import org.springframework.batch.retry.RetryOperations;
|
||||
import org.springframework.batch.retry.RetryPolicy;
|
||||
import org.springframework.batch.retry.RetryState;
|
||||
import org.springframework.batch.retry.backoff.BackOffPolicy;
|
||||
import org.springframework.batch.retry.context.RetryContextSupport;
|
||||
import org.springframework.batch.retry.policy.RetryContextCache;
|
||||
import org.springframework.batch.retry.support.DefaultRetryState;
|
||||
import org.springframework.batch.retry.support.RetrySynchronizationManager;
|
||||
import org.springframework.batch.retry.support.RetryTemplate;
|
||||
import org.springframework.batch.support.Classifier;
|
||||
|
||||
/**
|
||||
* A special purpose retry template that deals specifically with multi-valued
|
||||
* stateful retry. This is useful in the case where the operation to be retried
|
||||
* operates on multiple items, and when it fails there is no way to decide which
|
||||
* (if any) of the items was responsible. The {@link RetryState} used in the
|
||||
* execute methods is composite, and when a failure occurs, all of the keys in
|
||||
* the composite are "tarred with the same brush". Subsequent attempts to
|
||||
* execute with any of the keys that have failed previously results in a new
|
||||
* attempt and the previous state is used to check the {@link RetryPolicy}. If
|
||||
* one of the failed items eventually succeeds then the others in the current
|
||||
* composite for that attempt will be cleared from the context cache (as
|
||||
* normal), but there may still be entries in the cache for the original failed
|
||||
* items. This might mean that an item that did not cause a failure is never
|
||||
* retried because other items in the same batch fail fatally first.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public class BatchRetryTemplate implements RetryOperations {
|
||||
|
||||
private class BatchRetryState extends DefaultRetryState {
|
||||
|
||||
private final Collection<RetryState> keys;
|
||||
|
||||
public BatchRetryState(Collection<RetryState> keys) {
|
||||
super(keys);
|
||||
this.keys = new ArrayList<RetryState>(keys);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class BatchRetryContext extends RetryContextSupport {
|
||||
|
||||
private final Collection<RetryContext> contexts;
|
||||
|
||||
public BatchRetryContext(RetryContext parent, Collection<RetryContext> contexts) {
|
||||
|
||||
super(parent);
|
||||
|
||||
this.contexts = contexts;
|
||||
int count = 0;
|
||||
|
||||
for (RetryContext context : contexts) {
|
||||
int retryCount = context.getRetryCount();
|
||||
if (retryCount > count) {
|
||||
count = retryCount;
|
||||
registerThrowable(context.getLastThrowable());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class InnerRetryTemplate extends RetryTemplate {
|
||||
|
||||
@Override
|
||||
protected boolean canRetry(RetryPolicy retryPolicy, RetryContext context) {
|
||||
|
||||
BatchRetryContext batchContext = (BatchRetryContext) context;
|
||||
|
||||
for (RetryContext nextContext : batchContext.contexts) {
|
||||
if (!super.canRetry(retryPolicy, nextContext)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected RetryContext open(RetryPolicy retryPolicy, RetryState state) {
|
||||
|
||||
BatchRetryState batchState = (BatchRetryState) state;
|
||||
|
||||
Collection<RetryContext> contexts = new ArrayList<RetryContext>();
|
||||
for (RetryState retryState : batchState.keys) {
|
||||
contexts.add(super.open(retryPolicy, retryState));
|
||||
}
|
||||
|
||||
return new BatchRetryContext(RetrySynchronizationManager.getContext(), contexts);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void registerThrowable(RetryPolicy retryPolicy, RetryState state, RetryContext context, Exception e) {
|
||||
|
||||
BatchRetryState batchState = (BatchRetryState) state;
|
||||
BatchRetryContext batchContext = (BatchRetryContext) context;
|
||||
|
||||
Iterator<RetryContext> contextIterator = batchContext.contexts.iterator();
|
||||
for (RetryState retryState : batchState.keys) {
|
||||
RetryContext nextContext = contextIterator.next();
|
||||
super.registerThrowable(retryPolicy, retryState, nextContext, e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void close(RetryPolicy retryPolicy, RetryContext context, RetryState state, boolean succeeded) {
|
||||
|
||||
BatchRetryState batchState = (BatchRetryState) state;
|
||||
BatchRetryContext batchContext = (BatchRetryContext) context;
|
||||
|
||||
Iterator<RetryContext> contextIterator = batchContext.contexts.iterator();
|
||||
for (RetryState retryState : batchState.keys) {
|
||||
RetryContext nextContext = contextIterator.next();
|
||||
super.close(retryPolicy, nextContext, retryState, succeeded);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected <T> T handleRetryExhausted(RecoveryCallback<T> recoveryCallback, RetryContext context,
|
||||
RetryState state) throws Exception {
|
||||
|
||||
BatchRetryState batchState = (BatchRetryState) state;
|
||||
BatchRetryContext batchContext = (BatchRetryContext) context;
|
||||
|
||||
// Accumulate exceptions to be thrown so all the keys get a crack
|
||||
Exception rethrowable = null;
|
||||
ExhaustedRetryException exhausted = null;
|
||||
|
||||
Iterator<RetryContext> contextIterator = batchContext.contexts.iterator();
|
||||
for (RetryState retryState : batchState.keys) {
|
||||
|
||||
RetryContext nextContext = contextIterator.next();
|
||||
|
||||
try {
|
||||
super.handleRetryExhausted(null, nextContext, retryState);
|
||||
}
|
||||
catch (ExhaustedRetryException e) {
|
||||
exhausted = e;
|
||||
}
|
||||
catch (Exception e) {
|
||||
rethrowable = e;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (recoveryCallback != null) {
|
||||
return recoveryCallback.recover(context);
|
||||
}
|
||||
|
||||
if (exhausted != null) {
|
||||
throw exhausted;
|
||||
}
|
||||
|
||||
throw rethrowable;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private final InnerRetryTemplate delegate = new InnerRetryTemplate();
|
||||
|
||||
private final RetryTemplate regular = new RetryTemplate();
|
||||
|
||||
public <T> T execute(RetryCallback<T> retryCallback, Collection<RetryState> states) throws ExhaustedRetryException,
|
||||
Exception {
|
||||
RetryState batchState = new BatchRetryState(states);
|
||||
return delegate.execute(retryCallback, batchState);
|
||||
}
|
||||
|
||||
public <T> T execute(RetryCallback<T> retryCallback, RecoveryCallback<T> recoveryCallback,
|
||||
Collection<RetryState> states) throws ExhaustedRetryException, Exception {
|
||||
RetryState batchState = new BatchRetryState(states);
|
||||
return delegate.execute(retryCallback, recoveryCallback, batchState);
|
||||
}
|
||||
|
||||
public final <T> T execute(RetryCallback<T> retryCallback, RecoveryCallback<T> recoveryCallback,
|
||||
RetryState retryState) throws Exception, ExhaustedRetryException {
|
||||
return regular.execute(retryCallback, recoveryCallback, retryState);
|
||||
}
|
||||
|
||||
public final <T> T execute(RetryCallback<T> retryCallback, RecoveryCallback<T> recoveryCallback) throws Exception {
|
||||
return regular.execute(retryCallback, recoveryCallback);
|
||||
}
|
||||
|
||||
public final <T> T execute(RetryCallback<T> retryCallback, RetryState retryState) throws Exception,
|
||||
ExhaustedRetryException {
|
||||
return regular.execute(retryCallback, retryState);
|
||||
}
|
||||
|
||||
public final <T> T execute(RetryCallback<T> retryCallback) throws Exception {
|
||||
return regular.execute(retryCallback);
|
||||
}
|
||||
|
||||
public static List<RetryState> createState(List<?> keys) {
|
||||
List<RetryState> states = new ArrayList<RetryState>();
|
||||
for (Object key : keys) {
|
||||
states.add(new DefaultRetryState(key));
|
||||
}
|
||||
return states;
|
||||
}
|
||||
|
||||
public static List<RetryState> createState(List<?> keys, Classifier<? super Throwable, Boolean> classifier) {
|
||||
List<RetryState> states = new ArrayList<RetryState>();
|
||||
for (Object key : keys) {
|
||||
states.add(new DefaultRetryState(key, classifier));
|
||||
}
|
||||
return states;
|
||||
}
|
||||
|
||||
public void registerListener(RetryListener listener) {
|
||||
delegate.registerListener(listener);
|
||||
regular.registerListener(listener);
|
||||
}
|
||||
|
||||
public void setBackOffPolicy(BackOffPolicy backOffPolicy) {
|
||||
delegate.setBackOffPolicy(backOffPolicy);
|
||||
regular.setBackOffPolicy(backOffPolicy);
|
||||
}
|
||||
|
||||
public void setListeners(RetryListener[] listeners) {
|
||||
delegate.setListeners(listeners);
|
||||
regular.setListeners(listeners);
|
||||
}
|
||||
|
||||
public void setRetryContextCache(RetryContextCache retryContextCache) {
|
||||
delegate.setRetryContextCache(retryContextCache);
|
||||
regular.setRetryContextCache(retryContextCache);
|
||||
}
|
||||
|
||||
public void setRetryPolicy(RetryPolicy retryPolicy) {
|
||||
delegate.setRetryPolicy(retryPolicy);
|
||||
regular.setRetryPolicy(retryPolicy);
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* 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.springframework.batch.core.step.item;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.batch.retry.ExhaustedRetryException;
|
||||
import org.springframework.batch.retry.RecoveryCallback;
|
||||
import org.springframework.batch.retry.RetryCallback;
|
||||
import org.springframework.batch.retry.RetryContext;
|
||||
import org.springframework.batch.retry.RetryListener;
|
||||
import org.springframework.batch.retry.RetryOperations;
|
||||
import org.springframework.batch.retry.RetryPolicy;
|
||||
import org.springframework.batch.retry.RetryState;
|
||||
import org.springframework.batch.retry.backoff.BackOffPolicy;
|
||||
import org.springframework.batch.retry.context.RetryContextSupport;
|
||||
import org.springframework.batch.retry.policy.RetryContextCache;
|
||||
import org.springframework.batch.retry.support.DefaultRetryState;
|
||||
import org.springframework.batch.retry.support.RetrySynchronizationManager;
|
||||
import org.springframework.batch.retry.support.RetryTemplate;
|
||||
import org.springframework.batch.support.Classifier;
|
||||
|
||||
/**
|
||||
* A special purpose retry template that deals specifically with multi-valued
|
||||
* stateful retry. This is useful in the case where the operation to be retried
|
||||
* operates on multiple items, and when it fails there is no way to decide which
|
||||
* (if any) of the items was responsible. The {@link RetryState} used in the
|
||||
* execute methods is composite, and when a failure occurs, all of the keys in
|
||||
* the composite are "tarred with the same brush". Subsequent attempts to
|
||||
* execute with any of the keys that have failed previously results in a new
|
||||
* attempt and the previous state is used to check the {@link RetryPolicy}. If
|
||||
* one of the failed items eventually succeeds then the others in the current
|
||||
* composite for that attempt will be cleared from the context cache (as
|
||||
* normal), but there may still be entries in the cache for the original failed
|
||||
* items. This might mean that an item that did not cause a failure is never
|
||||
* retried because other items in the same batch fail fatally first.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public class BatchRetryTemplate implements RetryOperations {
|
||||
|
||||
private class BatchRetryState extends DefaultRetryState {
|
||||
|
||||
private final Collection<RetryState> keys;
|
||||
|
||||
public BatchRetryState(Collection<RetryState> keys) {
|
||||
super(keys);
|
||||
this.keys = new ArrayList<RetryState>(keys);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class BatchRetryContext extends RetryContextSupport {
|
||||
|
||||
private final Collection<RetryContext> contexts;
|
||||
|
||||
public BatchRetryContext(RetryContext parent, Collection<RetryContext> contexts) {
|
||||
|
||||
super(parent);
|
||||
|
||||
this.contexts = contexts;
|
||||
int count = 0;
|
||||
|
||||
for (RetryContext context : contexts) {
|
||||
int retryCount = context.getRetryCount();
|
||||
if (retryCount > count) {
|
||||
count = retryCount;
|
||||
registerThrowable(context.getLastThrowable());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class InnerRetryTemplate extends RetryTemplate {
|
||||
|
||||
@Override
|
||||
protected boolean canRetry(RetryPolicy retryPolicy, RetryContext context) {
|
||||
|
||||
BatchRetryContext batchContext = (BatchRetryContext) context;
|
||||
|
||||
for (RetryContext nextContext : batchContext.contexts) {
|
||||
if (!super.canRetry(retryPolicy, nextContext)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected RetryContext open(RetryPolicy retryPolicy, RetryState state) {
|
||||
|
||||
BatchRetryState batchState = (BatchRetryState) state;
|
||||
|
||||
Collection<RetryContext> contexts = new ArrayList<RetryContext>();
|
||||
for (RetryState retryState : batchState.keys) {
|
||||
contexts.add(super.open(retryPolicy, retryState));
|
||||
}
|
||||
|
||||
return new BatchRetryContext(RetrySynchronizationManager.getContext(), contexts);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void registerThrowable(RetryPolicy retryPolicy, RetryState state, RetryContext context, Exception e) {
|
||||
|
||||
BatchRetryState batchState = (BatchRetryState) state;
|
||||
BatchRetryContext batchContext = (BatchRetryContext) context;
|
||||
|
||||
Iterator<RetryContext> contextIterator = batchContext.contexts.iterator();
|
||||
for (RetryState retryState : batchState.keys) {
|
||||
RetryContext nextContext = contextIterator.next();
|
||||
super.registerThrowable(retryPolicy, retryState, nextContext, e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void close(RetryPolicy retryPolicy, RetryContext context, RetryState state, boolean succeeded) {
|
||||
|
||||
BatchRetryState batchState = (BatchRetryState) state;
|
||||
BatchRetryContext batchContext = (BatchRetryContext) context;
|
||||
|
||||
Iterator<RetryContext> contextIterator = batchContext.contexts.iterator();
|
||||
for (RetryState retryState : batchState.keys) {
|
||||
RetryContext nextContext = contextIterator.next();
|
||||
super.close(retryPolicy, nextContext, retryState, succeeded);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected <T> T handleRetryExhausted(RecoveryCallback<T> recoveryCallback, RetryContext context,
|
||||
RetryState state) throws Exception {
|
||||
|
||||
BatchRetryState batchState = (BatchRetryState) state;
|
||||
BatchRetryContext batchContext = (BatchRetryContext) context;
|
||||
|
||||
// Accumulate exceptions to be thrown so all the keys get a crack
|
||||
Exception rethrowable = null;
|
||||
ExhaustedRetryException exhausted = null;
|
||||
|
||||
Iterator<RetryContext> contextIterator = batchContext.contexts.iterator();
|
||||
for (RetryState retryState : batchState.keys) {
|
||||
|
||||
RetryContext nextContext = contextIterator.next();
|
||||
|
||||
try {
|
||||
super.handleRetryExhausted(null, nextContext, retryState);
|
||||
}
|
||||
catch (ExhaustedRetryException e) {
|
||||
exhausted = e;
|
||||
}
|
||||
catch (Exception e) {
|
||||
rethrowable = e;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (recoveryCallback != null) {
|
||||
return recoveryCallback.recover(context);
|
||||
}
|
||||
|
||||
if (exhausted != null) {
|
||||
throw exhausted;
|
||||
}
|
||||
|
||||
throw rethrowable;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private final InnerRetryTemplate delegate = new InnerRetryTemplate();
|
||||
|
||||
private final RetryTemplate regular = new RetryTemplate();
|
||||
|
||||
public <T> T execute(RetryCallback<T> retryCallback, Collection<RetryState> states) throws ExhaustedRetryException,
|
||||
Exception {
|
||||
RetryState batchState = new BatchRetryState(states);
|
||||
return delegate.execute(retryCallback, batchState);
|
||||
}
|
||||
|
||||
public <T> T execute(RetryCallback<T> retryCallback, RecoveryCallback<T> recoveryCallback,
|
||||
Collection<RetryState> states) throws ExhaustedRetryException, Exception {
|
||||
RetryState batchState = new BatchRetryState(states);
|
||||
return delegate.execute(retryCallback, recoveryCallback, batchState);
|
||||
}
|
||||
|
||||
public final <T> T execute(RetryCallback<T> retryCallback, RecoveryCallback<T> recoveryCallback,
|
||||
RetryState retryState) throws Exception, ExhaustedRetryException {
|
||||
return regular.execute(retryCallback, recoveryCallback, retryState);
|
||||
}
|
||||
|
||||
public final <T> T execute(RetryCallback<T> retryCallback, RecoveryCallback<T> recoveryCallback) throws Exception {
|
||||
return regular.execute(retryCallback, recoveryCallback);
|
||||
}
|
||||
|
||||
public final <T> T execute(RetryCallback<T> retryCallback, RetryState retryState) throws Exception,
|
||||
ExhaustedRetryException {
|
||||
return regular.execute(retryCallback, retryState);
|
||||
}
|
||||
|
||||
public final <T> T execute(RetryCallback<T> retryCallback) throws Exception {
|
||||
return regular.execute(retryCallback);
|
||||
}
|
||||
|
||||
public static List<RetryState> createState(List<?> keys) {
|
||||
List<RetryState> states = new ArrayList<RetryState>();
|
||||
for (Object key : keys) {
|
||||
states.add(new DefaultRetryState(key));
|
||||
}
|
||||
return states;
|
||||
}
|
||||
|
||||
public static List<RetryState> createState(List<?> keys, Classifier<? super Throwable, Boolean> classifier) {
|
||||
List<RetryState> states = new ArrayList<RetryState>();
|
||||
for (Object key : keys) {
|
||||
states.add(new DefaultRetryState(key, classifier));
|
||||
}
|
||||
return states;
|
||||
}
|
||||
|
||||
public void registerListener(RetryListener listener) {
|
||||
delegate.registerListener(listener);
|
||||
regular.registerListener(listener);
|
||||
}
|
||||
|
||||
public void setBackOffPolicy(BackOffPolicy backOffPolicy) {
|
||||
delegate.setBackOffPolicy(backOffPolicy);
|
||||
regular.setBackOffPolicy(backOffPolicy);
|
||||
}
|
||||
|
||||
public void setListeners(RetryListener[] listeners) {
|
||||
delegate.setListeners(listeners);
|
||||
regular.setListeners(listeners);
|
||||
}
|
||||
|
||||
public void setRetryContextCache(RetryContextCache retryContextCache) {
|
||||
delegate.setRetryContextCache(retryContextCache);
|
||||
regular.setRetryContextCache(retryContextCache);
|
||||
}
|
||||
|
||||
public void setRetryPolicy(RetryPolicy retryPolicy) {
|
||||
delegate.setRetryPolicy(retryPolicy);
|
||||
regular.setRetryPolicy(retryPolicy);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
* 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.springframework.batch.core.step.item;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
@@ -1,68 +1,84 @@
|
||||
package org.springframework.batch.core.step.item;
|
||||
|
||||
import org.springframework.batch.core.ExitStatus;
|
||||
import org.springframework.batch.core.StepContribution;
|
||||
import org.springframework.batch.core.scope.context.ChunkContext;
|
||||
import org.springframework.batch.core.step.tasklet.Tasklet;
|
||||
import org.springframework.batch.repeat.RepeatStatus;
|
||||
|
||||
/**
|
||||
* A {@link Tasklet} implementing variations on read-process-write item
|
||||
* handling.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*
|
||||
* @param <I> input item type
|
||||
*/
|
||||
public class ChunkOrientedTasklet<I> implements Tasklet {
|
||||
|
||||
private static final String INPUTS_KEY = "INPUTS";
|
||||
|
||||
private final ChunkProcessor<I> chunkProcessor;
|
||||
|
||||
private final ChunkProvider<I> chunkProvider;
|
||||
|
||||
private boolean buffering = true;
|
||||
|
||||
public ChunkOrientedTasklet(ChunkProvider<I> chunkProvider, ChunkProcessor<I> chunkProcessor) {
|
||||
this.chunkProvider = chunkProvider;
|
||||
this.chunkProcessor = chunkProcessor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flag to indicate that items should be buffered once read. Defaults to
|
||||
* true, which is appropriate for forward-only, non-transactional item
|
||||
* readers. Main (or only) use case for setting this flag to true is a
|
||||
* transactional JMS item reader.
|
||||
*
|
||||
* @param buffering
|
||||
*/
|
||||
public void setBuffering(boolean buffering) {
|
||||
this.buffering = buffering;
|
||||
}
|
||||
|
||||
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
Chunk<I> inputs = (Chunk<I>) chunkContext.getAttribute(INPUTS_KEY);
|
||||
if (inputs == null) {
|
||||
inputs = chunkProvider.provide(contribution);
|
||||
if (buffering) {
|
||||
chunkContext.setAttribute(INPUTS_KEY, inputs);
|
||||
}
|
||||
}
|
||||
|
||||
chunkProcessor.process(contribution, inputs);
|
||||
|
||||
chunkContext.removeAttribute(INPUTS_KEY);
|
||||
chunkContext.setComplete();
|
||||
chunkProvider.postProcess(contribution, inputs);
|
||||
if (!inputs.isEnd()) {
|
||||
contribution.setExitStatus(ExitStatus.FINISHED);
|
||||
}
|
||||
|
||||
return RepeatStatus.continueIf(!inputs.isEnd());
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* 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.springframework.batch.core.step.item;
|
||||
|
||||
import org.springframework.batch.core.ExitStatus;
|
||||
import org.springframework.batch.core.StepContribution;
|
||||
import org.springframework.batch.core.scope.context.ChunkContext;
|
||||
import org.springframework.batch.core.step.tasklet.Tasklet;
|
||||
import org.springframework.batch.repeat.RepeatStatus;
|
||||
|
||||
/**
|
||||
* A {@link Tasklet} implementing variations on read-process-write item
|
||||
* handling.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*
|
||||
* @param <I> input item type
|
||||
*/
|
||||
public class ChunkOrientedTasklet<I> implements Tasklet {
|
||||
|
||||
private static final String INPUTS_KEY = "INPUTS";
|
||||
|
||||
private final ChunkProcessor<I> chunkProcessor;
|
||||
|
||||
private final ChunkProvider<I> chunkProvider;
|
||||
|
||||
private boolean buffering = true;
|
||||
|
||||
public ChunkOrientedTasklet(ChunkProvider<I> chunkProvider, ChunkProcessor<I> chunkProcessor) {
|
||||
this.chunkProvider = chunkProvider;
|
||||
this.chunkProcessor = chunkProcessor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flag to indicate that items should be buffered once read. Defaults to
|
||||
* true, which is appropriate for forward-only, non-transactional item
|
||||
* readers. Main (or only) use case for setting this flag to true is a
|
||||
* transactional JMS item reader.
|
||||
*
|
||||
* @param buffering
|
||||
*/
|
||||
public void setBuffering(boolean buffering) {
|
||||
this.buffering = buffering;
|
||||
}
|
||||
|
||||
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
Chunk<I> inputs = (Chunk<I>) chunkContext.getAttribute(INPUTS_KEY);
|
||||
if (inputs == null) {
|
||||
inputs = chunkProvider.provide(contribution);
|
||||
if (buffering) {
|
||||
chunkContext.setAttribute(INPUTS_KEY, inputs);
|
||||
}
|
||||
}
|
||||
|
||||
chunkProcessor.process(contribution, inputs);
|
||||
|
||||
chunkContext.removeAttribute(INPUTS_KEY);
|
||||
chunkContext.setComplete();
|
||||
chunkProvider.postProcess(contribution, inputs);
|
||||
if (!inputs.isEnd()) {
|
||||
contribution.setExitStatus(ExitStatus.FINISHED);
|
||||
}
|
||||
|
||||
return RepeatStatus.continueIf(!inputs.isEnd());
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
* 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.springframework.batch.core.step.item;
|
||||
|
||||
import org.springframework.batch.core.StepContribution;
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
* 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.springframework.batch.core.step.item;
|
||||
|
||||
import org.springframework.batch.core.StepContribution;
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
* 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.springframework.batch.core.step.item;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
* 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.springframework.batch.core.step.item;
|
||||
|
||||
import org.springframework.batch.core.StepContribution;
|
||||
|
||||
@@ -1,299 +1,315 @@
|
||||
package org.springframework.batch.core.step.item;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.batch.core.ItemProcessListener;
|
||||
import org.springframework.batch.core.ItemReadListener;
|
||||
import org.springframework.batch.core.ItemWriteListener;
|
||||
import org.springframework.batch.core.SkipListener;
|
||||
import org.springframework.batch.core.step.skip.LimitCheckingItemSkipPolicy;
|
||||
import org.springframework.batch.core.step.skip.NonSkippableReadException;
|
||||
import org.springframework.batch.core.step.skip.SkipLimitExceededException;
|
||||
import org.springframework.batch.core.step.skip.SkipListenerFailedException;
|
||||
import org.springframework.batch.core.step.skip.SkipPolicy;
|
||||
import org.springframework.batch.core.step.tasklet.TaskletStep;
|
||||
import org.springframework.batch.repeat.RepeatOperations;
|
||||
import org.springframework.batch.repeat.support.RepeatTemplate;
|
||||
import org.springframework.batch.retry.RetryException;
|
||||
import org.springframework.batch.retry.RetryListener;
|
||||
import org.springframework.batch.retry.RetryPolicy;
|
||||
import org.springframework.batch.retry.backoff.BackOffPolicy;
|
||||
import org.springframework.batch.retry.policy.ExceptionClassifierRetryPolicy;
|
||||
import org.springframework.batch.retry.policy.MapRetryContextCache;
|
||||
import org.springframework.batch.retry.policy.RetryContextCache;
|
||||
import org.springframework.batch.retry.policy.SimpleRetryPolicy;
|
||||
import org.springframework.batch.support.Classifier;
|
||||
|
||||
/**
|
||||
* Factory bean for step that provides options for configuring skip behaviour.
|
||||
* User can set {@link #setSkipLimit(int)} to set how many exceptions of
|
||||
* {@link #setSkippableExceptionClasses(Collection)} types are tolerated.
|
||||
* {@link #setFatalExceptionClasses(Collection)} will cause immediate
|
||||
* termination of job - they are treated as higher priority than
|
||||
* {@link #setSkippableExceptionClasses(Collection)}, so the two lists don't
|
||||
* need to be exclusive.
|
||||
*
|
||||
* Skippable exceptions on write will by default cause transaction rollback - to
|
||||
* avoid rollback for specific exception class include it in the transaction
|
||||
* attribute as "no rollback for".
|
||||
*
|
||||
* @see SimpleStepFactoryBean
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @author Robert Kasanicky
|
||||
*
|
||||
*/
|
||||
public class FaultTolerantStepFactoryBean<T, S> extends SimpleStepFactoryBean<T, S> {
|
||||
|
||||
private int skipLimit = 0;
|
||||
|
||||
private Collection<Class<? extends Throwable>> skippableExceptionClasses = new HashSet<Class<? extends Throwable>>();
|
||||
|
||||
private Collection<Class<? extends Throwable>> fatalExceptionClasses = new HashSet<Class<? extends Throwable>>();
|
||||
|
||||
{
|
||||
fatalExceptionClasses.add(Error.class);
|
||||
skippableExceptionClasses.add(Exception.class);
|
||||
}
|
||||
|
||||
private int cacheCapacity = 0;
|
||||
|
||||
private int retryLimit = 0;
|
||||
|
||||
private Collection<Class<? extends Throwable>> retryableExceptionClasses = new HashSet<Class<? extends Throwable>>();
|
||||
|
||||
private BackOffPolicy backOffPolicy;
|
||||
|
||||
private RetryListener[] retryListeners;
|
||||
|
||||
private RetryPolicy retryPolicy;
|
||||
|
||||
private RetryContextCache retryContextCache;
|
||||
|
||||
private boolean isReaderTransactionalQueue = false;
|
||||
|
||||
public void setIsReaderTransactionalQueue(boolean isReaderTransactionalQueue) {
|
||||
this.isReaderTransactionalQueue = isReaderTransactionalQueue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter for the retry policy. If this is specified the other retry
|
||||
* properties are ignored (retryLimit, backOffPolicy,
|
||||
* retryableExceptionClasses).
|
||||
*
|
||||
* @param retryPolicy a stateless {@link RetryPolicy}
|
||||
*/
|
||||
public void setRetryPolicy(RetryPolicy retryPolicy) {
|
||||
this.retryPolicy = retryPolicy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Public setter for the retry limit. Each item can be retried up to this
|
||||
* limit.
|
||||
* @param retryLimit the retry limit to set
|
||||
*/
|
||||
public void setRetryLimit(int retryLimit) {
|
||||
this.retryLimit = retryLimit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Public setter for the capacity of the cache in the retry policy. If more
|
||||
* items than this fail without being skipped or recovered an exception will
|
||||
* be thrown. This is to guard against inadvertent infinite loops generated
|
||||
* by item identity problems.<br/>
|
||||
*
|
||||
* The default value should be high enough and more for most purposes. To
|
||||
* breach the limit in a single-threaded step typically you have to have
|
||||
* this many failures in a single transaction. Defaults to the value in the
|
||||
* {@link MapRetryContextCache}.<br/>
|
||||
*
|
||||
* This property is ignored if the
|
||||
* {@link #setRetryContextCache(RetryContextCache)} is set directly.
|
||||
*
|
||||
* @param cacheCapacity the cache capacity to set (greater than 0 else
|
||||
* ignored)
|
||||
*/
|
||||
public void setCacheCapacity(int cacheCapacity) {
|
||||
this.cacheCapacity = cacheCapacity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Override the default retry context cache for retry of chunk processing.
|
||||
* If this property is set then {@link #setCacheCapacity(int)} is ignored.
|
||||
*
|
||||
* @param retryContextCache the {@link RetryContextCache} to set
|
||||
*/
|
||||
public void setRetryContextCache(RetryContextCache retryContextCache) {
|
||||
this.retryContextCache = retryContextCache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Public setter for the Class[].
|
||||
* @param retryableExceptionClasses the retryableExceptionClasses to set
|
||||
*/
|
||||
public void setRetryableExceptionClasses(Collection<Class<? extends Throwable>> retryableExceptionClasses) {
|
||||
this.retryableExceptionClasses = retryableExceptionClasses;
|
||||
}
|
||||
|
||||
/**
|
||||
* Public setter for the {@link BackOffPolicy}.
|
||||
* @param backOffPolicy the {@link BackOffPolicy} to set
|
||||
*/
|
||||
public void setBackOffPolicy(BackOffPolicy backOffPolicy) {
|
||||
this.backOffPolicy = backOffPolicy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Public setter for the {@link RetryListener}s.
|
||||
* @param retryListeners the {@link RetryListener}s to set
|
||||
*/
|
||||
public void setRetryListeners(RetryListener[] retryListeners) {
|
||||
this.retryListeners = retryListeners;
|
||||
}
|
||||
|
||||
/**
|
||||
* Public setter for a limit that determines skip policy. If this value is
|
||||
* positive then an exception in chunk processing will cause the item to be
|
||||
* skipped and no exception propagated until the limit is reached. If it is
|
||||
* zero then all exceptions will be propagated from the chunk and cause the
|
||||
* step to abort.
|
||||
*
|
||||
* @param skipLimit the value to set. Default is 0 (never skip).
|
||||
*/
|
||||
public void setSkipLimit(int skipLimit) {
|
||||
this.skipLimit = skipLimit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Public setter for exception classes that when raised won't crash the job
|
||||
* but will result in transaction rollback and the item which handling
|
||||
* caused the exception will be skipped.
|
||||
*
|
||||
* @param exceptionClasses defaults to <code>Exception</code>
|
||||
*/
|
||||
public void setSkippableExceptionClasses(Collection<Class<? extends Throwable>> exceptionClasses) {
|
||||
this.skippableExceptionClasses = exceptionClasses;
|
||||
}
|
||||
|
||||
/**
|
||||
* Public setter for exception classes that should cause immediate failure.
|
||||
*
|
||||
* @param fatalExceptionClasses {@link Error} by default
|
||||
*/
|
||||
public void setFatalExceptionClasses(Collection<Class<? extends Throwable>> fatalExceptionClasses) {
|
||||
this.fatalExceptionClasses = fatalExceptionClasses;
|
||||
}
|
||||
|
||||
/**
|
||||
* Uses the {@link #setSkipLimit(int)} value to configure item handler and
|
||||
* and exception handler.
|
||||
*/
|
||||
protected void applyConfiguration(TaskletStep step) {
|
||||
super.applyConfiguration(step);
|
||||
|
||||
if (retryLimit > 0 || skipLimit > 0 || retryPolicy != null) {
|
||||
|
||||
addFatalExceptionIfMissing(SkipLimitExceededException.class);
|
||||
addFatalExceptionIfMissing(NonSkippableReadException.class);
|
||||
addFatalExceptionIfMissing(SkipListenerFailedException.class);
|
||||
addFatalExceptionIfMissing(RetryException.class);
|
||||
|
||||
if (retryPolicy == null) {
|
||||
|
||||
SimpleRetryPolicy simpleRetryPolicy = new SimpleRetryPolicy(retryLimit);
|
||||
if (!retryableExceptionClasses.isEmpty()) { // otherwise we
|
||||
// retry all exceptions
|
||||
simpleRetryPolicy.setRetryableExceptionClasses(retryableExceptionClasses);
|
||||
}
|
||||
simpleRetryPolicy.setFatalExceptionClasses(fatalExceptionClasses);
|
||||
|
||||
ExceptionClassifierRetryPolicy classifierRetryPolicy = new ExceptionClassifierRetryPolicy();
|
||||
HashMap<Class<? extends Throwable>, RetryPolicy> exceptionTypeMap = new HashMap<Class<? extends Throwable>, RetryPolicy>();
|
||||
for (Class<? extends Throwable> cls : retryableExceptionClasses) {
|
||||
exceptionTypeMap.put(cls, simpleRetryPolicy);
|
||||
}
|
||||
classifierRetryPolicy.setPolicyMap(exceptionTypeMap);
|
||||
retryPolicy = classifierRetryPolicy;
|
||||
|
||||
}
|
||||
BatchRetryTemplate batchRetryTemplate = new BatchRetryTemplate();
|
||||
if (backOffPolicy != null) {
|
||||
batchRetryTemplate.setBackOffPolicy(backOffPolicy);
|
||||
}
|
||||
batchRetryTemplate.setRetryPolicy(retryPolicy);
|
||||
|
||||
// Co-ordinate the retry policy with the exception handler:
|
||||
RepeatOperations stepOperations = getStepOperations();
|
||||
if (stepOperations instanceof RepeatTemplate) {
|
||||
SimpleRetryExceptionHandler exceptionHandler = new SimpleRetryExceptionHandler(retryPolicy,
|
||||
getExceptionHandler(), fatalExceptionClasses);
|
||||
((RepeatTemplate) stepOperations).setExceptionHandler(exceptionHandler);
|
||||
}
|
||||
|
||||
if (retryContextCache == null) {
|
||||
if (cacheCapacity > 0) {
|
||||
batchRetryTemplate.setRetryContextCache(new MapRetryContextCache(cacheCapacity));
|
||||
}
|
||||
}
|
||||
else {
|
||||
batchRetryTemplate.setRetryContextCache(retryContextCache);
|
||||
}
|
||||
|
||||
if (retryListeners != null) {
|
||||
batchRetryTemplate.setListeners(retryListeners);
|
||||
}
|
||||
|
||||
List<Class<? extends Throwable>> exceptions = new ArrayList<Class<? extends Throwable>>(
|
||||
skippableExceptionClasses);
|
||||
SkipPolicy readSkipPolicy = new LimitCheckingItemSkipPolicy(skipLimit, skippableExceptionClasses,
|
||||
new ArrayList<Class<? extends Throwable>>(fatalExceptionClasses));
|
||||
exceptions.addAll(new ArrayList<Class<? extends Throwable>>(retryableExceptionClasses));
|
||||
SkipPolicy writeSkipPolicy = new LimitCheckingItemSkipPolicy(skipLimit, exceptions,
|
||||
new ArrayList<Class<? extends Throwable>>(fatalExceptionClasses));
|
||||
|
||||
Classifier<Throwable, Boolean> rollbackClassifier = new Classifier<Throwable, Boolean>() {
|
||||
public Boolean classify(Throwable classifiable) {
|
||||
return getTransactionAttribute().rollbackOn(classifiable);
|
||||
}
|
||||
};
|
||||
|
||||
FaultTolerantChunkProvider<T> chunkProvider = new FaultTolerantChunkProvider<T>(getItemReader(),
|
||||
getChunkOperations());
|
||||
chunkProvider.setSkipPolicy(readSkipPolicy);
|
||||
chunkProvider.setListeners(BatchListenerFactoryHelper.<ItemReadListener<T>>getListeners(getListeners(), ItemReadListener.class));
|
||||
chunkProvider.setListeners(BatchListenerFactoryHelper.<SkipListener<T,S>>getListeners(getListeners(), SkipListener.class));
|
||||
|
||||
FaultTolerantChunkProcessor<T, S> chunkProcessor = new FaultTolerantChunkProcessor<T, S>(getItemProcessor(), getItemWriter(), batchRetryTemplate);
|
||||
chunkProcessor.setBuffering(!isReaderTransactionalQueue);
|
||||
chunkProcessor.setWriteSkipPolicy(writeSkipPolicy);
|
||||
chunkProcessor.setProcessSkipPolicy(writeSkipPolicy);
|
||||
chunkProcessor.setRollbackClassifier(rollbackClassifier);
|
||||
chunkProcessor.setListeners(BatchListenerFactoryHelper.<ItemProcessListener<T,S>>getListeners(getListeners(), ItemProcessListener.class));
|
||||
chunkProcessor.setListeners(BatchListenerFactoryHelper.<ItemWriteListener<S>>getListeners(getListeners(), ItemWriteListener.class));
|
||||
chunkProcessor.setListeners(BatchListenerFactoryHelper.<SkipListener<T,S>>getListeners(getListeners(), SkipListener.class));
|
||||
|
||||
ChunkOrientedTasklet<T> tasklet = new ChunkOrientedTasklet<T>(chunkProvider, chunkProcessor);
|
||||
tasklet.setBuffering(!isReaderTransactionalQueue);
|
||||
|
||||
step.setTasklet(tasklet);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void addFatalExceptionIfMissing(Class<? extends Throwable> cls) {
|
||||
List<Class<? extends Throwable>> fatalExceptionList = new ArrayList<Class<? extends Throwable>>();
|
||||
for (Class<? extends Throwable> exceptionClass : fatalExceptionClasses) {
|
||||
fatalExceptionList.add(exceptionClass);
|
||||
}
|
||||
if (!fatalExceptionList.contains(cls)) {
|
||||
fatalExceptionList.add(cls);
|
||||
}
|
||||
fatalExceptionClasses = fatalExceptionList;
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* 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.springframework.batch.core.step.item;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.batch.core.ItemProcessListener;
|
||||
import org.springframework.batch.core.ItemReadListener;
|
||||
import org.springframework.batch.core.ItemWriteListener;
|
||||
import org.springframework.batch.core.SkipListener;
|
||||
import org.springframework.batch.core.step.skip.LimitCheckingItemSkipPolicy;
|
||||
import org.springframework.batch.core.step.skip.NonSkippableReadException;
|
||||
import org.springframework.batch.core.step.skip.SkipLimitExceededException;
|
||||
import org.springframework.batch.core.step.skip.SkipListenerFailedException;
|
||||
import org.springframework.batch.core.step.skip.SkipPolicy;
|
||||
import org.springframework.batch.core.step.tasklet.TaskletStep;
|
||||
import org.springframework.batch.repeat.RepeatOperations;
|
||||
import org.springframework.batch.repeat.support.RepeatTemplate;
|
||||
import org.springframework.batch.retry.RetryException;
|
||||
import org.springframework.batch.retry.RetryListener;
|
||||
import org.springframework.batch.retry.RetryPolicy;
|
||||
import org.springframework.batch.retry.backoff.BackOffPolicy;
|
||||
import org.springframework.batch.retry.policy.ExceptionClassifierRetryPolicy;
|
||||
import org.springframework.batch.retry.policy.MapRetryContextCache;
|
||||
import org.springframework.batch.retry.policy.RetryContextCache;
|
||||
import org.springframework.batch.retry.policy.SimpleRetryPolicy;
|
||||
import org.springframework.batch.support.Classifier;
|
||||
|
||||
/**
|
||||
* Factory bean for step that provides options for configuring skip behaviour.
|
||||
* User can set {@link #setSkipLimit(int)} to set how many exceptions of
|
||||
* {@link #setSkippableExceptionClasses(Collection)} types are tolerated.
|
||||
* {@link #setFatalExceptionClasses(Collection)} will cause immediate
|
||||
* termination of job - they are treated as higher priority than
|
||||
* {@link #setSkippableExceptionClasses(Collection)}, so the two lists don't
|
||||
* need to be exclusive.
|
||||
*
|
||||
* Skippable exceptions on write will by default cause transaction rollback - to
|
||||
* avoid rollback for specific exception class include it in the transaction
|
||||
* attribute as "no rollback for".
|
||||
*
|
||||
* @see SimpleStepFactoryBean
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @author Robert Kasanicky
|
||||
*
|
||||
*/
|
||||
public class FaultTolerantStepFactoryBean<T, S> extends SimpleStepFactoryBean<T, S> {
|
||||
|
||||
private int skipLimit = 0;
|
||||
|
||||
private Collection<Class<? extends Throwable>> skippableExceptionClasses = new HashSet<Class<? extends Throwable>>();
|
||||
|
||||
private Collection<Class<? extends Throwable>> fatalExceptionClasses = new HashSet<Class<? extends Throwable>>();
|
||||
|
||||
{
|
||||
fatalExceptionClasses.add(Error.class);
|
||||
skippableExceptionClasses.add(Exception.class);
|
||||
}
|
||||
|
||||
private int cacheCapacity = 0;
|
||||
|
||||
private int retryLimit = 0;
|
||||
|
||||
private Collection<Class<? extends Throwable>> retryableExceptionClasses = new HashSet<Class<? extends Throwable>>();
|
||||
|
||||
private BackOffPolicy backOffPolicy;
|
||||
|
||||
private RetryListener[] retryListeners;
|
||||
|
||||
private RetryPolicy retryPolicy;
|
||||
|
||||
private RetryContextCache retryContextCache;
|
||||
|
||||
private boolean isReaderTransactionalQueue = false;
|
||||
|
||||
public void setIsReaderTransactionalQueue(boolean isReaderTransactionalQueue) {
|
||||
this.isReaderTransactionalQueue = isReaderTransactionalQueue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter for the retry policy. If this is specified the other retry
|
||||
* properties are ignored (retryLimit, backOffPolicy,
|
||||
* retryableExceptionClasses).
|
||||
*
|
||||
* @param retryPolicy a stateless {@link RetryPolicy}
|
||||
*/
|
||||
public void setRetryPolicy(RetryPolicy retryPolicy) {
|
||||
this.retryPolicy = retryPolicy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Public setter for the retry limit. Each item can be retried up to this
|
||||
* limit.
|
||||
* @param retryLimit the retry limit to set
|
||||
*/
|
||||
public void setRetryLimit(int retryLimit) {
|
||||
this.retryLimit = retryLimit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Public setter for the capacity of the cache in the retry policy. If more
|
||||
* items than this fail without being skipped or recovered an exception will
|
||||
* be thrown. This is to guard against inadvertent infinite loops generated
|
||||
* by item identity problems.<br/>
|
||||
*
|
||||
* The default value should be high enough and more for most purposes. To
|
||||
* breach the limit in a single-threaded step typically you have to have
|
||||
* this many failures in a single transaction. Defaults to the value in the
|
||||
* {@link MapRetryContextCache}.<br/>
|
||||
*
|
||||
* This property is ignored if the
|
||||
* {@link #setRetryContextCache(RetryContextCache)} is set directly.
|
||||
*
|
||||
* @param cacheCapacity the cache capacity to set (greater than 0 else
|
||||
* ignored)
|
||||
*/
|
||||
public void setCacheCapacity(int cacheCapacity) {
|
||||
this.cacheCapacity = cacheCapacity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Override the default retry context cache for retry of chunk processing.
|
||||
* If this property is set then {@link #setCacheCapacity(int)} is ignored.
|
||||
*
|
||||
* @param retryContextCache the {@link RetryContextCache} to set
|
||||
*/
|
||||
public void setRetryContextCache(RetryContextCache retryContextCache) {
|
||||
this.retryContextCache = retryContextCache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Public setter for the Class[].
|
||||
* @param retryableExceptionClasses the retryableExceptionClasses to set
|
||||
*/
|
||||
public void setRetryableExceptionClasses(Collection<Class<? extends Throwable>> retryableExceptionClasses) {
|
||||
this.retryableExceptionClasses = retryableExceptionClasses;
|
||||
}
|
||||
|
||||
/**
|
||||
* Public setter for the {@link BackOffPolicy}.
|
||||
* @param backOffPolicy the {@link BackOffPolicy} to set
|
||||
*/
|
||||
public void setBackOffPolicy(BackOffPolicy backOffPolicy) {
|
||||
this.backOffPolicy = backOffPolicy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Public setter for the {@link RetryListener}s.
|
||||
* @param retryListeners the {@link RetryListener}s to set
|
||||
*/
|
||||
public void setRetryListeners(RetryListener[] retryListeners) {
|
||||
this.retryListeners = retryListeners;
|
||||
}
|
||||
|
||||
/**
|
||||
* Public setter for a limit that determines skip policy. If this value is
|
||||
* positive then an exception in chunk processing will cause the item to be
|
||||
* skipped and no exception propagated until the limit is reached. If it is
|
||||
* zero then all exceptions will be propagated from the chunk and cause the
|
||||
* step to abort.
|
||||
*
|
||||
* @param skipLimit the value to set. Default is 0 (never skip).
|
||||
*/
|
||||
public void setSkipLimit(int skipLimit) {
|
||||
this.skipLimit = skipLimit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Public setter for exception classes that when raised won't crash the job
|
||||
* but will result in transaction rollback and the item which handling
|
||||
* caused the exception will be skipped.
|
||||
*
|
||||
* @param exceptionClasses defaults to <code>Exception</code>
|
||||
*/
|
||||
public void setSkippableExceptionClasses(Collection<Class<? extends Throwable>> exceptionClasses) {
|
||||
this.skippableExceptionClasses = exceptionClasses;
|
||||
}
|
||||
|
||||
/**
|
||||
* Public setter for exception classes that should cause immediate failure.
|
||||
*
|
||||
* @param fatalExceptionClasses {@link Error} by default
|
||||
*/
|
||||
public void setFatalExceptionClasses(Collection<Class<? extends Throwable>> fatalExceptionClasses) {
|
||||
this.fatalExceptionClasses = fatalExceptionClasses;
|
||||
}
|
||||
|
||||
/**
|
||||
* Uses the {@link #setSkipLimit(int)} value to configure item handler and
|
||||
* and exception handler.
|
||||
*/
|
||||
protected void applyConfiguration(TaskletStep step) {
|
||||
super.applyConfiguration(step);
|
||||
|
||||
if (retryLimit > 0 || skipLimit > 0 || retryPolicy != null) {
|
||||
|
||||
addFatalExceptionIfMissing(SkipLimitExceededException.class);
|
||||
addFatalExceptionIfMissing(NonSkippableReadException.class);
|
||||
addFatalExceptionIfMissing(SkipListenerFailedException.class);
|
||||
addFatalExceptionIfMissing(RetryException.class);
|
||||
|
||||
if (retryPolicy == null) {
|
||||
|
||||
SimpleRetryPolicy simpleRetryPolicy = new SimpleRetryPolicy(retryLimit);
|
||||
if (!retryableExceptionClasses.isEmpty()) { // otherwise we
|
||||
// retry all exceptions
|
||||
simpleRetryPolicy.setRetryableExceptionClasses(retryableExceptionClasses);
|
||||
}
|
||||
simpleRetryPolicy.setFatalExceptionClasses(fatalExceptionClasses);
|
||||
|
||||
ExceptionClassifierRetryPolicy classifierRetryPolicy = new ExceptionClassifierRetryPolicy();
|
||||
HashMap<Class<? extends Throwable>, RetryPolicy> exceptionTypeMap = new HashMap<Class<? extends Throwable>, RetryPolicy>();
|
||||
for (Class<? extends Throwable> cls : retryableExceptionClasses) {
|
||||
exceptionTypeMap.put(cls, simpleRetryPolicy);
|
||||
}
|
||||
classifierRetryPolicy.setPolicyMap(exceptionTypeMap);
|
||||
retryPolicy = classifierRetryPolicy;
|
||||
|
||||
}
|
||||
BatchRetryTemplate batchRetryTemplate = new BatchRetryTemplate();
|
||||
if (backOffPolicy != null) {
|
||||
batchRetryTemplate.setBackOffPolicy(backOffPolicy);
|
||||
}
|
||||
batchRetryTemplate.setRetryPolicy(retryPolicy);
|
||||
|
||||
// Co-ordinate the retry policy with the exception handler:
|
||||
RepeatOperations stepOperations = getStepOperations();
|
||||
if (stepOperations instanceof RepeatTemplate) {
|
||||
SimpleRetryExceptionHandler exceptionHandler = new SimpleRetryExceptionHandler(retryPolicy,
|
||||
getExceptionHandler(), fatalExceptionClasses);
|
||||
((RepeatTemplate) stepOperations).setExceptionHandler(exceptionHandler);
|
||||
}
|
||||
|
||||
if (retryContextCache == null) {
|
||||
if (cacheCapacity > 0) {
|
||||
batchRetryTemplate.setRetryContextCache(new MapRetryContextCache(cacheCapacity));
|
||||
}
|
||||
}
|
||||
else {
|
||||
batchRetryTemplate.setRetryContextCache(retryContextCache);
|
||||
}
|
||||
|
||||
if (retryListeners != null) {
|
||||
batchRetryTemplate.setListeners(retryListeners);
|
||||
}
|
||||
|
||||
List<Class<? extends Throwable>> exceptions = new ArrayList<Class<? extends Throwable>>(
|
||||
skippableExceptionClasses);
|
||||
SkipPolicy readSkipPolicy = new LimitCheckingItemSkipPolicy(skipLimit, skippableExceptionClasses,
|
||||
new ArrayList<Class<? extends Throwable>>(fatalExceptionClasses));
|
||||
exceptions.addAll(new ArrayList<Class<? extends Throwable>>(retryableExceptionClasses));
|
||||
SkipPolicy writeSkipPolicy = new LimitCheckingItemSkipPolicy(skipLimit, exceptions,
|
||||
new ArrayList<Class<? extends Throwable>>(fatalExceptionClasses));
|
||||
|
||||
Classifier<Throwable, Boolean> rollbackClassifier = new Classifier<Throwable, Boolean>() {
|
||||
public Boolean classify(Throwable classifiable) {
|
||||
return getTransactionAttribute().rollbackOn(classifiable);
|
||||
}
|
||||
};
|
||||
|
||||
FaultTolerantChunkProvider<T> chunkProvider = new FaultTolerantChunkProvider<T>(getItemReader(),
|
||||
getChunkOperations());
|
||||
chunkProvider.setSkipPolicy(readSkipPolicy);
|
||||
chunkProvider.setListeners(BatchListenerFactoryHelper.<ItemReadListener<T>>getListeners(getListeners(), ItemReadListener.class));
|
||||
chunkProvider.setListeners(BatchListenerFactoryHelper.<SkipListener<T,S>>getListeners(getListeners(), SkipListener.class));
|
||||
|
||||
FaultTolerantChunkProcessor<T, S> chunkProcessor = new FaultTolerantChunkProcessor<T, S>(getItemProcessor(), getItemWriter(), batchRetryTemplate);
|
||||
chunkProcessor.setBuffering(!isReaderTransactionalQueue);
|
||||
chunkProcessor.setWriteSkipPolicy(writeSkipPolicy);
|
||||
chunkProcessor.setProcessSkipPolicy(writeSkipPolicy);
|
||||
chunkProcessor.setRollbackClassifier(rollbackClassifier);
|
||||
chunkProcessor.setListeners(BatchListenerFactoryHelper.<ItemProcessListener<T,S>>getListeners(getListeners(), ItemProcessListener.class));
|
||||
chunkProcessor.setListeners(BatchListenerFactoryHelper.<ItemWriteListener<S>>getListeners(getListeners(), ItemWriteListener.class));
|
||||
chunkProcessor.setListeners(BatchListenerFactoryHelper.<SkipListener<T,S>>getListeners(getListeners(), SkipListener.class));
|
||||
|
||||
ChunkOrientedTasklet<T> tasklet = new ChunkOrientedTasklet<T>(chunkProvider, chunkProcessor);
|
||||
tasklet.setBuffering(!isReaderTransactionalQueue);
|
||||
|
||||
step.setTasklet(tasklet);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void addFatalExceptionIfMissing(Class<? extends Throwable> cls) {
|
||||
List<Class<? extends Throwable>> fatalExceptionList = new ArrayList<Class<? extends Throwable>>();
|
||||
for (Class<? extends Throwable> exceptionClass : fatalExceptionClasses) {
|
||||
fatalExceptionList.add(exceptionClass);
|
||||
}
|
||||
if (!fatalExceptionList.contains(cls)) {
|
||||
fatalExceptionList.add(cls);
|
||||
}
|
||||
fatalExceptionClasses = fatalExceptionList;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
* 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.springframework.batch.core.step.item;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -1,114 +1,130 @@
|
||||
package org.springframework.batch.core.step.item;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.batch.core.StepContribution;
|
||||
import org.springframework.batch.core.StepListener;
|
||||
import org.springframework.batch.core.listener.MulticasterBatchListener;
|
||||
import org.springframework.batch.core.step.skip.SkipListenerFailedException;
|
||||
import org.springframework.batch.item.ItemReader;
|
||||
import org.springframework.batch.repeat.RepeatCallback;
|
||||
import org.springframework.batch.repeat.RepeatContext;
|
||||
import org.springframework.batch.repeat.RepeatOperations;
|
||||
import org.springframework.batch.repeat.RepeatStatus;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Dave Syer
|
||||
*
|
||||
* @param <I> input item type
|
||||
*/
|
||||
public class SimpleChunkProvider<I> implements ChunkProvider<I> {
|
||||
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
protected final ItemReader<? extends I> itemReader;
|
||||
|
||||
private final MulticasterBatchListener<I, ?> listener = new MulticasterBatchListener<I, Object>();
|
||||
|
||||
private final RepeatOperations repeatOperations;
|
||||
|
||||
public SimpleChunkProvider(ItemReader<? extends I> itemReader, RepeatOperations repeatOperations) {
|
||||
this.itemReader = itemReader;
|
||||
this.repeatOperations = repeatOperations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register some {@link StepListener}s with the handler. Each will get the
|
||||
* callbacks in the order specified at the correct stage.
|
||||
*
|
||||
* @param listeners
|
||||
*/
|
||||
public void setListeners(List<? extends StepListener> listeners) {
|
||||
for (StepListener listener : listeners) {
|
||||
registerListener(listener);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a listener for callbacks at the appropriate stages in a process.
|
||||
*
|
||||
* @param listener a {@link StepListener}
|
||||
*/
|
||||
public void registerListener(StepListener listener) {
|
||||
this.listener.register(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Surrounds the read call with listener callbacks.
|
||||
* @return item
|
||||
* @throws Exception
|
||||
*/
|
||||
protected final I doRead() throws Exception {
|
||||
try {
|
||||
listener.beforeRead();
|
||||
I item = itemReader.read();
|
||||
listener.afterRead(item);
|
||||
return item;
|
||||
}
|
||||
catch (Exception e) {
|
||||
listener.onReadError(e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
public Chunk<I> provide(final StepContribution contribution) throws Exception {
|
||||
|
||||
final Chunk<I> inputs = new Chunk<I>();
|
||||
repeatOperations.iterate(new RepeatCallback() {
|
||||
|
||||
public RepeatStatus doInIteration(final RepeatContext context) throws Exception {
|
||||
I item = read(contribution, inputs);
|
||||
if (item == null) {
|
||||
inputs.setEnd();
|
||||
return RepeatStatus.FINISHED;
|
||||
}
|
||||
inputs.add(item);
|
||||
contribution.incrementReadCount();
|
||||
return RepeatStatus.CONTINUABLE;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
return inputs;
|
||||
|
||||
}
|
||||
|
||||
public void postProcess(StepContribution contribution, Chunk<I> chunk) {
|
||||
for (Exception e : chunk.getErrors()) {
|
||||
try {
|
||||
listener.onSkipInRead(e);
|
||||
}
|
||||
catch (RuntimeException ex) {
|
||||
throw new SkipListenerFailedException("Fatal exception in SkipListener.", ex, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected I read(StepContribution contribution, Chunk<I> chunk) throws Exception {
|
||||
return doRead();
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* 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.springframework.batch.core.step.item;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.batch.core.StepContribution;
|
||||
import org.springframework.batch.core.StepListener;
|
||||
import org.springframework.batch.core.listener.MulticasterBatchListener;
|
||||
import org.springframework.batch.core.step.skip.SkipListenerFailedException;
|
||||
import org.springframework.batch.item.ItemReader;
|
||||
import org.springframework.batch.repeat.RepeatCallback;
|
||||
import org.springframework.batch.repeat.RepeatContext;
|
||||
import org.springframework.batch.repeat.RepeatOperations;
|
||||
import org.springframework.batch.repeat.RepeatStatus;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Dave Syer
|
||||
*
|
||||
* @param <I> input item type
|
||||
*/
|
||||
public class SimpleChunkProvider<I> implements ChunkProvider<I> {
|
||||
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
protected final ItemReader<? extends I> itemReader;
|
||||
|
||||
private final MulticasterBatchListener<I, ?> listener = new MulticasterBatchListener<I, Object>();
|
||||
|
||||
private final RepeatOperations repeatOperations;
|
||||
|
||||
public SimpleChunkProvider(ItemReader<? extends I> itemReader, RepeatOperations repeatOperations) {
|
||||
this.itemReader = itemReader;
|
||||
this.repeatOperations = repeatOperations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register some {@link StepListener}s with the handler. Each will get the
|
||||
* callbacks in the order specified at the correct stage.
|
||||
*
|
||||
* @param listeners
|
||||
*/
|
||||
public void setListeners(List<? extends StepListener> listeners) {
|
||||
for (StepListener listener : listeners) {
|
||||
registerListener(listener);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a listener for callbacks at the appropriate stages in a process.
|
||||
*
|
||||
* @param listener a {@link StepListener}
|
||||
*/
|
||||
public void registerListener(StepListener listener) {
|
||||
this.listener.register(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Surrounds the read call with listener callbacks.
|
||||
* @return item
|
||||
* @throws Exception
|
||||
*/
|
||||
protected final I doRead() throws Exception {
|
||||
try {
|
||||
listener.beforeRead();
|
||||
I item = itemReader.read();
|
||||
listener.afterRead(item);
|
||||
return item;
|
||||
}
|
||||
catch (Exception e) {
|
||||
listener.onReadError(e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
public Chunk<I> provide(final StepContribution contribution) throws Exception {
|
||||
|
||||
final Chunk<I> inputs = new Chunk<I>();
|
||||
repeatOperations.iterate(new RepeatCallback() {
|
||||
|
||||
public RepeatStatus doInIteration(final RepeatContext context) throws Exception {
|
||||
I item = read(contribution, inputs);
|
||||
if (item == null) {
|
||||
inputs.setEnd();
|
||||
return RepeatStatus.FINISHED;
|
||||
}
|
||||
inputs.add(item);
|
||||
contribution.incrementReadCount();
|
||||
return RepeatStatus.CONTINUABLE;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
return inputs;
|
||||
|
||||
}
|
||||
|
||||
public void postProcess(StepContribution contribution, Chunk<I> chunk) {
|
||||
for (Exception e : chunk.getErrors()) {
|
||||
try {
|
||||
listener.onSkipInRead(e);
|
||||
}
|
||||
catch (RuntimeException ex) {
|
||||
throw new SkipListenerFailedException("Fatal exception in SkipListener.", ex, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected I read(StepContribution contribution, Chunk<I> chunk) throws Exception {
|
||||
return doRead();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
* 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.springframework.batch.core.step.item;
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,11 +1,27 @@
|
||||
package org.springframework.batch.core.step.skip;
|
||||
|
||||
import org.springframework.core.NestedRuntimeException;
|
||||
|
||||
public class NonSkippableReadException extends NestedRuntimeException {
|
||||
|
||||
public NonSkippableReadException(String msg, Throwable cause) {
|
||||
super(msg, cause);
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* 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.springframework.batch.core.step.skip;
|
||||
|
||||
import org.springframework.core.NestedRuntimeException;
|
||||
|
||||
public class NonSkippableReadException extends NestedRuntimeException {
|
||||
|
||||
public NonSkippableReadException(String msg, Throwable cause) {
|
||||
super(msg, cause);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,40 +1,56 @@
|
||||
package org.springframework.batch.core.step.tasklet;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.batch.core.ExitStatus;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Maps exit codes to {@link org.springframework.batch.core.ExitStatus}
|
||||
* according to injected map. The injected map is required to contain a value
|
||||
* for 'else' key, this value will be returned if the injected map
|
||||
* does not contain value for the exit code returned by the system process.
|
||||
*
|
||||
* @author Robert Kasanicky
|
||||
*/
|
||||
public class ConfigurableSystemProcessExitCodeMapper implements SystemProcessExitCodeMapper {
|
||||
|
||||
public static final String ELSE_KEY = "else";
|
||||
|
||||
private Map<Object, ExitStatus> mappings;
|
||||
|
||||
public ExitStatus getExitStatus(int exitCode) {
|
||||
ExitStatus exitStatus = mappings.get(exitCode);
|
||||
if (exitStatus != null) {
|
||||
return exitStatus;
|
||||
} else {
|
||||
return mappings.get(ELSE_KEY);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mappings <code>Integer</code> exit code keys to
|
||||
* {@link org.springframework.batch.core.ExitStatus} values.
|
||||
*/
|
||||
public void setMappings(Map<Object, ExitStatus> mappings) {
|
||||
Assert.notNull(mappings.get(ELSE_KEY));
|
||||
this.mappings = mappings;
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* 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.springframework.batch.core.step.tasklet;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.batch.core.ExitStatus;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Maps exit codes to {@link org.springframework.batch.core.ExitStatus}
|
||||
* according to injected map. The injected map is required to contain a value
|
||||
* for 'else' key, this value will be returned if the injected map
|
||||
* does not contain value for the exit code returned by the system process.
|
||||
*
|
||||
* @author Robert Kasanicky
|
||||
*/
|
||||
public class ConfigurableSystemProcessExitCodeMapper implements SystemProcessExitCodeMapper {
|
||||
|
||||
public static final String ELSE_KEY = "else";
|
||||
|
||||
private Map<Object, ExitStatus> mappings;
|
||||
|
||||
public ExitStatus getExitStatus(int exitCode) {
|
||||
ExitStatus exitStatus = mappings.get(exitCode);
|
||||
if (exitStatus != null) {
|
||||
return exitStatus;
|
||||
} else {
|
||||
return mappings.get(ELSE_KEY);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mappings <code>Integer</code> exit code keys to
|
||||
* {@link org.springframework.batch.core.ExitStatus} values.
|
||||
*/
|
||||
public void setMappings(Map<Object, ExitStatus> mappings) {
|
||||
Assert.notNull(mappings.get(ELSE_KEY));
|
||||
this.mappings = mappings;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,22 +1,38 @@
|
||||
package org.springframework.batch.core.step.tasklet;
|
||||
|
||||
import org.springframework.batch.core.ExitStatus;
|
||||
|
||||
/**
|
||||
* Simple {@link SystemProcessExitCodeMapper} implementation that performs following mapping:
|
||||
*
|
||||
* 0 -> ExitStatus.FINISHED
|
||||
* else -> ExitStatus.FAILED
|
||||
*
|
||||
* @author Robert Kasanicky
|
||||
*/
|
||||
public class SimpleSystemProcessExitCodeMapper implements SystemProcessExitCodeMapper {
|
||||
public ExitStatus getExitStatus(int exitCode) {
|
||||
if (exitCode == 0) {
|
||||
return ExitStatus.FINISHED;
|
||||
} else {
|
||||
return ExitStatus.FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* 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.springframework.batch.core.step.tasklet;
|
||||
|
||||
import org.springframework.batch.core.ExitStatus;
|
||||
|
||||
/**
|
||||
* Simple {@link SystemProcessExitCodeMapper} implementation that performs following mapping:
|
||||
*
|
||||
* 0 -> ExitStatus.FINISHED
|
||||
* else -> ExitStatus.FAILED
|
||||
*
|
||||
* @author Robert Kasanicky
|
||||
*/
|
||||
public class SimpleSystemProcessExitCodeMapper implements SystemProcessExitCodeMapper {
|
||||
public ExitStatus getExitStatus(int exitCode) {
|
||||
if (exitCode == 0) {
|
||||
return ExitStatus.FINISHED;
|
||||
} else {
|
||||
return ExitStatus.FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,18 +1,34 @@
|
||||
package org.springframework.batch.core.step.tasklet;
|
||||
|
||||
/**
|
||||
* Exception indicating failed execution of system command.
|
||||
*/
|
||||
public class SystemCommandException extends RuntimeException {
|
||||
|
||||
// generated
|
||||
private static final long serialVersionUID = 5139355923336176733L;
|
||||
|
||||
public SystemCommandException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public SystemCommandException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
/*
|
||||
* 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.springframework.batch.core.step.tasklet;
|
||||
|
||||
/**
|
||||
* Exception indicating failed execution of system command.
|
||||
*/
|
||||
public class SystemCommandException extends RuntimeException {
|
||||
|
||||
// generated
|
||||
private static final long serialVersionUID = 5139355923336176733L;
|
||||
|
||||
public SystemCommandException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public SystemCommandException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,199 +1,215 @@
|
||||
package org.springframework.batch.core.step.tasklet;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.FutureTask;
|
||||
|
||||
import org.apache.commons.lang.time.StopWatch;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.batch.core.ExitStatus;
|
||||
import org.springframework.batch.core.JobInterruptedException;
|
||||
import org.springframework.batch.core.StepContribution;
|
||||
import org.springframework.batch.core.StepExecution;
|
||||
import org.springframework.batch.core.listener.StepExecutionListenerSupport;
|
||||
import org.springframework.batch.core.scope.context.ChunkContext;
|
||||
import org.springframework.batch.repeat.RepeatStatus;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.core.task.SimpleAsyncTaskExecutor;
|
||||
import org.springframework.core.task.TaskExecutor;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* {@link Tasklet} that executes a system command.
|
||||
*
|
||||
* The system command is executed asynchronously using injected
|
||||
* {@link #setTaskExecutor(TaskExecutor)} - timeout value is required to be set,
|
||||
* so that the batch job does not hang forever if the external process hangs.
|
||||
*
|
||||
* Tasklet periodically checks for termination status (i.e.
|
||||
* {@link #setCommand(String)} finished its execution or
|
||||
* {@link #setTimeout(long)} expired or job was interrupted). The check interval
|
||||
* is given by {@link #setTerminationCheckInterval(long)}.
|
||||
*
|
||||
* When job interrupt is detected tasklet's execution is terminated immediately
|
||||
* by throwing {@link JobInterruptedException}.
|
||||
*
|
||||
* {@link #setInterruptOnCancel(boolean)} specifies whether the tasklet should
|
||||
* attempt to interrupt the thread that executes the system command if it is
|
||||
* still running when tasklet exits (abnormally).
|
||||
*
|
||||
* @author Robert Kasanicky
|
||||
*/
|
||||
public class SystemCommandTasklet extends StepExecutionListenerSupport implements Tasklet, InitializingBean {
|
||||
|
||||
protected static final Log logger = LogFactory.getLog(SystemCommandTasklet.class);
|
||||
|
||||
private String command;
|
||||
|
||||
private String[] environmentParams = null;
|
||||
|
||||
private File workingDirectory = null;
|
||||
|
||||
private SystemProcessExitCodeMapper systemProcessExitCodeMapper = new SimpleSystemProcessExitCodeMapper();
|
||||
|
||||
private long timeout = 0;
|
||||
|
||||
private long checkInterval = 1000;
|
||||
|
||||
private StepExecution execution = null;
|
||||
|
||||
private TaskExecutor taskExecutor = new SimpleAsyncTaskExecutor();
|
||||
|
||||
private boolean interruptOnCancel = false;
|
||||
|
||||
/**
|
||||
* Execute system command and map its exit code to {@link ExitStatus} using
|
||||
* {@link SystemProcessExitCodeMapper}.
|
||||
*/
|
||||
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
|
||||
|
||||
FutureTask<Integer> systemCommandTask = new FutureTask<Integer>(new Callable<Integer>() {
|
||||
|
||||
public Integer call() throws Exception {
|
||||
Process process = Runtime.getRuntime().exec(command, environmentParams, workingDirectory);
|
||||
return process.waitFor();
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
StopWatch stopWatch = new StopWatch();
|
||||
stopWatch.start();
|
||||
|
||||
taskExecutor.execute(systemCommandTask);
|
||||
|
||||
try {
|
||||
while (true) {
|
||||
Thread.sleep(checkInterval);
|
||||
if (systemCommandTask.isDone()) {
|
||||
contribution.setExitStatus(systemProcessExitCodeMapper.getExitStatus(systemCommandTask.get()));
|
||||
return RepeatStatus.FINISHED;
|
||||
}
|
||||
else if (stopWatch.getTime() > timeout) {
|
||||
systemCommandTask.cancel(interruptOnCancel);
|
||||
throw new SystemCommandException("Execution of system command did not finish within the timeout");
|
||||
}
|
||||
else if (execution.isTerminateOnly()) {
|
||||
systemCommandTask.cancel(interruptOnCancel);
|
||||
throw new JobInterruptedException("Job interrupted while executing system command '" + command
|
||||
+ "'");
|
||||
}
|
||||
}
|
||||
}
|
||||
finally {
|
||||
stopWatch.stop();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param command command to be executed in a separate system process
|
||||
*/
|
||||
public void setCommand(String command) {
|
||||
this.command = command;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param envp environment parameter values, inherited from parent process
|
||||
* when not set (or set to null).
|
||||
*/
|
||||
public void setEnvironmentParams(String[] envp) {
|
||||
this.environmentParams = envp;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param dir working directory of the spawned process, inherited from
|
||||
* parent process when not set (or set to null).
|
||||
*/
|
||||
public void setWorkingDirectory(String dir) {
|
||||
if (dir == null) {
|
||||
this.workingDirectory = null;
|
||||
return;
|
||||
}
|
||||
this.workingDirectory = new File(dir);
|
||||
Assert.isTrue(workingDirectory.exists(), "working directory must exist");
|
||||
Assert.isTrue(workingDirectory.isDirectory(), "working directory value must be a directory");
|
||||
|
||||
}
|
||||
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
Assert.hasLength(command, "'command' property value is required");
|
||||
Assert.notNull(systemProcessExitCodeMapper, "SystemProcessExitCodeMapper must be set");
|
||||
Assert.isTrue(timeout > 0, "timeout value must be greater than zero");
|
||||
Assert.notNull(taskExecutor, "taskExecutor is required");
|
||||
}
|
||||
|
||||
/**
|
||||
* @param systemProcessExitCodeMapper maps system process return value to
|
||||
* <code>ExitStatus</code> returned by Tasklet.
|
||||
* {@link SimpleSystemProcessExitCodeMapper} is used by default.
|
||||
*/
|
||||
public void setSystemProcessExitCodeMapper(SystemProcessExitCodeMapper systemProcessExitCodeMapper) {
|
||||
this.systemProcessExitCodeMapper = systemProcessExitCodeMapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param timeout upper limit for how long the execution of the external
|
||||
* program is allowed to last.
|
||||
*/
|
||||
public void setTimeout(long timeout) {
|
||||
this.timeout = timeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* The time interval how often the tasklet will check for termination
|
||||
* status.
|
||||
*
|
||||
* @param checkInterval time interval in milliseconds (1 second by default).
|
||||
*/
|
||||
public void setTerminationCheckInterval(long checkInterval) {
|
||||
this.checkInterval = checkInterval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a reference to {@link StepExecution} for interrupt checks during
|
||||
* system command execution.
|
||||
*/
|
||||
@Override
|
||||
public void beforeStep(StepExecution stepExecution) {
|
||||
this.execution = stepExecution;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the task executor that will be used to execute the system command
|
||||
* NB! Avoid using a synchronous task executor
|
||||
*/
|
||||
public void setTaskExecutor(TaskExecutor taskExecutor) {
|
||||
this.taskExecutor = taskExecutor;
|
||||
}
|
||||
|
||||
/**
|
||||
* If <code>true</code> tasklet will attempt to interrupt the thread
|
||||
* executing the system command if {@link #setTimeout(long)} has been
|
||||
* exceeded or user interrupts the job. <code>false</code> by default
|
||||
*/
|
||||
public void setInterruptOnCancel(boolean interruptOnCancel) {
|
||||
this.interruptOnCancel = interruptOnCancel;
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* 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.springframework.batch.core.step.tasklet;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.FutureTask;
|
||||
|
||||
import org.apache.commons.lang.time.StopWatch;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.batch.core.ExitStatus;
|
||||
import org.springframework.batch.core.JobInterruptedException;
|
||||
import org.springframework.batch.core.StepContribution;
|
||||
import org.springframework.batch.core.StepExecution;
|
||||
import org.springframework.batch.core.listener.StepExecutionListenerSupport;
|
||||
import org.springframework.batch.core.scope.context.ChunkContext;
|
||||
import org.springframework.batch.repeat.RepeatStatus;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.core.task.SimpleAsyncTaskExecutor;
|
||||
import org.springframework.core.task.TaskExecutor;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* {@link Tasklet} that executes a system command.
|
||||
*
|
||||
* The system command is executed asynchronously using injected
|
||||
* {@link #setTaskExecutor(TaskExecutor)} - timeout value is required to be set,
|
||||
* so that the batch job does not hang forever if the external process hangs.
|
||||
*
|
||||
* Tasklet periodically checks for termination status (i.e.
|
||||
* {@link #setCommand(String)} finished its execution or
|
||||
* {@link #setTimeout(long)} expired or job was interrupted). The check interval
|
||||
* is given by {@link #setTerminationCheckInterval(long)}.
|
||||
*
|
||||
* When job interrupt is detected tasklet's execution is terminated immediately
|
||||
* by throwing {@link JobInterruptedException}.
|
||||
*
|
||||
* {@link #setInterruptOnCancel(boolean)} specifies whether the tasklet should
|
||||
* attempt to interrupt the thread that executes the system command if it is
|
||||
* still running when tasklet exits (abnormally).
|
||||
*
|
||||
* @author Robert Kasanicky
|
||||
*/
|
||||
public class SystemCommandTasklet extends StepExecutionListenerSupport implements Tasklet, InitializingBean {
|
||||
|
||||
protected static final Log logger = LogFactory.getLog(SystemCommandTasklet.class);
|
||||
|
||||
private String command;
|
||||
|
||||
private String[] environmentParams = null;
|
||||
|
||||
private File workingDirectory = null;
|
||||
|
||||
private SystemProcessExitCodeMapper systemProcessExitCodeMapper = new SimpleSystemProcessExitCodeMapper();
|
||||
|
||||
private long timeout = 0;
|
||||
|
||||
private long checkInterval = 1000;
|
||||
|
||||
private StepExecution execution = null;
|
||||
|
||||
private TaskExecutor taskExecutor = new SimpleAsyncTaskExecutor();
|
||||
|
||||
private boolean interruptOnCancel = false;
|
||||
|
||||
/**
|
||||
* Execute system command and map its exit code to {@link ExitStatus} using
|
||||
* {@link SystemProcessExitCodeMapper}.
|
||||
*/
|
||||
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
|
||||
|
||||
FutureTask<Integer> systemCommandTask = new FutureTask<Integer>(new Callable<Integer>() {
|
||||
|
||||
public Integer call() throws Exception {
|
||||
Process process = Runtime.getRuntime().exec(command, environmentParams, workingDirectory);
|
||||
return process.waitFor();
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
StopWatch stopWatch = new StopWatch();
|
||||
stopWatch.start();
|
||||
|
||||
taskExecutor.execute(systemCommandTask);
|
||||
|
||||
try {
|
||||
while (true) {
|
||||
Thread.sleep(checkInterval);
|
||||
if (systemCommandTask.isDone()) {
|
||||
contribution.setExitStatus(systemProcessExitCodeMapper.getExitStatus(systemCommandTask.get()));
|
||||
return RepeatStatus.FINISHED;
|
||||
}
|
||||
else if (stopWatch.getTime() > timeout) {
|
||||
systemCommandTask.cancel(interruptOnCancel);
|
||||
throw new SystemCommandException("Execution of system command did not finish within the timeout");
|
||||
}
|
||||
else if (execution.isTerminateOnly()) {
|
||||
systemCommandTask.cancel(interruptOnCancel);
|
||||
throw new JobInterruptedException("Job interrupted while executing system command '" + command
|
||||
+ "'");
|
||||
}
|
||||
}
|
||||
}
|
||||
finally {
|
||||
stopWatch.stop();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param command command to be executed in a separate system process
|
||||
*/
|
||||
public void setCommand(String command) {
|
||||
this.command = command;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param envp environment parameter values, inherited from parent process
|
||||
* when not set (or set to null).
|
||||
*/
|
||||
public void setEnvironmentParams(String[] envp) {
|
||||
this.environmentParams = envp;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param dir working directory of the spawned process, inherited from
|
||||
* parent process when not set (or set to null).
|
||||
*/
|
||||
public void setWorkingDirectory(String dir) {
|
||||
if (dir == null) {
|
||||
this.workingDirectory = null;
|
||||
return;
|
||||
}
|
||||
this.workingDirectory = new File(dir);
|
||||
Assert.isTrue(workingDirectory.exists(), "working directory must exist");
|
||||
Assert.isTrue(workingDirectory.isDirectory(), "working directory value must be a directory");
|
||||
|
||||
}
|
||||
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
Assert.hasLength(command, "'command' property value is required");
|
||||
Assert.notNull(systemProcessExitCodeMapper, "SystemProcessExitCodeMapper must be set");
|
||||
Assert.isTrue(timeout > 0, "timeout value must be greater than zero");
|
||||
Assert.notNull(taskExecutor, "taskExecutor is required");
|
||||
}
|
||||
|
||||
/**
|
||||
* @param systemProcessExitCodeMapper maps system process return value to
|
||||
* <code>ExitStatus</code> returned by Tasklet.
|
||||
* {@link SimpleSystemProcessExitCodeMapper} is used by default.
|
||||
*/
|
||||
public void setSystemProcessExitCodeMapper(SystemProcessExitCodeMapper systemProcessExitCodeMapper) {
|
||||
this.systemProcessExitCodeMapper = systemProcessExitCodeMapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param timeout upper limit for how long the execution of the external
|
||||
* program is allowed to last.
|
||||
*/
|
||||
public void setTimeout(long timeout) {
|
||||
this.timeout = timeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* The time interval how often the tasklet will check for termination
|
||||
* status.
|
||||
*
|
||||
* @param checkInterval time interval in milliseconds (1 second by default).
|
||||
*/
|
||||
public void setTerminationCheckInterval(long checkInterval) {
|
||||
this.checkInterval = checkInterval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a reference to {@link StepExecution} for interrupt checks during
|
||||
* system command execution.
|
||||
*/
|
||||
@Override
|
||||
public void beforeStep(StepExecution stepExecution) {
|
||||
this.execution = stepExecution;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the task executor that will be used to execute the system command
|
||||
* NB! Avoid using a synchronous task executor
|
||||
*/
|
||||
public void setTaskExecutor(TaskExecutor taskExecutor) {
|
||||
this.taskExecutor = taskExecutor;
|
||||
}
|
||||
|
||||
/**
|
||||
* If <code>true</code> tasklet will attempt to interrupt the thread
|
||||
* executing the system command if {@link #setTimeout(long)} has been
|
||||
* exceeded or user interrupts the job. <code>false</code> by default
|
||||
*/
|
||||
public void setInterruptOnCancel(boolean interruptOnCancel) {
|
||||
this.interruptOnCancel = interruptOnCancel;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,20 +1,36 @@
|
||||
package org.springframework.batch.core.step.tasklet;
|
||||
|
||||
import org.springframework.batch.core.ExitStatus;
|
||||
import org.springframework.batch.core.step.tasklet.SystemCommandTasklet;
|
||||
|
||||
/**
|
||||
* Maps the exit code of a system process to ExitStatus value
|
||||
* returned by a system command. Designed for use with the
|
||||
* {@link SystemCommandTasklet}.
|
||||
*
|
||||
* @author Robert Kasanicky
|
||||
*/
|
||||
public interface SystemProcessExitCodeMapper {
|
||||
|
||||
/**
|
||||
* @param exitCode exit code returned by the system process
|
||||
* @return ExitStatus appropriate for the <code>systemExitCode</code> parameter value
|
||||
*/
|
||||
ExitStatus getExitStatus(int exitCode);
|
||||
}
|
||||
/*
|
||||
* 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.springframework.batch.core.step.tasklet;
|
||||
|
||||
import org.springframework.batch.core.ExitStatus;
|
||||
import org.springframework.batch.core.step.tasklet.SystemCommandTasklet;
|
||||
|
||||
/**
|
||||
* Maps the exit code of a system process to ExitStatus value
|
||||
* returned by a system command. Designed for use with the
|
||||
* {@link SystemCommandTasklet}.
|
||||
*
|
||||
* @author Robert Kasanicky
|
||||
*/
|
||||
public interface SystemProcessExitCodeMapper {
|
||||
|
||||
/**
|
||||
* @param exitCode exit code returned by the system process
|
||||
* @return ExitStatus appropriate for the <code>systemExitCode</code> parameter value
|
||||
*/
|
||||
ExitStatus getExitStatus(int exitCode);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
* 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.springframework.batch.item;
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
* 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.springframework.batch.item.adapter;
|
||||
|
||||
import org.springframework.util.MethodInvoker;
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
* 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.springframework.batch.item.database;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -1,6 +1,19 @@
|
||||
/**
|
||||
*
|
||||
/*
|
||||
* 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.springframework.batch.item.database.support;
|
||||
|
||||
import java.sql.PreparedStatement;
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
* 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.springframework.batch.item.database.support;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -1,18 +1,34 @@
|
||||
package org.springframework.batch.item.file;
|
||||
|
||||
import java.io.Writer;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Callback interface for writing a footer to a file.
|
||||
*
|
||||
* @author Robert Kasanicky
|
||||
*/
|
||||
public interface FlatFileFooterCallback {
|
||||
|
||||
/**
|
||||
* Write contents to a file using the supplied {@link Writer}. It is not
|
||||
* required to flush the writer inside this method.
|
||||
*/
|
||||
void writeFooter(Writer writer) throws IOException;
|
||||
}
|
||||
/*
|
||||
* 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.springframework.batch.item.file;
|
||||
|
||||
import java.io.Writer;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Callback interface for writing a footer to a file.
|
||||
*
|
||||
* @author Robert Kasanicky
|
||||
*/
|
||||
public interface FlatFileFooterCallback {
|
||||
|
||||
/**
|
||||
* Write contents to a file using the supplied {@link Writer}. It is not
|
||||
* required to flush the writer inside this method.
|
||||
*/
|
||||
void writeFooter(Writer writer) throws IOException;
|
||||
}
|
||||
|
||||
@@ -1,18 +1,34 @@
|
||||
package org.springframework.batch.item.file;
|
||||
|
||||
import java.io.Writer;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Callback interface for writing to a header to a file.
|
||||
*
|
||||
* @author Robert Kasanicky
|
||||
*/
|
||||
public interface FlatFileHeaderCallback {
|
||||
|
||||
/**
|
||||
* Write contents to a file using the supplied {@link Writer}. It is not
|
||||
* required to flush the writer inside this method.
|
||||
*/
|
||||
void writeHeader(Writer writer) throws IOException;
|
||||
}
|
||||
/*
|
||||
* 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.springframework.batch.item.file;
|
||||
|
||||
import java.io.Writer;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Callback interface for writing to a header to a file.
|
||||
*
|
||||
* @author Robert Kasanicky
|
||||
*/
|
||||
public interface FlatFileHeaderCallback {
|
||||
|
||||
/**
|
||||
* Write contents to a file using the supplied {@link Writer}. It is not
|
||||
* required to flush the writer inside this method.
|
||||
*/
|
||||
void writeHeader(Writer writer) throws IOException;
|
||||
}
|
||||
|
||||
@@ -1,227 +1,243 @@
|
||||
package org.springframework.batch.item.file;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.batch.item.ItemReader;
|
||||
import org.springframework.batch.item.ReaderNotOpenException;
|
||||
import org.springframework.batch.item.file.mapping.LineMapper;
|
||||
import org.springframework.batch.item.file.separator.RecordSeparatorPolicy;
|
||||
import org.springframework.batch.item.file.separator.SimpleRecordSeparatorPolicy;
|
||||
import org.springframework.batch.item.support.AbstractItemCountingItemStreamItemReader;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* Restartable {@link ItemReader} that reads lines from input
|
||||
* {@link #setResource(Resource)}. Line is defined by the
|
||||
* {@link #setRecordSeparatorPolicy(RecordSeparatorPolicy)} and mapped to item
|
||||
* using {@link #setLineMapper(LineMapper)}.
|
||||
*
|
||||
* @author Robert Kasanicky
|
||||
*/
|
||||
public class FlatFileItemReader<T> extends AbstractItemCountingItemStreamItemReader<T> implements
|
||||
ResourceAwareItemReaderItemStream<T>, InitializingBean {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(FlatFileItemReader.class);
|
||||
|
||||
// default encoding for input files
|
||||
public static final String DEFAULT_CHARSET = Charset.defaultCharset().name();
|
||||
|
||||
private RecordSeparatorPolicy recordSeparatorPolicy = new SimpleRecordSeparatorPolicy();
|
||||
|
||||
private Resource resource;
|
||||
|
||||
private BufferedReader reader;
|
||||
|
||||
private int lineCount = 0;
|
||||
|
||||
private String[] comments = new String[] { "#" };
|
||||
|
||||
private boolean noInput = false;
|
||||
|
||||
private String encoding = DEFAULT_CHARSET;
|
||||
|
||||
private LineMapper<T> lineMapper;
|
||||
|
||||
private int linesToSkip = 0;
|
||||
|
||||
private LineCallbackHandler skippedLinesCallback;
|
||||
|
||||
public FlatFileItemReader() {
|
||||
setName(ClassUtils.getShortName(FlatFileItemReader.class));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param skippedLinesCallback will be called for each one of the initial skipped
|
||||
* lines before any items are read.
|
||||
*/
|
||||
public void setSkippedLinesCallback(LineCallbackHandler skippedLinesCallback) {
|
||||
this.skippedLinesCallback = skippedLinesCallback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Public setter for the number of lines to skip at the start of a file. Can
|
||||
* be used if the file contains a header without useful (column name)
|
||||
* information, and without a comment delimiter at the beginning of the
|
||||
* lines.
|
||||
*
|
||||
* @param linesToSkip the number of lines to skip
|
||||
*/
|
||||
public void setLinesToSkip(int linesToSkip) {
|
||||
this.linesToSkip = linesToSkip;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter for line mapper. This property is required to be set.
|
||||
* @param lineMapper maps line to item
|
||||
*/
|
||||
public void setLineMapper(LineMapper<T> lineMapper) {
|
||||
this.lineMapper = lineMapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter for the encoding for this input source. Default value is
|
||||
* {@link #DEFAULT_CHARSET}.
|
||||
*
|
||||
* @param encoding a properties object which possibly contains the encoding
|
||||
* for this input file;
|
||||
*/
|
||||
public void setEncoding(String encoding) {
|
||||
this.encoding = encoding;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter for comment prefixes. Can be used to ignore header lines as well
|
||||
* by using e.g. the first couple of column names as a prefix.
|
||||
*
|
||||
* @param comments an array of comment line prefixes.
|
||||
*/
|
||||
public void setComments(String[] comments) {
|
||||
this.comments = new String[comments.length];
|
||||
System.arraycopy(comments, 0, this.comments, 0, comments.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Public setter for the input resource.
|
||||
*/
|
||||
public void setResource(Resource resource) {
|
||||
this.resource = resource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Public setter for the recordSeparatorPolicy. Used to determine where the
|
||||
* line endings are and do things like continue over a line ending if inside
|
||||
* a quoted string.
|
||||
*
|
||||
* @param recordSeparatorPolicy the recordSeparatorPolicy to set
|
||||
*/
|
||||
public void setRecordSeparatorPolicy(RecordSeparatorPolicy recordSeparatorPolicy) {
|
||||
this.recordSeparatorPolicy = recordSeparatorPolicy;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string corresponding to logical record according to
|
||||
* {@link #setRecordSeparatorPolicy(RecordSeparatorPolicy)} (might span
|
||||
* multiple lines in file).
|
||||
*/
|
||||
@Override
|
||||
protected T doRead() throws Exception {
|
||||
if (noInput) {
|
||||
return null;
|
||||
}
|
||||
String line = readLine();
|
||||
String record = line;
|
||||
if (line != null) {
|
||||
while (line != null && !recordSeparatorPolicy.isEndOfRecord(record)) {
|
||||
record = recordSeparatorPolicy.preProcess(record) + (line = readLine());
|
||||
}
|
||||
}
|
||||
String logicalLine = recordSeparatorPolicy.postProcess(record);
|
||||
if (logicalLine == null) {
|
||||
return null;
|
||||
}
|
||||
else {
|
||||
return lineMapper.mapLine(logicalLine, lineCount);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return next line (skip comments).
|
||||
*/
|
||||
private String readLine() {
|
||||
|
||||
if (reader == null) {
|
||||
throw new ReaderNotOpenException("Reader must be open before it can be read.");
|
||||
}
|
||||
|
||||
String line = null;
|
||||
|
||||
try {
|
||||
line = this.reader.readLine();
|
||||
if (line == null) {
|
||||
return null;
|
||||
}
|
||||
lineCount++;
|
||||
while (isComment(line)) {
|
||||
line = reader.readLine();
|
||||
if (line == null) {
|
||||
return null;
|
||||
}
|
||||
lineCount++;
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new FlatFileParseException("Unable to read from resource: [" + resource + "]", e, line, lineCount );
|
||||
}
|
||||
return line;
|
||||
}
|
||||
|
||||
private boolean isComment(String line) {
|
||||
for (String prefix : comments) {
|
||||
if (line.startsWith(prefix)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doClose() throws Exception {
|
||||
lineCount = 0;
|
||||
reader.close();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doOpen() throws Exception {
|
||||
Assert.notNull(resource, "Input resource must be set");
|
||||
Assert.notNull(recordSeparatorPolicy, "RecordSeparatorPolicy must be set");
|
||||
|
||||
noInput = false;
|
||||
if (!resource.exists()) {
|
||||
noInput = true;
|
||||
logger.warn("Input resource does not exist");
|
||||
return;
|
||||
}
|
||||
|
||||
reader = new BufferedReader(new InputStreamReader(resource.getInputStream(), encoding));
|
||||
for (int i = 0; i < linesToSkip; i++) {
|
||||
String line = readLine();
|
||||
if (skippedLinesCallback != null) {
|
||||
skippedLinesCallback.handleLine(line);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
Assert.notNull(lineMapper, "LineMapper is required");
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* 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.springframework.batch.item.file;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.batch.item.ItemReader;
|
||||
import org.springframework.batch.item.ReaderNotOpenException;
|
||||
import org.springframework.batch.item.file.mapping.LineMapper;
|
||||
import org.springframework.batch.item.file.separator.RecordSeparatorPolicy;
|
||||
import org.springframework.batch.item.file.separator.SimpleRecordSeparatorPolicy;
|
||||
import org.springframework.batch.item.support.AbstractItemCountingItemStreamItemReader;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* Restartable {@link ItemReader} that reads lines from input
|
||||
* {@link #setResource(Resource)}. Line is defined by the
|
||||
* {@link #setRecordSeparatorPolicy(RecordSeparatorPolicy)} and mapped to item
|
||||
* using {@link #setLineMapper(LineMapper)}.
|
||||
*
|
||||
* @author Robert Kasanicky
|
||||
*/
|
||||
public class FlatFileItemReader<T> extends AbstractItemCountingItemStreamItemReader<T> implements
|
||||
ResourceAwareItemReaderItemStream<T>, InitializingBean {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(FlatFileItemReader.class);
|
||||
|
||||
// default encoding for input files
|
||||
public static final String DEFAULT_CHARSET = Charset.defaultCharset().name();
|
||||
|
||||
private RecordSeparatorPolicy recordSeparatorPolicy = new SimpleRecordSeparatorPolicy();
|
||||
|
||||
private Resource resource;
|
||||
|
||||
private BufferedReader reader;
|
||||
|
||||
private int lineCount = 0;
|
||||
|
||||
private String[] comments = new String[] { "#" };
|
||||
|
||||
private boolean noInput = false;
|
||||
|
||||
private String encoding = DEFAULT_CHARSET;
|
||||
|
||||
private LineMapper<T> lineMapper;
|
||||
|
||||
private int linesToSkip = 0;
|
||||
|
||||
private LineCallbackHandler skippedLinesCallback;
|
||||
|
||||
public FlatFileItemReader() {
|
||||
setName(ClassUtils.getShortName(FlatFileItemReader.class));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param skippedLinesCallback will be called for each one of the initial skipped
|
||||
* lines before any items are read.
|
||||
*/
|
||||
public void setSkippedLinesCallback(LineCallbackHandler skippedLinesCallback) {
|
||||
this.skippedLinesCallback = skippedLinesCallback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Public setter for the number of lines to skip at the start of a file. Can
|
||||
* be used if the file contains a header without useful (column name)
|
||||
* information, and without a comment delimiter at the beginning of the
|
||||
* lines.
|
||||
*
|
||||
* @param linesToSkip the number of lines to skip
|
||||
*/
|
||||
public void setLinesToSkip(int linesToSkip) {
|
||||
this.linesToSkip = linesToSkip;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter for line mapper. This property is required to be set.
|
||||
* @param lineMapper maps line to item
|
||||
*/
|
||||
public void setLineMapper(LineMapper<T> lineMapper) {
|
||||
this.lineMapper = lineMapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter for the encoding for this input source. Default value is
|
||||
* {@link #DEFAULT_CHARSET}.
|
||||
*
|
||||
* @param encoding a properties object which possibly contains the encoding
|
||||
* for this input file;
|
||||
*/
|
||||
public void setEncoding(String encoding) {
|
||||
this.encoding = encoding;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter for comment prefixes. Can be used to ignore header lines as well
|
||||
* by using e.g. the first couple of column names as a prefix.
|
||||
*
|
||||
* @param comments an array of comment line prefixes.
|
||||
*/
|
||||
public void setComments(String[] comments) {
|
||||
this.comments = new String[comments.length];
|
||||
System.arraycopy(comments, 0, this.comments, 0, comments.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Public setter for the input resource.
|
||||
*/
|
||||
public void setResource(Resource resource) {
|
||||
this.resource = resource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Public setter for the recordSeparatorPolicy. Used to determine where the
|
||||
* line endings are and do things like continue over a line ending if inside
|
||||
* a quoted string.
|
||||
*
|
||||
* @param recordSeparatorPolicy the recordSeparatorPolicy to set
|
||||
*/
|
||||
public void setRecordSeparatorPolicy(RecordSeparatorPolicy recordSeparatorPolicy) {
|
||||
this.recordSeparatorPolicy = recordSeparatorPolicy;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string corresponding to logical record according to
|
||||
* {@link #setRecordSeparatorPolicy(RecordSeparatorPolicy)} (might span
|
||||
* multiple lines in file).
|
||||
*/
|
||||
@Override
|
||||
protected T doRead() throws Exception {
|
||||
if (noInput) {
|
||||
return null;
|
||||
}
|
||||
String line = readLine();
|
||||
String record = line;
|
||||
if (line != null) {
|
||||
while (line != null && !recordSeparatorPolicy.isEndOfRecord(record)) {
|
||||
record = recordSeparatorPolicy.preProcess(record) + (line = readLine());
|
||||
}
|
||||
}
|
||||
String logicalLine = recordSeparatorPolicy.postProcess(record);
|
||||
if (logicalLine == null) {
|
||||
return null;
|
||||
}
|
||||
else {
|
||||
return lineMapper.mapLine(logicalLine, lineCount);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return next line (skip comments).
|
||||
*/
|
||||
private String readLine() {
|
||||
|
||||
if (reader == null) {
|
||||
throw new ReaderNotOpenException("Reader must be open before it can be read.");
|
||||
}
|
||||
|
||||
String line = null;
|
||||
|
||||
try {
|
||||
line = this.reader.readLine();
|
||||
if (line == null) {
|
||||
return null;
|
||||
}
|
||||
lineCount++;
|
||||
while (isComment(line)) {
|
||||
line = reader.readLine();
|
||||
if (line == null) {
|
||||
return null;
|
||||
}
|
||||
lineCount++;
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new FlatFileParseException("Unable to read from resource: [" + resource + "]", e, line, lineCount );
|
||||
}
|
||||
return line;
|
||||
}
|
||||
|
||||
private boolean isComment(String line) {
|
||||
for (String prefix : comments) {
|
||||
if (line.startsWith(prefix)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doClose() throws Exception {
|
||||
lineCount = 0;
|
||||
reader.close();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doOpen() throws Exception {
|
||||
Assert.notNull(resource, "Input resource must be set");
|
||||
Assert.notNull(recordSeparatorPolicy, "RecordSeparatorPolicy must be set");
|
||||
|
||||
noInput = false;
|
||||
if (!resource.exists()) {
|
||||
noInput = true;
|
||||
logger.warn("Input resource does not exist");
|
||||
return;
|
||||
}
|
||||
|
||||
reader = new BufferedReader(new InputStreamReader(resource.getInputStream(), encoding));
|
||||
for (int i = 0; i < linesToSkip; i++) {
|
||||
String line = readLine();
|
||||
if (skippedLinesCallback != null) {
|
||||
skippedLinesCallback.handleLine(line);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
Assert.notNull(lineMapper, "LineMapper is required");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,12 +1,28 @@
|
||||
package org.springframework.batch.item.file;
|
||||
|
||||
/**
|
||||
* Callback interface for handling a line from file. Useful e.g. for header
|
||||
* processing.
|
||||
*
|
||||
* @author Robert Kasanicky
|
||||
*/
|
||||
public interface LineCallbackHandler {
|
||||
|
||||
void handleLine(String line);
|
||||
}
|
||||
/*
|
||||
* 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.springframework.batch.item.file;
|
||||
|
||||
/**
|
||||
* Callback interface for handling a line from file. Useful e.g. for header
|
||||
* processing.
|
||||
*
|
||||
* @author Robert Kasanicky
|
||||
*/
|
||||
public interface LineCallbackHandler {
|
||||
|
||||
void handleLine(String line);
|
||||
}
|
||||
|
||||
@@ -1,245 +1,261 @@
|
||||
package org.springframework.batch.item.file;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.batch.item.ExecutionContext;
|
||||
import org.springframework.batch.item.ItemReader;
|
||||
import org.springframework.batch.item.ItemStream;
|
||||
import org.springframework.batch.item.ItemStreamException;
|
||||
import org.springframework.batch.item.NoWorkFoundException;
|
||||
import org.springframework.batch.item.ParseException;
|
||||
import org.springframework.batch.item.UnexpectedInputException;
|
||||
import org.springframework.batch.item.util.ExecutionContextUserSupport;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* Reads items from multiple resources sequentially - resource list is given by
|
||||
* {@link #setResources(Resource[])}, the actual reading is delegated to
|
||||
* {@link #setDelegate(ResourceAwareItemReaderItemStream)}.
|
||||
*
|
||||
* Input resources are ordered using {@link #setComparator(Comparator)} to make
|
||||
* sure resource ordering is preserved between job runs in restart scenario.
|
||||
*
|
||||
* Reset (rollback) capability is implemented by item buffering.
|
||||
*
|
||||
*
|
||||
* @author Robert Kasanicky
|
||||
*/
|
||||
public class MultiResourceItemReader<T> implements ItemReader<T>, ItemStream {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(MultiResourceItemReader.class);
|
||||
|
||||
private final ExecutionContextUserSupport executionContextUserSupport = new ExecutionContextUserSupport();
|
||||
|
||||
private ResourceAwareItemReaderItemStream<? extends T> delegate;
|
||||
|
||||
private Resource[] resources;
|
||||
|
||||
private MultiResourceIndex index = new MultiResourceIndex();
|
||||
|
||||
private boolean saveState = true;
|
||||
|
||||
// signals there are no resources to read -> just return null on first read
|
||||
private boolean noInput;
|
||||
|
||||
private Comparator<Resource> comparator = new Comparator<Resource>() {
|
||||
|
||||
/**
|
||||
* Compares resource filenames.
|
||||
*/
|
||||
public int compare(Resource r1, Resource r2) {
|
||||
return r1.getFilename().compareTo(r2.getFilename());
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
public MultiResourceItemReader() {
|
||||
executionContextUserSupport.setName(ClassUtils.getShortName(MultiResourceItemReader.class));
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the next item, jumping to next resource if necessary.
|
||||
*/
|
||||
public T read() throws Exception, UnexpectedInputException, NoWorkFoundException, ParseException {
|
||||
|
||||
if (noInput) {
|
||||
return null;
|
||||
}
|
||||
|
||||
T item;
|
||||
item = readNextItem();
|
||||
index.incrementItemCount();
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use the delegate to read the next item, jump to next resource if current
|
||||
* one is exhausted. Items are appended to the buffer.
|
||||
* @return next item from input
|
||||
*/
|
||||
private T readNextItem() throws Exception {
|
||||
|
||||
T item = delegate.read();
|
||||
|
||||
while (item == null) {
|
||||
|
||||
index.incrementResourceCount();
|
||||
|
||||
if (index.currentResource >= resources.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
delegate.close();
|
||||
delegate.setResource(resources[index.currentResource]);
|
||||
delegate.open(new ExecutionContext());
|
||||
|
||||
item = delegate.read();
|
||||
}
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the {@link #setDelegate(ResourceAwareItemReaderItemStream)} reader
|
||||
* and reset instance variable values.
|
||||
*/
|
||||
public void close() throws ItemStreamException {
|
||||
index = new MultiResourceIndex();
|
||||
delegate.close();
|
||||
noInput = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Figure out which resource to start with in case of restart, open the
|
||||
* delegate and restore delegate's position in the resource.
|
||||
*/
|
||||
public void open(ExecutionContext executionContext) throws ItemStreamException {
|
||||
|
||||
Assert.notNull(resources, "Resources must be set");
|
||||
|
||||
noInput = false;
|
||||
if (resources.length == 0) {
|
||||
logger.warn("No resources to read");
|
||||
noInput = true;
|
||||
return;
|
||||
}
|
||||
|
||||
Arrays.sort(resources, comparator);
|
||||
|
||||
index.open(executionContext);
|
||||
|
||||
delegate.setResource(resources[index.currentResource]);
|
||||
|
||||
delegate.open(new ExecutionContext());
|
||||
|
||||
try {
|
||||
for (int i = 0; i < index.currentItem; i++) {
|
||||
delegate.read();
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new ItemStreamException("Could not restore position on restart", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Store the current resource index and position in the resource.
|
||||
*/
|
||||
public void update(ExecutionContext executionContext) throws ItemStreamException {
|
||||
if (saveState) {
|
||||
index.update(executionContext);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param delegate reads items from single {@link Resource}.
|
||||
*/
|
||||
public void setDelegate(ResourceAwareItemReaderItemStream<? extends T> delegate) {
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the boolean indicating whether or not state should be saved in the
|
||||
* provided {@link ExecutionContext} during the {@link ItemStream} call to
|
||||
* update.
|
||||
*
|
||||
* @param saveState
|
||||
*/
|
||||
public void setSaveState(boolean saveState) {
|
||||
this.saveState = saveState;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param comparator used to order the injected resources, by default
|
||||
* compares {@link Resource#getFilename()} values.
|
||||
*/
|
||||
public void setComparator(Comparator<Resource> comparator) {
|
||||
this.comparator = comparator;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param resources input resources
|
||||
*/
|
||||
public void setResources(Resource[] resources) {
|
||||
this.resources = resources;
|
||||
}
|
||||
|
||||
/**
|
||||
* Facilitates keeping track of the position within multi-resource input.
|
||||
*/
|
||||
private class MultiResourceIndex {
|
||||
|
||||
private static final String RESOURCE_KEY = "resourceIndex";
|
||||
|
||||
private static final String ITEM_KEY = "itemIndex";
|
||||
|
||||
private int currentResource = 0;
|
||||
|
||||
private int markedResource = 0;
|
||||
|
||||
private int currentItem = 0;
|
||||
|
||||
private int markedItem = 0;
|
||||
|
||||
public void incrementItemCount() {
|
||||
currentItem++;
|
||||
}
|
||||
|
||||
public void incrementResourceCount() {
|
||||
currentResource++;
|
||||
currentItem = 0;
|
||||
}
|
||||
|
||||
public void mark() {
|
||||
markedResource = currentResource;
|
||||
markedItem = currentItem;
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
currentResource = markedResource;
|
||||
currentItem = markedItem;
|
||||
}
|
||||
|
||||
public void open(ExecutionContext ctx) {
|
||||
if (ctx.containsKey(executionContextUserSupport.getKey(RESOURCE_KEY))) {
|
||||
currentResource = ctx.getInt(executionContextUserSupport.getKey(RESOURCE_KEY));
|
||||
}
|
||||
|
||||
if (ctx.containsKey(executionContextUserSupport.getKey(ITEM_KEY))) {
|
||||
currentItem = ctx.getInt(executionContextUserSupport.getKey(ITEM_KEY));
|
||||
}
|
||||
}
|
||||
|
||||
public void update(ExecutionContext ctx) {
|
||||
ctx.putInt(executionContextUserSupport.getKey(RESOURCE_KEY), index.currentResource);
|
||||
ctx.putInt(executionContextUserSupport.getKey(ITEM_KEY), index.currentItem);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* 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.springframework.batch.item.file;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.batch.item.ExecutionContext;
|
||||
import org.springframework.batch.item.ItemReader;
|
||||
import org.springframework.batch.item.ItemStream;
|
||||
import org.springframework.batch.item.ItemStreamException;
|
||||
import org.springframework.batch.item.NoWorkFoundException;
|
||||
import org.springframework.batch.item.ParseException;
|
||||
import org.springframework.batch.item.UnexpectedInputException;
|
||||
import org.springframework.batch.item.util.ExecutionContextUserSupport;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* Reads items from multiple resources sequentially - resource list is given by
|
||||
* {@link #setResources(Resource[])}, the actual reading is delegated to
|
||||
* {@link #setDelegate(ResourceAwareItemReaderItemStream)}.
|
||||
*
|
||||
* Input resources are ordered using {@link #setComparator(Comparator)} to make
|
||||
* sure resource ordering is preserved between job runs in restart scenario.
|
||||
*
|
||||
* Reset (rollback) capability is implemented by item buffering.
|
||||
*
|
||||
*
|
||||
* @author Robert Kasanicky
|
||||
*/
|
||||
public class MultiResourceItemReader<T> implements ItemReader<T>, ItemStream {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(MultiResourceItemReader.class);
|
||||
|
||||
private final ExecutionContextUserSupport executionContextUserSupport = new ExecutionContextUserSupport();
|
||||
|
||||
private ResourceAwareItemReaderItemStream<? extends T> delegate;
|
||||
|
||||
private Resource[] resources;
|
||||
|
||||
private MultiResourceIndex index = new MultiResourceIndex();
|
||||
|
||||
private boolean saveState = true;
|
||||
|
||||
// signals there are no resources to read -> just return null on first read
|
||||
private boolean noInput;
|
||||
|
||||
private Comparator<Resource> comparator = new Comparator<Resource>() {
|
||||
|
||||
/**
|
||||
* Compares resource filenames.
|
||||
*/
|
||||
public int compare(Resource r1, Resource r2) {
|
||||
return r1.getFilename().compareTo(r2.getFilename());
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
public MultiResourceItemReader() {
|
||||
executionContextUserSupport.setName(ClassUtils.getShortName(MultiResourceItemReader.class));
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the next item, jumping to next resource if necessary.
|
||||
*/
|
||||
public T read() throws Exception, UnexpectedInputException, NoWorkFoundException, ParseException {
|
||||
|
||||
if (noInput) {
|
||||
return null;
|
||||
}
|
||||
|
||||
T item;
|
||||
item = readNextItem();
|
||||
index.incrementItemCount();
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use the delegate to read the next item, jump to next resource if current
|
||||
* one is exhausted. Items are appended to the buffer.
|
||||
* @return next item from input
|
||||
*/
|
||||
private T readNextItem() throws Exception {
|
||||
|
||||
T item = delegate.read();
|
||||
|
||||
while (item == null) {
|
||||
|
||||
index.incrementResourceCount();
|
||||
|
||||
if (index.currentResource >= resources.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
delegate.close();
|
||||
delegate.setResource(resources[index.currentResource]);
|
||||
delegate.open(new ExecutionContext());
|
||||
|
||||
item = delegate.read();
|
||||
}
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the {@link #setDelegate(ResourceAwareItemReaderItemStream)} reader
|
||||
* and reset instance variable values.
|
||||
*/
|
||||
public void close() throws ItemStreamException {
|
||||
index = new MultiResourceIndex();
|
||||
delegate.close();
|
||||
noInput = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Figure out which resource to start with in case of restart, open the
|
||||
* delegate and restore delegate's position in the resource.
|
||||
*/
|
||||
public void open(ExecutionContext executionContext) throws ItemStreamException {
|
||||
|
||||
Assert.notNull(resources, "Resources must be set");
|
||||
|
||||
noInput = false;
|
||||
if (resources.length == 0) {
|
||||
logger.warn("No resources to read");
|
||||
noInput = true;
|
||||
return;
|
||||
}
|
||||
|
||||
Arrays.sort(resources, comparator);
|
||||
|
||||
index.open(executionContext);
|
||||
|
||||
delegate.setResource(resources[index.currentResource]);
|
||||
|
||||
delegate.open(new ExecutionContext());
|
||||
|
||||
try {
|
||||
for (int i = 0; i < index.currentItem; i++) {
|
||||
delegate.read();
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new ItemStreamException("Could not restore position on restart", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Store the current resource index and position in the resource.
|
||||
*/
|
||||
public void update(ExecutionContext executionContext) throws ItemStreamException {
|
||||
if (saveState) {
|
||||
index.update(executionContext);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param delegate reads items from single {@link Resource}.
|
||||
*/
|
||||
public void setDelegate(ResourceAwareItemReaderItemStream<? extends T> delegate) {
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the boolean indicating whether or not state should be saved in the
|
||||
* provided {@link ExecutionContext} during the {@link ItemStream} call to
|
||||
* update.
|
||||
*
|
||||
* @param saveState
|
||||
*/
|
||||
public void setSaveState(boolean saveState) {
|
||||
this.saveState = saveState;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param comparator used to order the injected resources, by default
|
||||
* compares {@link Resource#getFilename()} values.
|
||||
*/
|
||||
public void setComparator(Comparator<Resource> comparator) {
|
||||
this.comparator = comparator;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param resources input resources
|
||||
*/
|
||||
public void setResources(Resource[] resources) {
|
||||
this.resources = resources;
|
||||
}
|
||||
|
||||
/**
|
||||
* Facilitates keeping track of the position within multi-resource input.
|
||||
*/
|
||||
private class MultiResourceIndex {
|
||||
|
||||
private static final String RESOURCE_KEY = "resourceIndex";
|
||||
|
||||
private static final String ITEM_KEY = "itemIndex";
|
||||
|
||||
private int currentResource = 0;
|
||||
|
||||
private int markedResource = 0;
|
||||
|
||||
private int currentItem = 0;
|
||||
|
||||
private int markedItem = 0;
|
||||
|
||||
public void incrementItemCount() {
|
||||
currentItem++;
|
||||
}
|
||||
|
||||
public void incrementResourceCount() {
|
||||
currentResource++;
|
||||
currentItem = 0;
|
||||
}
|
||||
|
||||
public void mark() {
|
||||
markedResource = currentResource;
|
||||
markedItem = currentItem;
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
currentResource = markedResource;
|
||||
currentItem = markedItem;
|
||||
}
|
||||
|
||||
public void open(ExecutionContext ctx) {
|
||||
if (ctx.containsKey(executionContextUserSupport.getKey(RESOURCE_KEY))) {
|
||||
currentResource = ctx.getInt(executionContextUserSupport.getKey(RESOURCE_KEY));
|
||||
}
|
||||
|
||||
if (ctx.containsKey(executionContextUserSupport.getKey(ITEM_KEY))) {
|
||||
currentItem = ctx.getInt(executionContextUserSupport.getKey(ITEM_KEY));
|
||||
}
|
||||
}
|
||||
|
||||
public void update(ExecutionContext ctx) {
|
||||
ctx.putInt(executionContextUserSupport.getKey(RESOURCE_KEY), index.currentResource);
|
||||
ctx.putInt(executionContextUserSupport.getKey(ITEM_KEY), index.currentItem);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,141 +1,157 @@
|
||||
package org.springframework.batch.item.file;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.batch.item.ExecutionContext;
|
||||
import org.springframework.batch.item.ItemStream;
|
||||
import org.springframework.batch.item.ItemStreamException;
|
||||
import org.springframework.batch.item.ItemWriter;
|
||||
import org.springframework.batch.item.util.ExecutionContextUserSupport;
|
||||
import org.springframework.core.io.FileSystemResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* 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).
|
||||
*
|
||||
* @param <T> item type
|
||||
*
|
||||
* @author Robert Kasanicky
|
||||
*/
|
||||
public class MultiResourceItemWriter<T> extends ExecutionContextUserSupport implements ItemWriter<T>, ItemStream {
|
||||
|
||||
final static private String RESOURCE_INDEX_KEY = "resource.index";
|
||||
|
||||
final static private String CURRENT_RESOURCE_ITEM_COUNT = "resource.item.count";
|
||||
|
||||
private Resource resource;
|
||||
|
||||
private ResourceAwareItemWriterItemStream<? 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;
|
||||
|
||||
public MultiResourceItemWriter() {
|
||||
setName(ClassUtils.getShortName(MultiResourceItemWriter.class));
|
||||
}
|
||||
|
||||
public void write(List<? extends T> items) throws Exception {
|
||||
if (currentResourceItemCount >= itemCountLimitPerResource) {
|
||||
delegate.close();
|
||||
resourceIndex++;
|
||||
currentResourceItemCount = 0;
|
||||
setResourceToDelegate();
|
||||
delegate.open(new ExecutionContext());
|
||||
}
|
||||
delegate.write(items);
|
||||
currentResourceItemCount += items.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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(ResourceAwareItemWriterItemStream<? 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;
|
||||
}
|
||||
|
||||
public void close() throws ItemStreamException {
|
||||
resourceIndex = 1;
|
||||
currentResourceItemCount = 0;
|
||||
delegate.close();
|
||||
}
|
||||
|
||||
public void open(ExecutionContext executionContext) throws ItemStreamException {
|
||||
resourceIndex = executionContext.getInt(getKey(RESOURCE_INDEX_KEY), 1);
|
||||
currentResourceItemCount = executionContext.getInt(getKey(CURRENT_RESOURCE_ITEM_COUNT), 0);
|
||||
|
||||
try {
|
||||
setResourceToDelegate();
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new ItemStreamException("Couldn't open resource", e);
|
||||
}
|
||||
delegate.open(executionContext);
|
||||
}
|
||||
|
||||
public void update(ExecutionContext executionContext) throws ItemStreamException {
|
||||
if (saveState) {
|
||||
delegate.update(executionContext);
|
||||
executionContext.putInt(getKey(CURRENT_RESOURCE_ITEM_COUNT), currentResourceItemCount);
|
||||
executionContext.putInt(getKey(RESOURCE_INDEX_KEY), resourceIndex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create output resource (if necessary) and point the delegate to it.
|
||||
*/
|
||||
private void setResourceToDelegate() throws IOException {
|
||||
String path = resource.getFile().getAbsolutePath() + suffixCreator.getSuffix(resourceIndex);
|
||||
File file = new File(path);
|
||||
file.createNewFile();
|
||||
Assert.state(file.canWrite(), "Output resource " + path + " must be writable");
|
||||
delegate.setResource(new FileSystemResource(file));
|
||||
}
|
||||
}
|
||||
/*
|
||||
* 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.springframework.batch.item.file;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.batch.item.ExecutionContext;
|
||||
import org.springframework.batch.item.ItemStream;
|
||||
import org.springframework.batch.item.ItemStreamException;
|
||||
import org.springframework.batch.item.ItemWriter;
|
||||
import org.springframework.batch.item.util.ExecutionContextUserSupport;
|
||||
import org.springframework.core.io.FileSystemResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* 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).
|
||||
*
|
||||
* @param <T> item type
|
||||
*
|
||||
* @author Robert Kasanicky
|
||||
*/
|
||||
public class MultiResourceItemWriter<T> extends ExecutionContextUserSupport implements ItemWriter<T>, ItemStream {
|
||||
|
||||
final static private String RESOURCE_INDEX_KEY = "resource.index";
|
||||
|
||||
final static private String CURRENT_RESOURCE_ITEM_COUNT = "resource.item.count";
|
||||
|
||||
private Resource resource;
|
||||
|
||||
private ResourceAwareItemWriterItemStream<? 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;
|
||||
|
||||
public MultiResourceItemWriter() {
|
||||
setName(ClassUtils.getShortName(MultiResourceItemWriter.class));
|
||||
}
|
||||
|
||||
public void write(List<? extends T> items) throws Exception {
|
||||
if (currentResourceItemCount >= itemCountLimitPerResource) {
|
||||
delegate.close();
|
||||
resourceIndex++;
|
||||
currentResourceItemCount = 0;
|
||||
setResourceToDelegate();
|
||||
delegate.open(new ExecutionContext());
|
||||
}
|
||||
delegate.write(items);
|
||||
currentResourceItemCount += items.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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(ResourceAwareItemWriterItemStream<? 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;
|
||||
}
|
||||
|
||||
public void close() throws ItemStreamException {
|
||||
resourceIndex = 1;
|
||||
currentResourceItemCount = 0;
|
||||
delegate.close();
|
||||
}
|
||||
|
||||
public void open(ExecutionContext executionContext) throws ItemStreamException {
|
||||
resourceIndex = executionContext.getInt(getKey(RESOURCE_INDEX_KEY), 1);
|
||||
currentResourceItemCount = executionContext.getInt(getKey(CURRENT_RESOURCE_ITEM_COUNT), 0);
|
||||
|
||||
try {
|
||||
setResourceToDelegate();
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new ItemStreamException("Couldn't open resource", e);
|
||||
}
|
||||
delegate.open(executionContext);
|
||||
}
|
||||
|
||||
public void update(ExecutionContext executionContext) throws ItemStreamException {
|
||||
if (saveState) {
|
||||
delegate.update(executionContext);
|
||||
executionContext.putInt(getKey(CURRENT_RESOURCE_ITEM_COUNT), currentResourceItemCount);
|
||||
executionContext.putInt(getKey(RESOURCE_INDEX_KEY), resourceIndex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create output resource (if necessary) and point the delegate to it.
|
||||
*/
|
||||
private void setResourceToDelegate() throws IOException {
|
||||
String path = resource.getFile().getAbsolutePath() + suffixCreator.getSuffix(resourceIndex);
|
||||
File file = new File(path);
|
||||
file.createNewFile();
|
||||
Assert.state(file.canWrite(), "Output resource " + path + " must be writable");
|
||||
delegate.setResource(new FileSystemResource(file));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +1,32 @@
|
||||
package org.springframework.batch.item.file;
|
||||
|
||||
import org.springframework.batch.item.ItemReader;
|
||||
import org.springframework.batch.item.ItemStream;
|
||||
import org.springframework.core.io.Resource;
|
||||
|
||||
/**
|
||||
* Interface for {@link ItemReader}s that implement {@link ItemStream} and read
|
||||
* input from {@link Resource}.
|
||||
*
|
||||
* @author Robert Kasanicky
|
||||
*/
|
||||
public interface ResourceAwareItemReaderItemStream<T> extends ItemReader<T>, ItemStream {
|
||||
|
||||
void setResource(Resource resource);
|
||||
}
|
||||
/*
|
||||
* 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.springframework.batch.item.file;
|
||||
|
||||
import org.springframework.batch.item.ItemReader;
|
||||
import org.springframework.batch.item.ItemStream;
|
||||
import org.springframework.core.io.Resource;
|
||||
|
||||
/**
|
||||
* Interface for {@link ItemReader}s that implement {@link ItemStream} and read
|
||||
* input from {@link Resource}.
|
||||
*
|
||||
* @author Robert Kasanicky
|
||||
*/
|
||||
public interface ResourceAwareItemReaderItemStream<T> extends ItemReader<T>, ItemStream {
|
||||
|
||||
void setResource(Resource resource);
|
||||
}
|
||||
|
||||
@@ -1,16 +1,32 @@
|
||||
package org.springframework.batch.item.file;
|
||||
|
||||
import org.springframework.batch.item.ItemStream;
|
||||
import org.springframework.batch.item.ItemWriter;
|
||||
import org.springframework.core.io.Resource;
|
||||
|
||||
/**
|
||||
* Interface for {@link ItemWriter}s that implement {@link ItemStream} and write
|
||||
* output to {@link Resource}.
|
||||
*
|
||||
* @author Robert Kasanicky
|
||||
*/
|
||||
public interface ResourceAwareItemWriterItemStream<T> extends ItemStream, ItemWriter<T> {
|
||||
|
||||
void setResource(Resource resource);
|
||||
}
|
||||
/*
|
||||
* 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.springframework.batch.item.file;
|
||||
|
||||
import org.springframework.batch.item.ItemStream;
|
||||
import org.springframework.batch.item.ItemWriter;
|
||||
import org.springframework.core.io.Resource;
|
||||
|
||||
/**
|
||||
* Interface for {@link ItemWriter}s that implement {@link ItemStream} and write
|
||||
* output to {@link Resource}.
|
||||
*
|
||||
* @author Robert Kasanicky
|
||||
*/
|
||||
public interface ResourceAwareItemWriterItemStream<T> extends ItemStream, ItemWriter<T> {
|
||||
|
||||
void setResource(Resource resource);
|
||||
}
|
||||
|
||||
@@ -1,15 +1,31 @@
|
||||
package org.springframework.batch.item.file;
|
||||
|
||||
/**
|
||||
* Strategy interface for translating resource index into unique filename
|
||||
* suffix.
|
||||
*
|
||||
* @see MultiResourceItemWriter
|
||||
* @see SimpleResourceSuffixCreator
|
||||
*
|
||||
* @author Robert Kasanicky
|
||||
*/
|
||||
public interface ResourceSuffixCreator {
|
||||
|
||||
String getSuffix(int index);
|
||||
}
|
||||
/*
|
||||
* 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.springframework.batch.item.file;
|
||||
|
||||
/**
|
||||
* Strategy interface for translating resource index into unique filename
|
||||
* suffix.
|
||||
*
|
||||
* @see MultiResourceItemWriter
|
||||
* @see SimpleResourceSuffixCreator
|
||||
*
|
||||
* @author Robert Kasanicky
|
||||
*/
|
||||
public interface ResourceSuffixCreator {
|
||||
|
||||
String getSuffix(int index);
|
||||
}
|
||||
|
||||
@@ -1,15 +1,31 @@
|
||||
package org.springframework.batch.item.file;
|
||||
|
||||
/**
|
||||
* Trivial implementation of {@link ResourceSuffixCreator} that uses the index
|
||||
* itself as suffix, separated by dot.
|
||||
*
|
||||
* @author Robert Kasanicky
|
||||
*/
|
||||
public class SimpleResourceSuffixCreator implements ResourceSuffixCreator {
|
||||
|
||||
public String getSuffix(int index) {
|
||||
return "." + index;
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* 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.springframework.batch.item.file;
|
||||
|
||||
/**
|
||||
* Trivial implementation of {@link ResourceSuffixCreator} that uses the index
|
||||
* itself as suffix, separated by dot.
|
||||
*
|
||||
* @author Robert Kasanicky
|
||||
*/
|
||||
public class SimpleResourceSuffixCreator implements ResourceSuffixCreator {
|
||||
|
||||
public String getSuffix(int index) {
|
||||
return "." + index;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,39 +1,55 @@
|
||||
package org.springframework.batch.item.file.mapping;
|
||||
|
||||
import org.springframework.batch.item.file.transform.FieldSet;
|
||||
import org.springframework.batch.item.file.transform.LineTokenizer;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Two-phase {@link LineMapper} implementation consisting of tokenization of the
|
||||
* line into {@link FieldSet} followed by mapping to item.
|
||||
*
|
||||
* @author Robert Kasanicky
|
||||
*
|
||||
* @param <T> type of the item
|
||||
*/
|
||||
public class DefaultLineMapper<T> implements LineMapper<T>, InitializingBean {
|
||||
|
||||
private LineTokenizer tokenizer;
|
||||
|
||||
private FieldSetMapper<T> fieldSetMapper;
|
||||
|
||||
public T mapLine(String line, int lineNumber) throws Exception {
|
||||
return fieldSetMapper.mapFieldSet(tokenizer.tokenize(line));
|
||||
}
|
||||
|
||||
public void setLineTokenizer(LineTokenizer tokenizer) {
|
||||
this.tokenizer = tokenizer;
|
||||
}
|
||||
|
||||
public void setFieldSetMapper(FieldSetMapper<T> fieldSetMapper) {
|
||||
this.fieldSetMapper = fieldSetMapper;
|
||||
}
|
||||
|
||||
public void afterPropertiesSet() {
|
||||
Assert.notNull(tokenizer, "The LineTokenizer must be set");
|
||||
Assert.notNull(fieldSetMapper, "The FieldSetMapper must be set");
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* 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.springframework.batch.item.file.mapping;
|
||||
|
||||
import org.springframework.batch.item.file.transform.FieldSet;
|
||||
import org.springframework.batch.item.file.transform.LineTokenizer;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Two-phase {@link LineMapper} implementation consisting of tokenization of the
|
||||
* line into {@link FieldSet} followed by mapping to item.
|
||||
*
|
||||
* @author Robert Kasanicky
|
||||
*
|
||||
* @param <T> type of the item
|
||||
*/
|
||||
public class DefaultLineMapper<T> implements LineMapper<T>, InitializingBean {
|
||||
|
||||
private LineTokenizer tokenizer;
|
||||
|
||||
private FieldSetMapper<T> fieldSetMapper;
|
||||
|
||||
public T mapLine(String line, int lineNumber) throws Exception {
|
||||
return fieldSetMapper.mapFieldSet(tokenizer.tokenize(line));
|
||||
}
|
||||
|
||||
public void setLineTokenizer(LineTokenizer tokenizer) {
|
||||
this.tokenizer = tokenizer;
|
||||
}
|
||||
|
||||
public void setFieldSetMapper(FieldSetMapper<T> fieldSetMapper) {
|
||||
this.fieldSetMapper = fieldSetMapper;
|
||||
}
|
||||
|
||||
public void afterPropertiesSet() {
|
||||
Assert.notNull(tokenizer, "The LineTokenizer must be set");
|
||||
Assert.notNull(fieldSetMapper, "The FieldSetMapper must be set");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,31 +1,47 @@
|
||||
package org.springframework.batch.item.file.mapping;
|
||||
|
||||
import org.springframework.batch.item.file.transform.LineTokenizer;
|
||||
|
||||
|
||||
/**
|
||||
* Interface for mapping lines (strings) to domain objects typically used to map lines read from a file to domain objects
|
||||
* on a per line basis. Implementations of this interface perform the actual
|
||||
* work of parsing a line without having to deal with how the line was
|
||||
* obtained.
|
||||
*
|
||||
* @author Robert Kasanicky
|
||||
* @param <T> type of the domain object
|
||||
* @see FieldSetMapper
|
||||
* @see LineTokenizer
|
||||
* @since 2.0
|
||||
*/
|
||||
public interface LineMapper<T> {
|
||||
|
||||
/**
|
||||
* Implementations must implement this method to map the provided line to
|
||||
* the parameter type T. The line number represents the number of lines
|
||||
* into a file the current line resides.
|
||||
*
|
||||
* @param line to be mapped
|
||||
* @param lineNumber of the current line
|
||||
* @return mapped object of type T
|
||||
* @throws Exception if error occured while parsing.
|
||||
*/
|
||||
T mapLine(String line, int lineNumber) throws Exception;
|
||||
}
|
||||
/*
|
||||
* 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.springframework.batch.item.file.mapping;
|
||||
|
||||
import org.springframework.batch.item.file.transform.LineTokenizer;
|
||||
|
||||
|
||||
/**
|
||||
* Interface for mapping lines (strings) to domain objects typically used to map lines read from a file to domain objects
|
||||
* on a per line basis. Implementations of this interface perform the actual
|
||||
* work of parsing a line without having to deal with how the line was
|
||||
* obtained.
|
||||
*
|
||||
* @author Robert Kasanicky
|
||||
* @param <T> type of the domain object
|
||||
* @see FieldSetMapper
|
||||
* @see LineTokenizer
|
||||
* @since 2.0
|
||||
*/
|
||||
public interface LineMapper<T> {
|
||||
|
||||
/**
|
||||
* Implementations must implement this method to map the provided line to
|
||||
* the parameter type T. The line number represents the number of lines
|
||||
* into a file the current line resides.
|
||||
*
|
||||
* @param line to be mapped
|
||||
* @param lineNumber of the current line
|
||||
* @return mapped object of type T
|
||||
* @throws Exception if error occured while parsing.
|
||||
*/
|
||||
T mapLine(String line, int lineNumber) throws Exception;
|
||||
}
|
||||
|
||||
@@ -1,9 +1,25 @@
|
||||
package org.springframework.batch.item.file.mapping;
|
||||
|
||||
public class PassThroughLineMapper implements LineMapper<String>{
|
||||
|
||||
public String mapLine(String line, int lineNumber) throws Exception {
|
||||
return line;
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* 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.springframework.batch.item.file.mapping;
|
||||
|
||||
public class PassThroughLineMapper implements LineMapper<String>{
|
||||
|
||||
public String mapLine(String line, int lineNumber) throws Exception {
|
||||
return line;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,15 +1,31 @@
|
||||
package org.springframework.batch.item.file.transform;
|
||||
|
||||
|
||||
public class PassThroughLineAggregator<T> implements LineAggregator<T> {
|
||||
|
||||
/**
|
||||
* Simply convert to a String with toString().
|
||||
*
|
||||
* @see org.springframework.batch.item.file.transform.LineAggregator#aggregate(java.lang.Object)
|
||||
*/
|
||||
public String aggregate(T item) {
|
||||
return item.toString();
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* 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.springframework.batch.item.file.transform;
|
||||
|
||||
|
||||
public class PassThroughLineAggregator<T> implements LineAggregator<T> {
|
||||
|
||||
/**
|
||||
* Simply convert to a String with toString().
|
||||
*
|
||||
* @see org.springframework.batch.item.file.transform.LineAggregator#aggregate(java.lang.Object)
|
||||
*/
|
||||
public String aggregate(T item) {
|
||||
return item.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
* 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.springframework.batch.item.file.transform;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
* 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.springframework.batch.item.file.transform;
|
||||
|
||||
import java.beans.PropertyEditorSupport;
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
* 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.springframework.batch.item.file.transform;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
@@ -1,129 +1,145 @@
|
||||
package org.springframework.batch.item.support;
|
||||
|
||||
import org.springframework.batch.item.ExecutionContext;
|
||||
import org.springframework.batch.item.ItemReader;
|
||||
import org.springframework.batch.item.ItemStream;
|
||||
import org.springframework.batch.item.ItemStreamException;
|
||||
import org.springframework.batch.item.ParseException;
|
||||
import org.springframework.batch.item.UnexpectedInputException;
|
||||
import org.springframework.batch.item.util.ExecutionContextUserSupport;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Abstract superclass for {@link ItemReader}s that supports restart by storing
|
||||
* item count in the {@link ExecutionContext} (therefore requires item ordering
|
||||
* to be preserved between runs).
|
||||
*
|
||||
* Subclasses are inherently *not* thread-safe.
|
||||
*
|
||||
* @author Robert Kasanicky
|
||||
*/
|
||||
public abstract class AbstractItemCountingItemStreamItemReader<T> implements ItemReader<T>, ItemStream {
|
||||
|
||||
private static final String READ_COUNT = "read.count";
|
||||
|
||||
private int currentItemCount = 0;
|
||||
|
||||
private ExecutionContextUserSupport ecSupport = new ExecutionContextUserSupport();
|
||||
|
||||
private boolean saveState = true;
|
||||
|
||||
/**
|
||||
* Read next item from input.
|
||||
* @return item
|
||||
* @throws Exception
|
||||
*/
|
||||
protected abstract T doRead() throws Exception;
|
||||
|
||||
/**
|
||||
* Open resources necessary to start reading input.
|
||||
*/
|
||||
protected abstract void doOpen() throws Exception;
|
||||
|
||||
/**
|
||||
* Close the resources opened in {@link #doOpen()}.
|
||||
*/
|
||||
protected abstract void doClose() throws Exception;
|
||||
|
||||
/**
|
||||
* Move to the given item index. Subclasses should override this method if
|
||||
* there is a more efficient way of moving to given index than re-reading
|
||||
* the input using {@link #doRead()}.
|
||||
*/
|
||||
protected void jumpToItem(int itemIndex) throws Exception {
|
||||
for (int i = 0; i < itemIndex; i++) {
|
||||
doRead();
|
||||
}
|
||||
}
|
||||
|
||||
public T read() throws Exception, UnexpectedInputException, ParseException {
|
||||
currentItemCount++;
|
||||
return doRead();
|
||||
}
|
||||
|
||||
protected int getCurrentItemCount() {
|
||||
return currentItemCount;
|
||||
}
|
||||
|
||||
protected void setCurrentItemCount(int count) {
|
||||
this.currentItemCount = count;
|
||||
}
|
||||
|
||||
public void close() throws ItemStreamException {
|
||||
currentItemCount = 0;
|
||||
try {
|
||||
doClose();
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new ItemStreamException("Error while closing item reader", e);
|
||||
}
|
||||
}
|
||||
|
||||
public void open(ExecutionContext executionContext) throws ItemStreamException {
|
||||
|
||||
try {
|
||||
doOpen();
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new ItemStreamException("Failed to initialize the reader", e);
|
||||
}
|
||||
|
||||
if (executionContext.containsKey(ecSupport.getKey(READ_COUNT))) {
|
||||
int itemCount = executionContext.getInt(ecSupport.getKey(READ_COUNT));
|
||||
|
||||
try {
|
||||
jumpToItem(itemCount);
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new ItemStreamException("Could not move to stored position on restart", e);
|
||||
}
|
||||
|
||||
currentItemCount = itemCount;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void update(ExecutionContext executionContext) throws ItemStreamException {
|
||||
if (saveState) {
|
||||
Assert.notNull(executionContext, "ExecutionContext must not be null");
|
||||
executionContext.putInt(ecSupport.getKey(READ_COUNT), currentItemCount);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
ecSupport.setName(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the flag that determines whether to save internal data for
|
||||
* {@link ExecutionContext}. Only switch this to false if you don't want to
|
||||
* save any state from this stream, and you don't need it to be restartable.
|
||||
*
|
||||
* @param saveState flag value (default true).
|
||||
*/
|
||||
public void setSaveState(boolean saveState) {
|
||||
this.saveState = saveState;
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* 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.springframework.batch.item.support;
|
||||
|
||||
import org.springframework.batch.item.ExecutionContext;
|
||||
import org.springframework.batch.item.ItemReader;
|
||||
import org.springframework.batch.item.ItemStream;
|
||||
import org.springframework.batch.item.ItemStreamException;
|
||||
import org.springframework.batch.item.ParseException;
|
||||
import org.springframework.batch.item.UnexpectedInputException;
|
||||
import org.springframework.batch.item.util.ExecutionContextUserSupport;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Abstract superclass for {@link ItemReader}s that supports restart by storing
|
||||
* item count in the {@link ExecutionContext} (therefore requires item ordering
|
||||
* to be preserved between runs).
|
||||
*
|
||||
* Subclasses are inherently *not* thread-safe.
|
||||
*
|
||||
* @author Robert Kasanicky
|
||||
*/
|
||||
public abstract class AbstractItemCountingItemStreamItemReader<T> implements ItemReader<T>, ItemStream {
|
||||
|
||||
private static final String READ_COUNT = "read.count";
|
||||
|
||||
private int currentItemCount = 0;
|
||||
|
||||
private ExecutionContextUserSupport ecSupport = new ExecutionContextUserSupport();
|
||||
|
||||
private boolean saveState = true;
|
||||
|
||||
/**
|
||||
* Read next item from input.
|
||||
* @return item
|
||||
* @throws Exception
|
||||
*/
|
||||
protected abstract T doRead() throws Exception;
|
||||
|
||||
/**
|
||||
* Open resources necessary to start reading input.
|
||||
*/
|
||||
protected abstract void doOpen() throws Exception;
|
||||
|
||||
/**
|
||||
* Close the resources opened in {@link #doOpen()}.
|
||||
*/
|
||||
protected abstract void doClose() throws Exception;
|
||||
|
||||
/**
|
||||
* Move to the given item index. Subclasses should override this method if
|
||||
* there is a more efficient way of moving to given index than re-reading
|
||||
* the input using {@link #doRead()}.
|
||||
*/
|
||||
protected void jumpToItem(int itemIndex) throws Exception {
|
||||
for (int i = 0; i < itemIndex; i++) {
|
||||
doRead();
|
||||
}
|
||||
}
|
||||
|
||||
public T read() throws Exception, UnexpectedInputException, ParseException {
|
||||
currentItemCount++;
|
||||
return doRead();
|
||||
}
|
||||
|
||||
protected int getCurrentItemCount() {
|
||||
return currentItemCount;
|
||||
}
|
||||
|
||||
protected void setCurrentItemCount(int count) {
|
||||
this.currentItemCount = count;
|
||||
}
|
||||
|
||||
public void close() throws ItemStreamException {
|
||||
currentItemCount = 0;
|
||||
try {
|
||||
doClose();
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new ItemStreamException("Error while closing item reader", e);
|
||||
}
|
||||
}
|
||||
|
||||
public void open(ExecutionContext executionContext) throws ItemStreamException {
|
||||
|
||||
try {
|
||||
doOpen();
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new ItemStreamException("Failed to initialize the reader", e);
|
||||
}
|
||||
|
||||
if (executionContext.containsKey(ecSupport.getKey(READ_COUNT))) {
|
||||
int itemCount = executionContext.getInt(ecSupport.getKey(READ_COUNT));
|
||||
|
||||
try {
|
||||
jumpToItem(itemCount);
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new ItemStreamException("Could not move to stored position on restart", e);
|
||||
}
|
||||
|
||||
currentItemCount = itemCount;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void update(ExecutionContext executionContext) throws ItemStreamException {
|
||||
if (saveState) {
|
||||
Assert.notNull(executionContext, "ExecutionContext must not be null");
|
||||
executionContext.putInt(ecSupport.getKey(READ_COUNT), currentItemCount);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
ecSupport.setName(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the flag that determines whether to save internal data for
|
||||
* {@link ExecutionContext}. Only switch this to false if you don't want to
|
||||
* save any state from this stream, and you don't need it to be restartable.
|
||||
*
|
||||
* @param saveState flag value (default true).
|
||||
*/
|
||||
public void setSaveState(boolean saveState) {
|
||||
this.saveState = saveState;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
* 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.springframework.batch.item.support;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
* 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.springframework.batch.item.support;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
* 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.springframework.batch.item.support;
|
||||
|
||||
import org.springframework.batch.item.ItemProcessor;
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
* 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.springframework.batch.item.util;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
@@ -1,42 +1,58 @@
|
||||
package org.springframework.batch.item.validator;
|
||||
|
||||
import org.springframework.batch.item.ItemProcessor;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Simple implementation of {@link ItemProcessor} validates and input and
|
||||
* returns it without modifications.
|
||||
*
|
||||
* @author Robert Kasanicky
|
||||
*
|
||||
*/
|
||||
public class ValidatingItemProcessor<T> implements ItemProcessor<T, T> {
|
||||
|
||||
private Validator validator;
|
||||
|
||||
public ValidatingItemProcessor(Validator validator){
|
||||
Assert.notNull(validator, "Validator must not be null.");
|
||||
this.validator = validator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the validator used to validate each item.
|
||||
*
|
||||
* @param validator
|
||||
*/
|
||||
public void setValidator(Validator validator) {
|
||||
this.validator = validator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the item and return it unmodified
|
||||
*
|
||||
* @return the input item
|
||||
* @throws ValidationException if validation fails
|
||||
*/
|
||||
public T process(T item) throws ValidationException {
|
||||
validator.validate(item);
|
||||
return item;
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* 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.springframework.batch.item.validator;
|
||||
|
||||
import org.springframework.batch.item.ItemProcessor;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Simple implementation of {@link ItemProcessor} validates and input and
|
||||
* returns it without modifications.
|
||||
*
|
||||
* @author Robert Kasanicky
|
||||
*
|
||||
*/
|
||||
public class ValidatingItemProcessor<T> implements ItemProcessor<T, T> {
|
||||
|
||||
private Validator validator;
|
||||
|
||||
public ValidatingItemProcessor(Validator validator){
|
||||
Assert.notNull(validator, "Validator must not be null.");
|
||||
this.validator = validator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the validator used to validate each item.
|
||||
*
|
||||
* @param validator
|
||||
*/
|
||||
public void setValidator(Validator validator) {
|
||||
this.validator = validator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the item and return it unmodified
|
||||
*
|
||||
* @return the input item
|
||||
* @throws ValidationException if validation fails
|
||||
*/
|
||||
public T process(T item) throws ValidationException {
|
||||
validator.validate(item);
|
||||
return item;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
* 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.springframework.batch.item.xml;
|
||||
|
||||
import java.io.InputStream;
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
* 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.springframework.batch.item.xml;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user