Skip to content

Commit f227e3a

Browse files
author
Thomas Darimont
committed
DATACMNS-650 - Introduced CloseableIterator abstraction as a foundation for streaming of results.
A CloseableIterator abstracts the underlying result with support for releasing the associated resources via close() which can be transparently be used with TRW in Java 7 since Closeable implements AutoCloseable from Java 7 upwards. Added detector methods to QueryMethod to check whether the current method returns a Stream or a CloseableIterator. Introduced ReflectionUtils.isJava8StreamType to safely detect whether the given type is assignable to a Java 8 Stream type. Introdocued CloseableIteratorDisposingRunnable that can be registered as a cleanup action on a Stream. Original pull request: #116.
1 parent 6a556e4 commit f227e3a

File tree

5 files changed

+151
-3
lines changed

5 files changed

+151
-3
lines changed

src/main/java/org/springframework/data/repository/core/support/AbstractRepositoryMetadata.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import org.springframework.data.repository.core.RepositoryMetadata;
2525
import org.springframework.data.repository.util.QueryExecutionConverters;
2626
import org.springframework.data.util.ClassTypeInformation;
27+
import org.springframework.data.util.ReflectionUtils;
2728
import org.springframework.data.util.TypeInformation;
2829
import org.springframework.util.Assert;
2930

@@ -125,7 +126,7 @@ private static Class<?> unwrapWrapperTypes(TypeInformation<?> type) {
125126
Class<?> rawType = type.getType();
126127

127128
boolean needToUnwrap = Iterable.class.isAssignableFrom(rawType) || rawType.isArray()
128-
|| QueryExecutionConverters.supports(rawType);
129+
|| QueryExecutionConverters.supports(rawType) || ReflectionUtils.isJava8StreamType(rawType);
129130

130131
return needToUnwrap ? unwrapWrapperTypes(type.getComponentType()) : rawType;
131132
}

src/main/java/org/springframework/data/repository/query/QueryMethod.java

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2008-2014 the original author or authors.
2+
* Copyright 2008-2015 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -26,13 +26,16 @@
2626
import org.springframework.data.domain.Sort;
2727
import org.springframework.data.repository.core.EntityMetadata;
2828
import org.springframework.data.repository.core.RepositoryMetadata;
29+
import org.springframework.data.util.CloseableIterator;
30+
import org.springframework.data.util.ReflectionUtils;
2931
import org.springframework.util.Assert;
3032

