diff --git a/.gradletasknamecache b/.gradletasknamecache
new file mode 100644
index 000000000..1ed9ef802
--- /dev/null
+++ b/.gradletasknamecache
@@ -0,0 +1,315 @@
+All
+assemble
+spring-batch-core:assemble
+spring-batch-core-tests:assemble
+spring-batch-infrastructure:assemble
+spring-batch-infrastructure-neo4j4:assemble
+spring-batch-infrastructure-tests:assemble
+spring-batch-integration:assemble
+spring-batch-samples:assemble
+spring-batch-test:assemble
+spring-build-src:assemble
+build
+spring-batch-core:build
+spring-batch-core-tests:build
+spring-batch-infrastructure:build
+spring-batch-infrastructure-neo4j4:build
+spring-batch-infrastructure-tests:build
+spring-batch-integration:build
+spring-batch-samples:build
+spring-batch-test:build
+spring-build-src:build
+spring-batch-core:buildDependents
+spring-batch-core-tests:buildDependents
+spring-batch-infrastructure:buildDependents
+spring-batch-infrastructure-neo4j4:buildDependents
+spring-batch-infrastructure-tests:buildDependents
+spring-batch-integration:buildDependents
+spring-batch-samples:buildDependents
+spring-batch-test:buildDependents
+spring-build-src:buildDependents
+spring-batch-core:buildNeeded
+spring-batch-core-tests:buildNeeded
+spring-batch-infrastructure:buildNeeded
+spring-batch-infrastructure-neo4j4:buildNeeded
+spring-batch-infrastructure-tests:buildNeeded
+spring-batch-integration:buildNeeded
+spring-batch-samples:buildNeeded
+spring-batch-test:buildNeeded
+spring-build-src:buildNeeded
+spring-batch-core:classes
+spring-batch-core:compileJava
+spring-batch-core:processResources
+spring-batch-core-tests:classes
+spring-batch-core-tests:compileJava
+spring-batch-core-tests:processResources
+spring-batch-infrastructure:classes
+spring-batch-infrastructure:compileJava
+spring-batch-infrastructure:processResources
+spring-batch-infrastructure-neo4j4:classes
+spring-batch-infrastructure-neo4j4:compileJava
+spring-batch-infrastructure-neo4j4:processResources
+spring-batch-infrastructure-tests:classes
+spring-batch-infrastructure-tests:compileJava
+spring-batch-infrastructure-tests:processResources
+spring-batch-integration:classes
+spring-batch-integration:compileJava
+spring-batch-integration:processResources
+spring-batch-samples:classes
+spring-batch-samples:compileJava
+spring-batch-samples:processResources
+spring-batch-test:classes
+spring-batch-test:compileJava
+spring-batch-test:processResources
+spring-build-src:classes
+spring-build-src:compileGroovy
+spring-build-src:compileJava
+spring-build-src:processResources
+spring-batch-core:generateSql
+spring-batch-core:jar
+spring-batch-core-tests:jar
+spring-batch-infrastructure:jar
+spring-batch-infrastructure-neo4j4:jar
+spring-batch-infrastructure-tests:jar
+spring-batch-integration:jar
+spring-batch-samples:jar
+spring-batch-test:jar
+spring-build-src:jar
+spring-batch-core:testClasses
+spring-batch-core:compileTestJava
+spring-batch-core:processTestResources
+spring-batch-core-tests:testClasses
+spring-batch-core-tests:compileTestJava
+spring-batch-core-tests:processTestResources
+spring-batch-infrastructure:testClasses
+spring-batch-infrastructure:compileTestJava
+spring-batch-infrastructure:processTestResources
+spring-batch-infrastructure-neo4j4:testClasses
+spring-batch-infrastructure-neo4j4:compileTestJava
+spring-batch-infrastructure-neo4j4:processTestResources
+spring-batch-infrastructure-tests:testClasses
+spring-batch-infrastructure-tests:compileTestJava
+spring-batch-infrastructure-tests:processTestResources
+spring-batch-integration:testClasses
+spring-batch-integration:compileTestJava
+spring-batch-integration:processTestResources
+spring-batch-samples:testClasses
+spring-batch-samples:compileTestJava
+spring-batch-samples:processTestResources
+spring-batch-test:testClasses
+spring-batch-test:compileTestJava
+spring-batch-test:processTestResources
+spring-build-src:testClasses
+spring-build-src:compileTestGroovy
+spring-build-src:compileTestJava
+spring-build-src:processTestResources
+init
+depsZip
+dist
+distZip
+docsZip
+schemaZip
+api
+spring-build-src:groovydoc
+spring-batch-core:javadoc
+spring-batch-core-tests:javadoc
+spring-batch-infrastructure:javadoc
+spring-batch-infrastructure-neo4j4:javadoc
+spring-batch-infrastructure-tests:javadoc
+spring-batch-integration:javadoc
+spring-batch-samples:javadoc
+spring-batch-test:javadoc
+spring-build-src:javadoc
+reference
+referenceEpub
+referenceHtmlMulti
+referenceHtmlSingle
+referencePdf
+components
+spring-batch-core:components
+spring-batch-core-tests:components
+spring-batch-infrastructure:components
+spring-batch-infrastructure-neo4j4:components
+spring-batch-infrastructure-tests:components
+spring-batch-integration:components
+spring-batch-samples:components
+spring-batch-test:components
+spring-build-src:components
+dependencies
+spring-batch-core:dependencies
+spring-batch-core-tests:dependencies
+spring-batch-infrastructure:dependencies
+spring-batch-infrastructure-neo4j4:dependencies
+spring-batch-infrastructure-tests:dependencies
+spring-batch-integration:dependencies
+spring-batch-samples:dependencies
+spring-batch-test:dependencies
+spring-build-src:dependencies
+dependencyInsight
+spring-batch-core:dependencyInsight
+spring-batch-core-tests:dependencyInsight
+spring-batch-infrastructure:dependencyInsight
+spring-batch-infrastructure-neo4j4:dependencyInsight
+spring-batch-infrastructure-tests:dependencyInsight
+spring-batch-integration:dependencyInsight
+spring-batch-samples:dependencyInsight
+spring-batch-test:dependencyInsight
+spring-build-src:dependencyInsight
+help
+spring-batch-core:help
+spring-batch-core-tests:help
+spring-batch-infrastructure:help
+spring-batch-infrastructure-neo4j4:help
+spring-batch-infrastructure-tests:help
+spring-batch-integration:help
+spring-batch-samples:help
+spring-batch-test:help
+spring-build-src:help
+model
+spring-batch-core:model
+spring-batch-core-tests:model
+spring-batch-infrastructure:model
+spring-batch-infrastructure-neo4j4:model
+spring-batch-infrastructure-tests:model
+spring-batch-integration:model
+spring-batch-samples:model
+spring-batch-test:model
+spring-build-src:model
+projects
+spring-batch-core:projects
+spring-batch-core-tests:projects
+spring-batch-infrastructure:projects
+spring-batch-infrastructure-neo4j4:projects
+spring-batch-infrastructure-tests:projects
+spring-batch-integration:projects
+spring-batch-samples:projects
+spring-batch-test:projects
+spring-build-src:projects
+properties
+spring-batch-core:properties
+spring-batch-core-tests:properties
+spring-batch-infrastructure:properties
+spring-batch-infrastructure-neo4j4:properties
+spring-batch-infrastructure-tests:properties
+spring-batch-integration:properties
+spring-batch-samples:properties
+spring-batch-test:properties
+spring-build-src:properties
+tasks
+spring-batch-core:tasks
+spring-batch-core-tests:tasks
+spring-batch-infrastructure:tasks
+spring-batch-infrastructure-neo4j4:tasks
+spring-batch-infrastructure-tests:tasks
+spring-batch-integration:tasks
+spring-batch-samples:tasks
+spring-batch-test:tasks
+spring-build-src:tasks
+spring-batch-core:cleanEclipse
+spring-batch-core-tests:cleanEclipse
+spring-batch-infrastructure:cleanEclipse
+spring-batch-infrastructure-neo4j4:cleanEclipse
+spring-batch-infrastructure-tests:cleanEclipse
+spring-batch-integration:cleanEclipse
+spring-batch-samples:cleanEclipse
+spring-batch-test:cleanEclipse
+cleanIdea
+spring-batch-core:cleanIdea
+spring-batch-core-tests:cleanIdea
+spring-batch-infrastructure:cleanIdea
+spring-batch-infrastructure-neo4j4:cleanIdea
+spring-batch-infrastructure-tests:cleanIdea
+spring-batch-integration:cleanIdea
+spring-batch-samples:cleanIdea
+spring-batch-test:cleanIdea
+spring-batch-core:eclipse
+spring-batch-core:eclipseClasspath
+spring-batch-core:eclipseJdt
+spring-batch-core:eclipseProject
+spring-batch-core-tests:eclipse
+spring-batch-core-tests:eclipseClasspath
+spring-batch-core-tests:eclipseJdt
+spring-batch-core-tests:eclipseProject
+spring-batch-infrastructure:eclipse
+spring-batch-infrastructure:eclipseClasspath
+spring-batch-infrastructure:eclipseJdt
+spring-batch-infrastructure:eclipseProject
+spring-batch-infrastructure-neo4j4:eclipse
+spring-batch-infrastructure-neo4j4:eclipseClasspath
+spring-batch-infrastructure-neo4j4:eclipseJdt
+spring-batch-infrastructure-neo4j4:eclipseProject
+spring-batch-infrastructure-tests:eclipse
+spring-batch-infrastructure-tests:eclipseClasspath
+spring-batch-infrastructure-tests:eclipseJdt
+spring-batch-infrastructure-tests:eclipseProject
+spring-batch-integration:eclipse
+spring-batch-integration:eclipseClasspath
+spring-batch-integration:eclipseJdt
+spring-batch-integration:eclipseProject
+spring-batch-samples:eclipse
+spring-batch-samples:eclipseClasspath
+spring-batch-samples:eclipseJdt
+spring-batch-samples:eclipseProject
+spring-batch-test:eclipse
+spring-batch-test:eclipseClasspath
+spring-batch-test:eclipseJdt
+spring-batch-test:eclipseProject
+idea
+ideaModule
+ideaProject
+ideaWorkspace
+spring-batch-core:idea
+spring-batch-core:ideaModule
+spring-batch-core-tests:idea
+spring-batch-core-tests:ideaModule
+spring-batch-infrastructure:idea
+spring-batch-infrastructure:ideaModule
+spring-batch-infrastructure-neo4j4:idea
+spring-batch-infrastructure-neo4j4:ideaModule
+spring-batch-infrastructure-tests:idea
+spring-batch-infrastructure-tests:ideaModule
+spring-batch-integration:idea
+spring-batch-integration:ideaModule
+spring-batch-samples:idea
+spring-batch-samples:ideaModule
+spring-batch-test:idea
+spring-batch-test:ideaModule
+check
+spring-batch-core:check
+spring-batch-core-tests:check
+spring-batch-infrastructure:check
+spring-batch-infrastructure-neo4j4:check
+spring-batch-infrastructure-tests:check
+spring-batch-integration:check
+spring-batch-samples:check
+spring-batch-test:check
+spring-build-src:check
+clean
+spring-batch-core:clean
+spring-batch-core-tests:clean
+spring-batch-infrastructure:clean
+spring-batch-infrastructure-neo4j4:clean
+spring-batch-infrastructure-tests:clean
+spring-batch-integration:clean
+spring-batch-samples:clean
+spring-batch-test:clean
+spring-build-src:clean
+spring-batch-core:test
+spring-batch-core-tests:test
+spring-batch-infrastructure:test
+spring-batch-infrastructure-neo4j4:test
+spring-batch-infrastructure-tests:test
+spring-batch-integration:test
+spring-batch-samples:test
+spring-batch-test:test
+spring-build-src:test
+spring-batch-core:install
+spring-batch-core-tests:install
+spring-batch-infrastructure:install
+spring-batch-infrastructure-neo4j4:install
+spring-batch-infrastructure-tests:install
+spring-batch-integration:install
+spring-batch-samples:install
+spring-batch-test:install
+sonarRunner
+wrapper
diff --git a/build.gradle b/build.gradle
index df1b71eda..ca403009e 100644
--- a/build.gradle
+++ b/build.gradle
@@ -8,8 +8,8 @@ buildscript {
maven { url 'https://repo.spring.io/plugins-release' }
}
dependencies {
- classpath 'org.springframework.build.gradle:docbook-reference-plugin:0.2.8'
- classpath 'org.springframework.build.gradle:propdeps-plugin:0.0.5'
+ classpath 'io.spring.gradle:docbook-reference-plugin:0.3.1'
+ classpath 'org.springframework.build.gradle:propdeps-plugin:0.0.7'
classpath 'io.spring.gradle:spring-io-plugin:0.0.5.RELEASE'
}
}
@@ -50,6 +50,7 @@ allprojects {
springDataJpaVersion = '1.6.0.RELEASE'
springDataMongodbVersion = '1.5.0.RELEASE'
springDataNeo4jVersion = '3.1.0.RELEASE'
+ springDataNeo4j4Version = '4.1.1.RELEASE'
springIntegrationVersion = '4.0.1.RELEASE'
springLdapVersion = '2.0.2.RELEASE'
@@ -62,7 +63,7 @@ allprojects {
commonsLangVersion = '2.6'
derbyVersion = '10.10.1.1'
groovyVersion = '2.3.0'
- hamcrestVersion = '1.3'
+ hamcrestVersion = '1.3'
h2databaseVersion = '1.3.175'
hibernateVersion = '4.2.12.Final'
hibernateValidatorVersion = '4.3.1.Final'
@@ -94,13 +95,14 @@ allprojects {
}
}
-subprojects { subproject ->
+configure(subprojects - project(":spring-build-src")) { subproject ->
apply plugin: 'java'
apply from: "${rootProject.projectDir}/publish-maven.gradle"
apply plugin: 'jacoco'
apply plugin: 'propdeps-idea'
apply plugin: 'propdeps-eclipse'
+ apply plugin: 'merge'
jacoco {
toolVersion = "0.7.0.201403182114"
@@ -231,6 +233,18 @@ configure(mainProjects) {
}
}
+project("spring-build-src") {
+ description = "Exposes gradle buildSrc for IDE support"
+ apply plugin: "groovy"
+
+ dependencies {
+ compile gradleApi()
+ compile localGroovy()
+ }
+
+ configurations.archives.artifacts.clear()
+}
+
project('spring-batch-core') {
description = 'Spring Batch Core'
@@ -263,7 +277,7 @@ project('spring-batch-core') {
optional "org.slf4j:slf4j-log4j12:$slf4jVersion"
optional "log4j:log4j:$log4jVersion"
- provided "javax.batch:javax.batch-api:$javaxBatchApiVersion"
+ optional "javax.batch:javax.batch-api:$javaxBatchApiVersion"
}
}
@@ -349,6 +363,17 @@ project('spring-batch-infrastructure') {
}
}
+project('spring-batch-infrastructure-neo4j4') {
+
+ merge.into = project(':spring-batch-infrastructure')
+
+ dependencies {
+ optional "org.springframework.data:spring-data-neo4j:$springDataNeo4j4Version"
+ testCompile "org.mockito:mockito-core:$mockitoVersion"
+ testCompile "junit:junit:${junitVersion}"
+ }
+}
+
project('spring-batch-core-tests') {
description = 'Spring Batch Core Tests'
project.tasks.findByPath("artifactoryPublish")?.enabled = false
@@ -504,6 +529,8 @@ project('spring-batch-integration') {
optional "log4j:log4j:1.2.14"
optional "org.springframework.integration:spring-integration-jms:$springIntegrationVersion"
optional "org.springframework:spring-jms:$springVersion"
+
+ optional "javax.batch:javax.batch-api:$javaxBatchApiVersion"
}
}
@@ -573,13 +600,13 @@ project('spring-batch-samples') {
optional "org.springframework.amqp:spring-rabbit:$springAmqpVersion"
optional "javax.inject:javax.inject:1"
+ optional "javax.batch:javax.batch-api:$javaxBatchApiVersion"
}
}
apply plugin: 'docbook-reference'
reference {
- //sourceDir = file('src/reference/docbook')
sourceDir = file('src/site/docbook/reference')
}
@@ -793,5 +820,5 @@ or specify the Gradle property `TCK_HOME`, e.g: ./gradlew runTck -PTCK_HOME=/pat
task wrapper(type: Wrapper) {
description = 'Generates gradlew[.bat] scripts'
- gradleVersion = '1.11'
+ gradleVersion = '2.12'
}
diff --git a/buildSrc/src/main/groovy/org/springframework/build/gradle/MergePlugin.groovy b/buildSrc/src/main/groovy/org/springframework/build/gradle/MergePlugin.groovy
new file mode 100644
index 000000000..1933dd0fb
--- /dev/null
+++ b/buildSrc/src/main/groovy/org/springframework/build/gradle/MergePlugin.groovy
@@ -0,0 +1,159 @@
+/*
+ * Copyright 2002-2015 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.build.gradle
+
+import org.gradle.api.Project
+import org.gradle.api.Action
+import org.gradle.api.Plugin
+import org.gradle.api.invocation.Gradle
+import org.gradle.api.artifacts.Configuration
+import org.gradle.api.artifacts.ProjectDependency
+import org.gradle.api.artifacts.maven.Conf2ScopeMapping
+import org.gradle.api.plugins.MavenPlugin
+import org.gradle.plugins.ide.eclipse.EclipsePlugin
+import org.gradle.plugins.ide.idea.IdeaPlugin
+/**
+ * Gradle plugin that allows projects to merged together. Primarily developed to
+ * allow Spring to support multiple incompatible versions of third-party
+ * dependencies (for example Hibernate v3 and v4).
+ *
+ * The 'merge' extension should be used to define how projects are merged, for example:
+ *
+ * configure(subprojects) {
+ * apply plugin: MergePlugin
+ * }
+ *
+ * project("myproject") {
+ * }
+ *
+ * project("myproject-extra") {
+ * merge.into = project("myproject")
+ * }
+ *
+ *
+ * This plugin adds two new configurations:
+ *
+ * - merging - Contains the projects being merged into this project
-
+ *
- runtimeMerge - Contains all dependencies that are merge projects. These are used
+ * to allow an IDE to reference merge projects.
+ *
+ *
+ * @author Rob Winch
+ * @author Phillip Webb
+ */
+class MergePlugin implements Plugin {
+
+ private static boolean attachedProjectsEvaluated;
+
+ public void apply(Project project) {
+ project.plugins.apply(MavenPlugin)
+ project.plugins.apply(EclipsePlugin)
+ project.plugins.apply(IdeaPlugin)
+
+ MergeModel model = project.extensions.create("merge", MergeModel)
+ project.configurations.create("merging")
+ Configuration runtimeMerge = project.configurations.create("runtimeMerge")
+
+ // Ensure the IDE can reference merged projects
+ project.eclipse.classpath.plusConfigurations += [ runtimeMerge ]
+ project.idea.module.scopes.PROVIDED.plus += [ runtimeMerge ]
+
+ // Hook to perform the actual merge logic
+ project.afterEvaluate{
+ if (it.merge.into != null) {
+ setup(it)
+ }
+ }
+
+ // Hook to build runtimeMerge dependencies
+ if (!attachedProjectsEvaluated) {
+ project.gradle.projectsEvaluated{
+ postProcessProjects(it)
+ }
+ attachedProjectsEvaluated = true;
+ }
+ }
+
+ private void setup(Project project) {
+ project.merge.into.dependencies.add("merging", project)
+ project.dependencies.add("provided", project.merge.into.sourceSets.main.output)
+ project.dependencies.add("runtimeMerge", project.merge.into)
+ setupTaskDependencies(project)
+ setupMaven(project)
+ }
+
+ private void setupTaskDependencies(Project project) {
+ // invoking a task will invoke the task with the same name on 'into' project
+ ["sourcesJar", "jar", "javadocJar", "javadoc", "install", "artifactoryPublish"].each {
+ def task = project.tasks.findByPath(it)
+ if (task) {
+ task.enabled = false
+ task.dependsOn(project.merge.into.tasks.findByPath(it))
+ }
+ }
+
+ // update 'into' project artifacts to contain the source artifact contents
+ project.merge.into.sourcesJar.from(project.sourcesJar.source)
+ project.merge.into.jar.from(project.sourceSets.main.output)
+ project.merge.into.javadoc {
+ source += project.javadoc.source
+ classpath += project.javadoc.classpath
+ }
+ }
+
+ private void setupMaven(Project project) {
+ project.configurations.each { configuration ->
+ Conf2ScopeMapping mapping = project.conf2ScopeMappings.getMapping([configuration])
+ if (mapping.scope) {
+ Configuration intoConfiguration = project.merge.into.configurations.create(
+ project.name + "-" + configuration.name)
+ configuration.excludeRules.each {
+ configuration.exclude([
+ (ExcludeRule.GROUP_KEY) : it.group,
+ (ExcludeRule.MODULE_KEY) : it.module])
+ }
+ configuration.dependencies.each {
+ def intoCompile = project.merge.into.configurations.getByName("compile")
+ // Protect against changing a compile scope dependency (SPR-10218)
+ if (!intoCompile.dependencies.contains(it)) {
+ intoConfiguration.dependencies.add(it)
+ }
+ }
+ def index = project.parent.childProjects.findIndexOf {p -> p.getValue() == project}
+ project.merge.into.install.repositories.mavenInstaller.pom.scopeMappings.addMapping(
+ mapping.priority + 100 + index, intoConfiguration, mapping.scope)
+ }
+ }
+ }
+
+ private postProcessProjects(Gradle gradle) {
+ gradle.rootProject.subprojects(new Action() {
+ public void execute(Project project) {
+ project.configurations.getByName("runtime").allDependencies.withType(ProjectDependency).each{
+ Configuration dependsOnMergedFrom = it.dependencyProject.configurations.getByName("merging");
+ dependsOnMergedFrom.dependencies.each{ dep ->
+ project.dependencies.add("runtimeMerge", dep.dependencyProject)
+ }
+ }
+ }
+ });
+ }
+}
+
+class MergeModel {
+ Project into;
+}
\ No newline at end of file
diff --git a/buildSrc/src/main/resources/META-INF/gradle-plugins/merge.properties b/buildSrc/src/main/resources/META-INF/gradle-plugins/merge.properties
new file mode 100644
index 000000000..9cef80416
--- /dev/null
+++ b/buildSrc/src/main/resources/META-INF/gradle-plugins/merge.properties
@@ -0,0 +1 @@
+implementation-class=org.springframework.build.gradle.MergePlugin
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
index 583859812..b5166dad4 100644
Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index f5ee6ed8d..f72fbfa23 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
-#Thu May 08 10:59:52 CDT 2014
+#Mon Apr 18 18:28:38 CDT 2016
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=http\://services.gradle.org/distributions/gradle-1.11-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.12-bin.zip
diff --git a/settings.gradle b/settings.gradle
index 114e2ac7f..927693315 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -3,7 +3,11 @@ rootProject.name = 'spring-batch'
include 'spring-batch-core'
include 'spring-batch-core-tests'
include 'spring-batch-infrastructure'
+include 'spring-batch-infrastructure-neo4j4'
include 'spring-batch-infrastructure-tests'
include 'spring-batch-test'
include 'spring-batch-integration'
include 'spring-batch-samples'
+
+include "buildSrc"
+rootProject.children.find{ it.name == "buildSrc" }.name = "spring-build-src"
diff --git a/spring-batch-infrastructure-neo4j4/src/main/java/org/springframework/batch/item/data/Neo4j4ItemReader.java b/spring-batch-infrastructure-neo4j4/src/main/java/org/springframework/batch/item/data/Neo4j4ItemReader.java
new file mode 100644
index 000000000..0805bdb02
--- /dev/null
+++ b/spring-batch-infrastructure-neo4j4/src/main/java/org/springframework/batch/item/data/Neo4j4ItemReader.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2016 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.data;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+
+/**
+ *
+ * Extensions of the {@link AbstractNeo4jItemReader} intended for use with versions of
+ * Spring Data Neo4J > 4. Conversions of the results are done using logic based on the
+ * target type.
+ *
+ *
+ * @author Michael Minella
+ * @author Vince Bickers
+ * @since 3.0.7
+ */
+public class Neo4j4ItemReader extends AbstractNeo4jItemReader {
+
+ @Override
+ protected Iterator doPageRead() {
+ Iterable queryResults = getTemplate().queryForObjects(
+ getTargetType(), generateLimitCypherQuery(), getParameterValues());
+
+ if(queryResults != null) {
+ return queryResults.iterator();
+ }
+ else {
+ return new ArrayList().iterator();
+ }
+ }
+}
diff --git a/spring-batch-infrastructure-neo4j4/src/test/java/org/springframework/batch/item/data/Neo4j4ItemReaderTests.java b/spring-batch-infrastructure-neo4j4/src/test/java/org/springframework/batch/item/data/Neo4j4ItemReaderTests.java
new file mode 100644
index 000000000..917f0a494
--- /dev/null
+++ b/spring-batch-infrastructure-neo4j4/src/test/java/org/springframework/batch/item/data/Neo4j4ItemReaderTests.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright 2013-2014 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.data;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import org.springframework.data.neo4j.template.Neo4jOperations;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Matchers.isNull;
+import static org.mockito.Mockito.when;
+
+public class Neo4j4ItemReaderTests {
+
+ private Neo4j4ItemReader reader;
+ @Mock
+ private Neo4jOperations template;
+ @Mock
+ private Iterable result;
+
+ @Before
+ public void setUp() throws Exception {
+ reader = new Neo4j4ItemReader();
+
+ MockitoAnnotations.initMocks(this);
+
+ reader.setTemplate(template);
+ reader.setTargetType(String.class);
+ reader.setStartStatement("n=node(*)");
+ reader.setReturnStatement("*");
+ reader.setOrderByStatement("n.age");
+ reader.setPageSize(50);
+ reader.afterPropertiesSet();
+ }
+
+ @Test
+ public void testAfterPropertiesSet() throws Exception {
+ reader = new Neo4j4ItemReader();
+
+ try {
+ reader.afterPropertiesSet();
+ fail("Template was not set but exception was not thrown.");
+ } catch (IllegalStateException iae) {
+ assertEquals("A Neo4JOperations implementation is required", iae.getMessage());
+ } catch (Throwable t) {
+ fail("Wrong exception was thrown:" + t);
+ }
+
+ reader.setTemplate(template);
+
+ try {
+ reader.afterPropertiesSet();
+ fail("type was not set but exception was not thrown.");
+ } catch (IllegalStateException iae) {
+ assertEquals("The type to be returned is required", iae.getMessage());
+ } catch (Throwable t) {
+ fail("Wrong exception was thrown:" + t);
+ }
+
+ reader.setTargetType(String.class);
+
+ try {
+ reader.afterPropertiesSet();
+ fail("START was not set but exception was not thrown.");
+ } catch (IllegalStateException iae) {
+ assertEquals("A START statement is required", iae.getMessage());
+ } catch (Throwable t) {
+ fail("Wrong exception was thrown:" + t);
+ }
+
+ reader.setStartStatement("n=node(*)");
+
+ try {
+ reader.afterPropertiesSet();
+ fail("RETURN was not set but exception was not thrown.");
+ } catch (IllegalStateException iae) {
+ assertEquals("A RETURN statement is required", iae.getMessage());
+ } catch (Throwable t) {
+ fail("Wrong exception was thrown:" + t);
+ }
+
+ reader.setReturnStatement("n.name, n.phone");
+
+ try {
+ reader.afterPropertiesSet();
+ fail("ORDER BY was not set but exception was not thrown.");
+ } catch (IllegalStateException iae) {
+ assertEquals("A ORDER BY statement is required", iae.getMessage());
+ } catch (Throwable t) {
+ fail("Wrong exception was thrown:" + t);
+ }
+
+ reader.setOrderByStatement("n.age");
+
+ reader.afterPropertiesSet();
+ }
+
+ @SuppressWarnings("unchecked")
+ @Test
+ public void testNullResults() {
+ ArgumentCaptor query = ArgumentCaptor.forClass(String.class);
+
+ when(template.queryForObjects(eq(String.class), query.capture(), (Map) isNull())).thenReturn(null);
+
+ assertFalse(reader.doPageRead().hasNext());
+ assertEquals("START n=node(*) RETURN * ORDER BY n.age SKIP 0 LIMIT 50", query.getValue());
+ }
+
+ @SuppressWarnings("unchecked")
+ @Test
+ public void testNoResults() {
+ ArgumentCaptor query = ArgumentCaptor.forClass(String.class);
+
+ when(template.queryForObjects(eq(String.class), query.capture(), (Map) isNull())).thenReturn(result);
+ when(result.iterator()).thenReturn(Collections.emptyIterator());
+
+ assertFalse(reader.doPageRead().hasNext());
+ assertEquals("START n=node(*) RETURN * ORDER BY n.age SKIP 0 LIMIT 50", query.getValue());
+ }
+
+ @SuppressWarnings("serial")
+ @Test
+ public void testResultsWithMatchAndWhere() throws Exception {
+ reader.setMatchStatement("n -- m");
+ reader.setWhereStatement("has(n.name)");
+ reader.setReturnStatement("m");
+ reader.afterPropertiesSet();
+ when(template.queryForObjects(String.class, "START n=node(*) MATCH n -- m WHERE has(n.name) RETURN m ORDER BY n.age SKIP 0 LIMIT 50", null)).thenReturn(result);
+ when(result.iterator()).thenReturn(Arrays.asList("foo", "bar", "baz").iterator());
+
+ assertTrue(reader.doPageRead().hasNext());
+ }
+
+ @SuppressWarnings("serial")
+ @Test
+ public void testResultsWithMatchAndWhereWithParameters() throws Exception {
+ Map params = new HashMap();
+ params.put("foo", "bar");
+ reader.setParameterValues(params);
+ reader.setMatchStatement("n -- m");
+ reader.setWhereStatement("has(n.name)");
+ reader.setReturnStatement("m");
+ reader.afterPropertiesSet();
+ when(template.queryForObjects(String.class, "START n=node(*) MATCH n -- m WHERE has(n.name) RETURN m ORDER BY n.age SKIP 0 LIMIT 50", params)).thenReturn(result);
+ when(result.iterator()).thenReturn(Arrays.asList("foo", "bar", "baz").iterator());
+
+ assertTrue(reader.doPageRead().hasNext());
+ }
+}
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/data/AbstractNeo4jItemReader.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/data/AbstractNeo4jItemReader.java
new file mode 100644
index 000000000..2724a7094
--- /dev/null
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/data/AbstractNeo4jItemReader.java
@@ -0,0 +1,206 @@
+/*
+ * Copyright 2012-2016 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.data;
+
+import java.util.Map;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.springframework.batch.item.ItemReader;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.data.neo4j.template.Neo4jOperations;
+import org.springframework.util.Assert;
+import org.springframework.util.StringUtils;
+
+/**
+ *
+ * Restartable {@link ItemReader} that reads objects from the graph database Neo4j
+ * via a paging technique.
+ *
+ *
+ *
+ * It executes cypher queries built from the statement fragments provided to
+ * retrieve the requested data. The query is executed using paged requests of
+ * a size specified in {@link #setPageSize(int)}. Additional pages are requested
+ * as needed when the {@link #read()} method is called. On restart, the reader
+ * will begin again at the same number item it left off at.
+ *
+ *
+ *
+ * Performance is dependent on your Neo4J configuration (embedded or remote) as
+ * well as page size. Setting a fairly large page size and using a commit
+ * interval that matches the page size should provide better performance.
+ *
+ *
+ *
+ * This implementation is thread-safe between calls to
+ * {@link #open(org.springframework.batch.item.ExecutionContext)}, however you
+ * should set saveState=false if used in a multi-threaded
+ * environment (no restart available).
+ *
+ *
+ * @author Michael Minella
+ * @since 3.07
+ */
+public abstract class AbstractNeo4jItemReader extends
+ AbstractPaginatedDataItemReader implements InitializingBean {
+
+ protected Log logger = LogFactory.getLog(getClass());
+
+ private Neo4jOperations template;
+
+ private String startStatement;
+ private String returnStatement;
+ private String matchStatement;
+ private String whereStatement;
+ private String orderByStatement;
+
+ private Class targetType;
+
+ private Map parameterValues;
+
+ /**
+ * Optional parameters to be used in the cypher query.
+ *
+ * @param parameterValues the parameter values to be used in the cypher query
+ */
+ public void setParameterValues(Map parameterValues) {
+ this.parameterValues = parameterValues;
+ }
+
+ protected final Map getParameterValues() {
+ return this.parameterValues;
+ }
+
+ /**
+ * The start segment of the cypher query. START is prepended
+ * to the statement provided and should not be
+ * included.
+ *
+ * @param startStatement the start fragment of the cypher query.
+ */
+ public void setStartStatement(String startStatement) {
+ this.startStatement = startStatement;
+ }
+
+ /**
+ * The return statement of the cypher query. RETURN is prepended
+ * to the statement provided and should not be
+ * included
+ *
+ * @param returnStatement the return fragment of the cypher query.
+ */
+ public void setReturnStatement(String returnStatement) {
+ this.returnStatement = returnStatement;
+ }
+
+ /**
+ * An optional match fragment of the cypher query. MATCH is
+ * prepended to the statement provided and should not
+ * be included.
+ *
+ * @param matchStatement the match fragment of the cypher query
+ */
+ public void setMatchStatement(String matchStatement) {
+ this.matchStatement = matchStatement;
+ }
+
+ /**
+ * An optional where fragment of the cypher query. WHERE is
+ * prepended to the statement provided and should not
+ * be included.
+ *
+ * @param whereStatement where fragment of the cypher query
+ */
+ public void setWhereStatement(String whereStatement) {
+ this.whereStatement = whereStatement;
+ }
+
+ /**
+ * A list of properties to order the results by. This is
+ * required so that subsequent page requests pull back the
+ * segment of results correctly. ORDER BY is prepended to
+ * the statement provided and should not be included.
+ *
+ * @param orderByStatement order by fragment of the cypher query.
+ */
+ public void setOrderByStatement(String orderByStatement) {
+ this.orderByStatement = orderByStatement;
+ }
+
+ /**
+ * Used to perform operations against the Neo4J database.
+ *
+ * @param template the Neo4jOperations instance to use
+ * @see Neo4jOperations
+ */
+ public void setTemplate(Neo4jOperations template) {
+ this.template = template;
+ }
+
+ protected final Neo4jOperations getTemplate() {
+ return this.template;
+ }
+
+ /**
+ * The object type to be returned from each call to {@link #read()}
+ *
+ * @param targetType the type of object to return.
+ */
+ public void setTargetType(Class targetType) {
+ this.targetType = targetType;
+ }
+
+ protected final Class getTargetType() {
+ return this.targetType;
+ }
+
+ protected String generateLimitCypherQuery() {
+ StringBuilder query = new StringBuilder();
+
+ query.append("START ").append(startStatement);
+ query.append(matchStatement != null ? " MATCH " + matchStatement : "");
+ query.append(whereStatement != null ? " WHERE " + whereStatement : "");
+ query.append(" RETURN ").append(returnStatement);
+ query.append(" ORDER BY ").append(orderByStatement);
+ query.append(" SKIP " + (pageSize * page));
+ query.append(" LIMIT " + pageSize);
+
+ String resultingQuery = query.toString();
+
+ if (logger.isDebugEnabled()) {
+ logger.debug(resultingQuery);
+ }
+
+ return resultingQuery;
+ }
+
+ /**
+ * Checks mandatory properties
+ *
+ * @see InitializingBean#afterPropertiesSet()
+ */
+ @Override
+ public void afterPropertiesSet() throws Exception {
+ Assert.state(template != null, "A Neo4JOperations implementation is required");
+ Assert.state(targetType != null, "The type to be returned is required");
+ Assert.state(StringUtils.hasText(startStatement), "A START statement is required");
+ Assert.state(StringUtils.hasText(returnStatement), "A RETURN statement is required");
+ Assert.state(StringUtils.hasText(orderByStatement), "A ORDER BY statement is required");
+ }
+}
diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/data/Neo4jItemReader.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/data/Neo4jItemReader.java
index 3f79bd501..305907265 100644
--- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/data/Neo4jItemReader.java
+++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/data/Neo4jItemReader.java
@@ -22,153 +22,32 @@ import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
-import org.springframework.batch.item.ItemReader;
-import org.springframework.beans.factory.InitializingBean;
+
import org.springframework.data.neo4j.conversion.DefaultConverter;
import org.springframework.data.neo4j.conversion.Result;
import org.springframework.data.neo4j.conversion.ResultConverter;
-import org.springframework.data.neo4j.template.Neo4jOperations;
-import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
-import org.springframework.util.StringUtils;
/**
*
- * Restartable {@link ItemReader} that reads objects from the graph database Neo4j
- * via a paging technique.
- *
- *
- *
- * It executes cypher queries built from the statement fragments provided to
- * retrieve the requested data. The query is executed using paged requests of
- * a size specified in {@link #setPageSize(int)}. Additional pages are requested
- * as needed when the {@link #read()} method is called. On restart, the reader
- * will begin again at the same number item it left off at.
- *
- *
- *
- * Performance is dependent on your Neo4J configuration (embedded or remote) as
- * well as page size. Setting a fairly large page size and using a commit
- * interval that matches the page size should provide better performance.
- *
- *
- *
- * This implementation is thread-safe between calls to
- * {@link #open(org.springframework.batch.item.ExecutionContext)}, however you
- * should set saveState=false if used in a multi-threaded
- * environment (no restart available).
+ * Extensions of the {@link AbstractNeo4jItemReader} intended for use with versions of
+ * Spring Data Neo4J < 4. Conversions of the results are done using an external
+ * {@link ResultConverter}.
*
*
* @author Michael Minella
- *
+ * @see org.springframework.batch.item.data.Neo4j4ItemReader
*/
-public class Neo4jItemReader extends AbstractPaginatedDataItemReader implements
-InitializingBean {
+public class Neo4jItemReader extends AbstractNeo4jItemReader {
protected Log logger = LogFactory.getLog(getClass());
- private Neo4jOperations template;
-
- private String startStatement;
- private String returnStatement;
- private String matchStatement;
- private String whereStatement;
- private String orderByStatement;
-
- private Class targetType;
-
- private Map parameterValues;
-
private ResultConverter