3133
/**
3234
* Abstraction of a method that is designated to execute a finder query. Enriches the standard {@link Method} interface
3335
* with specific information that is necessary to construct {@link RepositoryQuery}s for the method.
3436
*
3537
* @author Oliver Gierke
38+
* @author Thomas Darimont
3639
*/
3740
public class QueryMethod {
3841

@@ -220,4 +223,20 @@ public boolean isQueryForEntity() {
220223
public String toString() {
221224
return method.toString();
222225
}
226+
227+
/**
228+
* @return
229+
* @since 1.10
230+
*/
231+
public boolean isCloseableIteratorQuery() {
232+
return org.springframework.util.ClassUtils.isAssignable(CloseableIterator.class, method.getReturnType());
233+
}
234+
235+
/**
236+
* @return
237+
* @since 1.10
238+
*/
239+
public boolean isStreamQuery() {
240+
return ReflectionUtils.isJava8StreamType(method.getReturnType());
241+
}
223242
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/*
2+
* Copyright 2015 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.util;
17+
18+
import java.io.Closeable;
19+
import java.util.Iterator;
20+
21+
/**
22+
* A {@link CloseableIterator} serves as a bridging data structure for the underlying data store specific results that
23+
* can be wrapped in a Java 8 {@link java.util.stream.Stream}.
24+
* <p>
25+
* For convenient usage in an advanced for-loop we the {@code iterator()} method should return {@literal this}.
26+
*
27+
* @author Thomas Darimont
28+
* @param <T>
29+
* @since 1.10
30+
*/
31+
public interface CloseableIterator<T> extends Iterator<T>, Closeable, Iterable<T> {
32+
33+
/**
34+
* Closes this iterator, freeing any resources created.
35+
*/
36+
@Override
37+
void close();
38+
39+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/*
2+
* Copyright 2015 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.util;
17+
18+
/**
19+
* A {@link Runnable} that closes the given {@link CloseableIterator} in its {@link #run()} method. If the given
20+
* {@code closeable} is {@literal null} the {@link #run()} method effectively becomes a noop.
21+
* <p>
22+
* Can be used in conjunction with streams as close action via:
23+
*
24+
* <pre>
25+
* CloseableIterator<T> result = ...;
26+
* Spliterator<T> spliterator = ...;
27+
*
28+
* return StreamSupport.stream(spliterator, false).onClose(new CloseableIteratorDisposingRunnable(result));
29+
* </pre>
30+
*
31+
* @author Thomas Darimont
32+
*/
33+
public class CloseableIteratorDisposingRunnable implements Runnable {
34+
35+
private CloseableIterator<?> closeable;
36+
37+
/**
38+
* Creates a new {@link CloseableIteratorDisposingRunnable}.
39+
*
40+
* @param closeable may be {@literal null}
41+
*/
42+
public CloseableIteratorDisposingRunnable(CloseableIterator<?> closeable) {
43+
this.closeable = closeable;
44+
}
45+
46+
@Override
47+
public void run() {
48+
49+
CloseableIterator<?> ci = closeable;
50+
if (ci == null) {
51+
return;
52+
}
53+
54+
closeable = null;
55+
ci.close();
56+
}
57+
}

src/main/java/org/springframework/data/util/ReflectionUtils.java

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2013 the original author or authors.
2+
* Copyright 2012-2015 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -19,6 +19,7 @@
1919
import java.lang.reflect.Field;
2020
import java.lang.reflect.Method;
2121
import java.lang.reflect.Modifier;
22+
import java.util.stream.Stream;
2223

2324
import org.springframework.beans.BeanUtils;
2425
import org.springframework.core.annotation.AnnotationUtils;
@@ -30,10 +31,26 @@
3031
* Spring Data specific reflection utility methods and classes.
3132
*
3233
* @author Oliver Gierke
34+
* @author Thomas Darimont
3335
* @since 1.5
3436
*/
3537
public abstract class ReflectionUtils {
3638

39+
private static final Class<?> JAVA8_STREAM_TYPE;
40+
41+
static {
42+
String streamClassName = "java.util.stream.Stream";
43+
44+
Class<?> cls = null;
45+
if (ClassUtils.isPresent(streamClassName, ReflectionUtils.class.getClassLoader())) {
46+
try {
47+
cls = Class.forName(streamClassName, true, ReflectionUtils.class.getClassLoader());
48+
} catch (ClassNotFoundException ignore) {}
49+
}
50+
51+
JAVA8_STREAM_TYPE = cls;
52+
}
53+
3754
private ReflectionUtils() {}
3855

3956
/**
@@ -208,4 +225,19 @@ public static void setField(Field field, Object target, Object value) {
208225
org.springframework.util.ReflectionUtils.makeAccessible(field);
209226
org.springframework.util.ReflectionUtils.setField(field, target, value);
210227
}
228+
229+
/**
230+
* Tests whether the given {@code type} is assignable from a Java 8 {@link Stream}.
231+
*
232+
* @param type may be {@literal null}
233+
* @return
234+
*/
235+
public static boolean isJava8StreamType(Class<?> type) {
236+
237+
if (type == null || JAVA8_STREAM_TYPE == null) {
238+
return false;
239+
}
240+
241+
return JAVA8_STREAM_TYPE.isAssignableFrom(type);
242+
}
211243
}

0 commit comments

Comments
 (0)