diff --git a/community/collections/src/main/java/org/neo4j/helpers/collection/ResourceClosingIterator.java b/community/collections/src/main/java/org/neo4j/helpers/collection/ResourceClosingIterator.java index a1ee9c4b78afe..960bdc4336eeb 100644 --- a/community/collections/src/main/java/org/neo4j/helpers/collection/ResourceClosingIterator.java +++ b/community/collections/src/main/java/org/neo4j/helpers/collection/ResourceClosingIterator.java @@ -24,12 +24,13 @@ import org.neo4j.graphdb.Resource; import org.neo4j.graphdb.ResourceIterator; +import org.neo4j.graphdb.ResourceUtils; public abstract class ResourceClosingIterator implements ResourceIterator { - public static ResourceIterator newResourceIterator( Resource resource, Iterator iterator ) + public static ResourceIterator newResourceIterator( Iterator iterator, Resource... resources ) { - return new ResourceClosingIterator( resource, iterator ) + return new ResourceClosingIterator( iterator, resources ) { @Override public R map( R elem ) @@ -39,20 +40,23 @@ public R map( R elem ) }; } - private Resource resource; + private Resource[] resources; private final Iterator iterator; - ResourceClosingIterator( Resource resource, Iterator iterator ) + ResourceClosingIterator( Iterator iterator, Resource... resources ) { - this.resource = resource; + this.resources = resources; this.iterator = iterator; } @Override public void close() { - resource.close(); - resource = Resource.EMPTY; + if ( resources != null ) + { + ResourceUtils.closeAll( resources ); + resources = null; + } } @Override diff --git a/community/graphdb-api/src/main/java/org/neo4j/graphdb/impl/StandardExpander.java b/community/graphdb-api/src/main/java/org/neo4j/graphdb/impl/StandardExpander.java index 01971c7da1306..fc6a3ee7e1498 100644 --- a/community/graphdb-api/src/main/java/org/neo4j/graphdb/impl/StandardExpander.java +++ b/community/graphdb-api/src/main/java/org/neo4j/graphdb/impl/StandardExpander.java @@ -374,12 +374,12 @@ ResourceIterator doExpand( Path path, BranchState state ) { final Node node = path.endNode(); ResourceIterator resourceIterator = asResourceIterator( node.getRelationships().iterator() ); - return newResourceIterator( resourceIterator, new FilteringIterator<>( resourceIterator, rel -> + return newResourceIterator( new FilteringIterator<>( resourceIterator, rel -> { Exclusion exclude = exclusion.get( rel.getType().name() ); exclude = (exclude == null) ? defaultExclusion : exclude; return exclude.accept( node, rel ); - } ) ); + } ), resourceIterator ); } @Override @@ -588,7 +588,7 @@ void buildString( StringBuilder result ) ResourceIterator doExpand( final Path path, BranchState state ) { ResourceIterator resourceIterator = expander.doExpand( path, state ); - return newResourceIterator( resourceIterator, new FilteringIterator<>( resourceIterator, item -> + return newResourceIterator( new FilteringIterator<>( resourceIterator, item -> { Path extendedPath = ExtendedPath.extend( path, item ); for ( Filter filter : filters ) @@ -599,7 +599,7 @@ ResourceIterator doExpand( final Path path, BranchState state ) } } return true; - } ) ); + } ), resourceIterator ); } @Override diff --git a/community/kernel/src/main/java/org/neo4j/kernel/api/ReadOperations.java b/community/kernel/src/main/java/org/neo4j/kernel/api/ReadOperations.java index e7e4be160e09e..40bd697f3545b 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/api/ReadOperations.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/api/ReadOperations.java @@ -27,8 +27,10 @@ import org.neo4j.collection.primitive.PrimitiveIntIterator; import org.neo4j.collection.primitive.PrimitiveLongIterator; +import org.neo4j.collection.primitive.PrimitiveLongResourceIterator; import org.neo4j.cursor.Cursor; import org.neo4j.graphdb.Direction; +import org.neo4j.internal.kernel.api.IndexQuery; import org.neo4j.kernel.api.exceptions.EntityNotFoundException; import org.neo4j.kernel.api.exceptions.LabelNotFoundKernelException; import org.neo4j.kernel.api.exceptions.ProcedureException; @@ -44,7 +46,6 @@ import org.neo4j.kernel.api.proc.ProcedureSignature; import org.neo4j.kernel.api.proc.QualifiedName; import org.neo4j.kernel.api.proc.UserFunctionSignature; -import org.neo4j.internal.kernel.api.IndexQuery; import org.neo4j.kernel.api.schema.LabelSchemaDescriptor; import org.neo4j.kernel.api.schema.SchemaDescriptor; import org.neo4j.kernel.api.schema.constaints.ConstraintDescriptor; @@ -126,7 +127,7 @@ public interface ReadOperations * @return ids of the matching nodes * @throws org.neo4j.kernel.api.exceptions.index.IndexNotFoundKernelException if no such index is found. */ - PrimitiveLongIterator indexQuery( IndexDescriptor index, IndexQuery... predicates ) + PrimitiveLongResourceIterator indexQuery( IndexDescriptor index, IndexQuery... predicates ) throws IndexNotFoundKernelException, IndexNotApplicableKernelException; /** diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/api/ConstraintEnforcingEntityOperations.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/api/ConstraintEnforcingEntityOperations.java index cc60ccaa8077f..35489651330f4 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/api/ConstraintEnforcingEntityOperations.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/api/ConstraintEnforcingEntityOperations.java @@ -28,13 +28,16 @@ import org.neo4j.collection.primitive.PrimitiveIntIterator; import org.neo4j.collection.primitive.PrimitiveIntSet; import org.neo4j.collection.primitive.PrimitiveLongIterator; +import org.neo4j.collection.primitive.PrimitiveLongResourceIterator; import org.neo4j.cursor.Cursor; import org.neo4j.helpers.collection.CastingIterator; import org.neo4j.helpers.collection.Iterators; +import org.neo4j.internal.kernel.api.IndexQuery; +import org.neo4j.internal.kernel.api.IndexQuery.ExactPredicate; +import org.neo4j.internal.kernel.api.exceptions.KernelException; import org.neo4j.kernel.api.StatementConstants; import org.neo4j.kernel.api.exceptions.EntityNotFoundException; import org.neo4j.kernel.api.exceptions.InvalidTransactionTypeKernelException; -import org.neo4j.internal.kernel.api.exceptions.KernelException; import org.neo4j.kernel.api.exceptions.explicitindex.AutoIndexingKernelException; import org.neo4j.kernel.api.exceptions.index.IndexEntryConflictException; import org.neo4j.kernel.api.exceptions.index.IndexNotApplicableKernelException; @@ -49,8 +52,6 @@ import org.neo4j.kernel.api.exceptions.schema.RepeatedPropertyInCompositeSchemaException; import org.neo4j.kernel.api.exceptions.schema.UnableToValidateConstraintException; import org.neo4j.kernel.api.exceptions.schema.UniquePropertyValueValidationException; -import org.neo4j.internal.kernel.api.IndexQuery; -import org.neo4j.internal.kernel.api.IndexQuery.ExactPredicate; import org.neo4j.kernel.api.schema.LabelSchemaDescriptor; import org.neo4j.kernel.api.schema.RelationTypeSchemaDescriptor; import org.neo4j.kernel.api.schema.SchemaDescriptor; @@ -357,7 +358,7 @@ public PrimitiveLongIterator nodesGetForLabel( KernelStatement state, int labelI } @Override - public PrimitiveLongIterator indexQuery( KernelStatement statement, IndexDescriptor index, + public PrimitiveLongResourceIterator indexQuery( KernelStatement statement, IndexDescriptor index, IndexQuery[] predicates ) throws IndexNotFoundKernelException, IndexNotApplicableKernelException { diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/api/LookupFilter.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/api/LookupFilter.java index cb5fd166e156e..a6a16f771c6ae 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/api/LookupFilter.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/api/LookupFilter.java @@ -25,9 +25,9 @@ import org.neo4j.collection.primitive.PrimitiveLongCollections; import org.neo4j.collection.primitive.PrimitiveLongIterator; import org.neo4j.cursor.Cursor; +import org.neo4j.internal.kernel.api.IndexQuery; import org.neo4j.kernel.api.exceptions.EntityNotFoundException; import org.neo4j.kernel.api.index.PropertyAccessor; -import org.neo4j.internal.kernel.api.IndexQuery; import org.neo4j.kernel.impl.api.operations.EntityOperations; import org.neo4j.storageengine.api.NodeItem; import org.neo4j.values.storable.Value; diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/api/OperationsFacade.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/api/OperationsFacade.java index 1bc3bf6c3ebce..0ef531dedd40d 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/api/OperationsFacade.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/api/OperationsFacade.java @@ -30,8 +30,11 @@ import org.neo4j.collection.primitive.PrimitiveIntIterator; import org.neo4j.collection.primitive.PrimitiveLongCollections; import org.neo4j.collection.primitive.PrimitiveLongIterator; +import org.neo4j.collection.primitive.PrimitiveLongResourceIterator; import org.neo4j.cursor.Cursor; import org.neo4j.graphdb.Direction; +import org.neo4j.internal.kernel.api.IndexQuery; +import org.neo4j.internal.kernel.api.exceptions.KernelException; import org.neo4j.io.pagecache.tracing.cursor.PageCursorTracer; import org.neo4j.kernel.api.DataWriteOperations; import org.neo4j.kernel.api.ExecutionStatisticsOperations; @@ -45,7 +48,6 @@ import org.neo4j.kernel.api.TokenWriteOperations; import org.neo4j.kernel.api.exceptions.EntityNotFoundException; import org.neo4j.kernel.api.exceptions.InvalidTransactionTypeKernelException; -import org.neo4j.internal.kernel.api.exceptions.KernelException; import org.neo4j.kernel.api.exceptions.LabelNotFoundKernelException; import org.neo4j.kernel.api.exceptions.ProcedureException; import org.neo4j.kernel.api.exceptions.PropertyKeyIdNotFoundKernelException; @@ -74,7 +76,6 @@ import org.neo4j.kernel.api.proc.QualifiedName; import org.neo4j.kernel.api.proc.UserFunctionSignature; import org.neo4j.kernel.api.query.ExecutingQuery; -import org.neo4j.internal.kernel.api.IndexQuery; import org.neo4j.kernel.api.schema.LabelSchemaDescriptor; import org.neo4j.kernel.api.schema.RelationTypeSchemaDescriptor; import org.neo4j.kernel.api.schema.SchemaDescriptor; @@ -225,7 +226,7 @@ public PrimitiveLongIterator nodesGetForLabel( int labelId ) } @Override - public PrimitiveLongIterator indexQuery( IndexDescriptor index, IndexQuery... predicates ) + public PrimitiveLongResourceIterator indexQuery( IndexDescriptor index, IndexQuery... predicates ) throws IndexNotFoundKernelException, IndexNotApplicableKernelException { statement.assertOpen(); diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/api/StateHandlingStatementOperations.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/api/StateHandlingStatementOperations.java index 4a5e5c9fab2ab..4bfa0a38efec4 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/api/StateHandlingStatementOperations.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/api/StateHandlingStatementOperations.java @@ -28,10 +28,12 @@ import org.neo4j.collection.primitive.PrimitiveIntIterator; import org.neo4j.collection.primitive.PrimitiveIntSet; import org.neo4j.collection.primitive.PrimitiveIntStack; +import org.neo4j.collection.primitive.PrimitiveLongCollections; import org.neo4j.collection.primitive.PrimitiveLongIterator; import org.neo4j.collection.primitive.PrimitiveLongResourceIterator; import org.neo4j.cursor.Cursor; import org.neo4j.graphdb.NotFoundException; +import org.neo4j.internal.kernel.api.IndexQuery; import org.neo4j.kernel.api.DataWriteOperations; import org.neo4j.kernel.api.ExplicitIndex; import org.neo4j.kernel.api.ExplicitIndexHits; @@ -59,7 +61,6 @@ import org.neo4j.kernel.api.index.InternalIndexState; import org.neo4j.kernel.api.index.SchemaIndexProvider; import org.neo4j.kernel.api.properties.PropertyKeyIdIterator; -import org.neo4j.internal.kernel.api.IndexQuery; import org.neo4j.kernel.api.schema.LabelSchemaDescriptor; import org.neo4j.kernel.api.schema.RelationTypeSchemaDescriptor; import org.neo4j.kernel.api.schema.SchemaDescriptor; @@ -845,51 +846,57 @@ public long nodeGetFromUniqueIndexSeek( } @Override - public PrimitiveLongIterator indexQuery( KernelStatement state, IndexDescriptor index, IndexQuery... predicates ) + public PrimitiveLongResourceIterator indexQuery( KernelStatement state, IndexDescriptor index, IndexQuery... predicates ) throws IndexNotFoundKernelException, IndexNotApplicableKernelException { StorageStatement storeStatement = state.getStoreStatement(); IndexReader reader = storeStatement.getIndexReader( index ); - PrimitiveLongIterator committed = reader.query( predicates ); + PrimitiveLongResourceIterator committed = reader.query( predicates ); PrimitiveLongIterator exactMatches = reader.hasFullNumberPrecision( predicates ) ? committed : LookupFilter.exactIndexMatches( this, state, committed, predicates ); + PrimitiveLongIterator result; IndexQuery firstPredicate = predicates[0]; switch ( firstPredicate.type() ) { case exact: IndexQuery.ExactPredicate[] exactPreds = assertOnlyExactPredicates( predicates ); - return filterIndexStateChangesForSeek( state, exactMatches, index, IndexQuery.asValueTuple( exactPreds ) ); + result = filterIndexStateChangesForSeek( state, exactMatches, index, IndexQuery.asValueTuple( exactPreds ) ); + break; case stringSuffix: case stringContains: case exists: - return filterIndexStateChangesForScan( state, exactMatches, index ); + result = filterIndexStateChangesForScan( state, exactMatches, index ); + break; case rangeNumeric: - { assertSinglePredicate( predicates ); IndexQuery.NumberRangePredicate numPred = (IndexQuery.NumberRangePredicate) firstPredicate; - return filterIndexStateChangesForRangeSeekByNumber( state, index, numPred.from(), + result = filterIndexStateChangesForRangeSeekByNumber( state, index, numPred.from(), numPred.fromInclusive(), numPred.to(), numPred.toInclusive(), exactMatches ); - } + break; + case rangeString: { assertSinglePredicate( predicates ); IndexQuery.StringRangePredicate strPred = (IndexQuery.StringRangePredicate) firstPredicate; - return filterIndexStateChangesForRangeSeekByString( + result = filterIndexStateChangesForRangeSeekByString( state, index, strPred.from(), strPred.fromInclusive(), strPred.to(), strPred.toInclusive(), committed ); + break; } case stringPrefix: { assertSinglePredicate( predicates ); IndexQuery.StringPrefixPredicate strPred = (IndexQuery.StringPrefixPredicate) firstPredicate; - return filterIndexStateChangesForRangeSeekByPrefix( state, index, strPred.prefix(), committed ); + result = filterIndexStateChangesForRangeSeekByPrefix( state, index, strPred.prefix(), committed ); + break; } default: throw new UnsupportedOperationException( "Query not supported: " + Arrays.toString( predicates ) ); } + return PrimitiveLongCollections.resourceIterator( result, committed ); } private IndexQuery.ExactPredicate[] assertOnlyExactPredicates( IndexQuery[] predicates ) diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/api/operations/EntityReadOperations.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/api/operations/EntityReadOperations.java index ced25fb4e52c3..a8eb56439b1af 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/api/operations/EntityReadOperations.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/api/operations/EntityReadOperations.java @@ -23,13 +23,14 @@ import org.neo4j.collection.primitive.PrimitiveIntIterator; import org.neo4j.collection.primitive.PrimitiveIntSet; import org.neo4j.collection.primitive.PrimitiveLongIterator; +import org.neo4j.collection.primitive.PrimitiveLongResourceIterator; import org.neo4j.cursor.Cursor; +import org.neo4j.internal.kernel.api.IndexQuery; import org.neo4j.kernel.api.StatementConstants; import org.neo4j.kernel.api.exceptions.EntityNotFoundException; import org.neo4j.kernel.api.exceptions.index.IndexNotApplicableKernelException; import org.neo4j.kernel.api.exceptions.index.IndexNotFoundKernelException; import org.neo4j.kernel.api.exceptions.schema.IndexBrokenKernelException; -import org.neo4j.internal.kernel.api.IndexQuery; import org.neo4j.kernel.api.schema.index.IndexDescriptor; import org.neo4j.kernel.impl.api.KernelStatement; import org.neo4j.kernel.impl.api.RelationshipVisitor; @@ -56,7 +57,7 @@ public interface EntityReadOperations * @return ids of the matching nodes * @throws IndexNotFoundKernelException if no such index is found. */ - PrimitiveLongIterator indexQuery( KernelStatement statement, IndexDescriptor index, IndexQuery... predicates ) + PrimitiveLongResourceIterator indexQuery( KernelStatement statement, IndexDescriptor index, IndexQuery... predicates ) throws IndexNotFoundKernelException, IndexNotApplicableKernelException; /** diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/factory/GraphDatabaseFacade.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/factory/GraphDatabaseFacade.java index 68e09191b3b4c..c7e4c736aa2eb 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/factory/GraphDatabaseFacade.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/factory/GraphDatabaseFacade.java @@ -29,6 +29,7 @@ import org.neo4j.collection.primitive.PrimitiveLongCollections; import org.neo4j.collection.primitive.PrimitiveLongIterator; +import org.neo4j.collection.primitive.PrimitiveLongResourceIterator; import org.neo4j.function.Suppliers; import org.neo4j.function.ThrowingAction; import org.neo4j.graphdb.ConstraintViolationException; @@ -40,6 +41,7 @@ import org.neo4j.graphdb.QueryExecutionException; import org.neo4j.graphdb.Relationship; import org.neo4j.graphdb.RelationshipType; +import org.neo4j.graphdb.Resource; import org.neo4j.graphdb.ResourceIterable; import org.neo4j.graphdb.ResourceIterator; import org.neo4j.graphdb.Result; @@ -54,13 +56,14 @@ import org.neo4j.graphdb.traversal.TraversalDescription; import org.neo4j.helpers.collection.PrefetchingResourceIterator; import org.neo4j.helpers.collection.ResourceClosingIterator; +import org.neo4j.internal.kernel.api.IndexQuery; +import org.neo4j.internal.kernel.api.exceptions.KernelException; import org.neo4j.kernel.GraphDatabaseQueryService; import org.neo4j.kernel.api.KernelTransaction; import org.neo4j.kernel.api.ReadOperations; import org.neo4j.kernel.api.Statement; import org.neo4j.kernel.api.exceptions.EntityNotFoundException; import org.neo4j.kernel.api.exceptions.InvalidTransactionTypeKernelException; -import org.neo4j.internal.kernel.api.exceptions.KernelException; import org.neo4j.kernel.api.exceptions.Status; import org.neo4j.kernel.api.exceptions.index.IndexNotFoundKernelException; import org.neo4j.kernel.api.exceptions.schema.ConstraintValidationException; @@ -68,7 +71,6 @@ import org.neo4j.kernel.api.exceptions.schema.SchemaRuleNotFoundException; import org.neo4j.kernel.api.explicitindex.AutoIndexing; import org.neo4j.kernel.api.index.InternalIndexState; -import org.neo4j.internal.kernel.api.IndexQuery; import org.neo4j.kernel.api.schema.SchemaDescriptorFactory; import org.neo4j.kernel.api.schema.index.IndexDescriptor; import org.neo4j.internal.kernel.api.security.SecurityContext; @@ -633,7 +635,8 @@ private ResourceIterator nodesByLabelAndProperty( Label myLabel, String ke { // Ha! We found an index - let's use it to find matching nodes IndexQuery.ExactPredicate query = IndexQuery.exact( descriptor.schema().getPropertyId(), value ); - return map2nodes( readOps.indexQuery( descriptor, query ), statement ); + PrimitiveLongResourceIterator indexResult = readOps.indexQuery( descriptor, query ); + return map2nodes( indexResult, statement, indexResult ); } } catch ( KernelException e ) @@ -686,13 +689,13 @@ private ResourceIterator allNodesWithLabel( final Label myLabel ) final PrimitiveLongIterator nodeIds = statement.readOperations().nodesGetForLabel( labelId ); return ResourceClosingIterator - .newResourceIterator( statement, map( nodeId -> new NodeProxy( nodeActions, nodeId ), nodeIds ) ); + .newResourceIterator( map( nodeId -> new NodeProxy( nodeActions, nodeId ), nodeIds ), statement ); } - private ResourceIterator map2nodes( PrimitiveLongIterator input, Statement statement ) + private ResourceIterator map2nodes( PrimitiveLongIterator input, Resource... resources ) { return ResourceClosingIterator - .newResourceIterator( statement, map( id -> new NodeProxy( nodeActions, id ), input ) ); + .newResourceIterator( map( id -> new NodeProxy( nodeActions, id ), input ), resources ); } @Override diff --git a/community/kernel/src/test/java/org/neo4j/graphdb/ResourceIterableTest.java b/community/kernel/src/test/java/org/neo4j/graphdb/ResourceIterableTest.java index cb3ef1e220686..bfd3bea4b175f 100644 --- a/community/kernel/src/test/java/org/neo4j/graphdb/ResourceIterableTest.java +++ b/community/kernel/src/test/java/org/neo4j/graphdb/ResourceIterableTest.java @@ -23,6 +23,7 @@ import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; import static java.util.Arrays.asList; @@ -34,11 +35,11 @@ public class ResourceIterableTest { @Test - public void streamShouldCloseOnCompleted() throws Throwable + public void streamShouldCloseSingleOnCompleted() throws Throwable { // Given AtomicBoolean closed = new AtomicBoolean( false ); - ResourceIterator resourceIterator = newResourceIterator( () -> closed.set( true ), iterator( new Integer[]{1, 2, 3} ) ); + ResourceIterator resourceIterator = newResourceIterator( iterator( new Integer[]{1, 2, 3} ), () -> closed.set( true ) ); ResourceIterable iterable = () -> resourceIterator; @@ -49,4 +50,23 @@ public void streamShouldCloseOnCompleted() throws Throwable assertEquals( asList(1,2,3), result ); assertTrue( closed.get() ); } + + @Test + public void streamShouldCloseMultipleOnCompleted() throws Throwable + { + // Given + AtomicInteger closed = new AtomicInteger(); + Resource resource = closed::incrementAndGet; + ResourceIterator resourceIterator = + newResourceIterator( iterator( new Integer[]{1, 2, 3} ), resource, resource ); + + ResourceIterable iterable = () -> resourceIterator; + + // When + List result = iterable.stream().collect( Collectors.toList() ); + + // Then + assertEquals( asList(1,2,3), result ); + assertEquals( "two calls to close", 2, closed.get() ); + } } diff --git a/community/neo4j/src/test/java/org/neo4j/locking/QueryExecutionLocksIT.java b/community/neo4j/src/test/java/org/neo4j/locking/QueryExecutionLocksIT.java index 1eaa8c73b8631..6e428cd48a0da 100644 --- a/community/neo4j/src/test/java/org/neo4j/locking/QueryExecutionLocksIT.java +++ b/community/neo4j/src/test/java/org/neo4j/locking/QueryExecutionLocksIT.java @@ -37,14 +37,15 @@ import org.neo4j.collection.primitive.PrimitiveIntIterator; import org.neo4j.collection.primitive.PrimitiveLongIterator; +import org.neo4j.collection.primitive.PrimitiveLongResourceIterator; import org.neo4j.cursor.Cursor; -//import org.neo4j.cypher.internal.javacompat.ExecutionEngine; import org.neo4j.graphdb.Direction; import org.neo4j.graphdb.Label; import org.neo4j.graphdb.Lock; import org.neo4j.graphdb.Node; import org.neo4j.graphdb.PropertyContainer; import org.neo4j.graphdb.Transaction; +import org.neo4j.internal.kernel.api.IndexQuery; import org.neo4j.kernel.GraphDatabaseQueryService; import org.neo4j.kernel.api.ExplicitIndexHits; import org.neo4j.kernel.api.KernelTransaction; @@ -67,7 +68,6 @@ import org.neo4j.kernel.api.proc.QualifiedName; import org.neo4j.kernel.api.proc.UserFunctionSignature; import org.neo4j.kernel.api.query.ExecutingQuery; -import org.neo4j.internal.kernel.api.IndexQuery; import org.neo4j.kernel.api.schema.LabelSchemaDescriptor; import org.neo4j.kernel.api.schema.SchemaDescriptor; import org.neo4j.kernel.api.schema.constaints.ConstraintDescriptor; @@ -107,6 +107,8 @@ import static org.junit.Assert.assertTrue; import static org.neo4j.values.virtual.VirtualValues.EMPTY_MAP; +//import org.neo4j.cypher.internal.javacompat.ExecutionEngine; + public class QueryExecutionLocksIT { @@ -456,7 +458,7 @@ public PrimitiveLongIterator nodesGetForLabel( int labelId ) } @Override - public PrimitiveLongIterator indexQuery( IndexDescriptor index, IndexQuery... predicates ) + public PrimitiveLongResourceIterator indexQuery( IndexDescriptor index, IndexQuery... predicates ) throws IndexNotFoundKernelException, IndexNotApplicableKernelException { return readOperations.indexQuery( index, predicates ); diff --git a/community/primitive-collections/src/main/java/org/neo4j/collection/primitive/PrimitiveLongResourceCollections.java b/community/primitive-collections/src/main/java/org/neo4j/collection/primitive/PrimitiveLongResourceCollections.java index 662b435739878..d4db9477c22b8 100644 --- a/community/primitive-collections/src/main/java/org/neo4j/collection/primitive/PrimitiveLongResourceCollections.java +++ b/community/primitive-collections/src/main/java/org/neo4j/collection/primitive/PrimitiveLongResourceCollections.java @@ -20,18 +20,15 @@ package org.neo4j.collection.primitive; import java.util.Arrays; +import java.util.function.LongPredicate; import org.neo4j.graphdb.Resource; +import org.neo4j.graphdb.ResourceUtils; import static org.neo4j.collection.primitive.PrimitiveLongCollections.resourceIterator; public class PrimitiveLongResourceCollections { - public static PrimitiveLongResourceIterator iterator( Resource resource, final long... items ) - { - return resourceIterator( PrimitiveLongCollections.iterator( items ), resource ); - } - private static final PrimitiveLongResourceIterator EMPTY = new PrimitiveLongBaseResourceIterator( null ) { @Override @@ -46,6 +43,11 @@ public static PrimitiveLongResourceIterator emptyIterator() return EMPTY; } + public static PrimitiveLongResourceIterator iterator( Resource resource, final long... items ) + { + return resourceIterator( PrimitiveLongCollections.iterator( items ), resource ); + } + public static PrimitiveLongResourceIterator concat( PrimitiveLongResourceIterator... primitiveLongResourceIterators ) { return concat( Arrays.asList( primitiveLongResourceIterators ) ); @@ -56,12 +58,24 @@ public static PrimitiveLongResourceIterator concat( Iterable iterators; private volatile boolean closed; - PrimitiveLongConcatingResourceIterator( Iterable iterators ) + private PrimitiveLongConcatingResourceIterator( Iterable iterators ) { super( iterators.iterator() ); this.iterators = iterators; @@ -101,39 +115,37 @@ public void close() if ( !closed ) { closed = true; - closeAll(); + ResourceUtils.closeAll( iterators ); } } - // Copied from IOUtils - private void closeAll() + } + + private abstract static class PrimitiveLongFilteringResourceIterator extends PrimitiveLongBaseResourceIterator implements LongPredicate + { + private final PrimitiveLongIterator source; + + private PrimitiveLongFilteringResourceIterator( PrimitiveLongResourceIterator source ) { - Throwable closeThrowable = null; - for ( Resource resource : iterators ) + super( source ); + this.source = source; + } + + @Override + protected boolean fetchNext() + { + while ( source.hasNext() ) { - if ( resource != null ) + long testItem = source.next(); + if ( test( testItem ) ) { - try - { - resource.close(); - } - catch ( Throwable t ) - { - if ( closeThrowable == null ) - { - closeThrowable = t; - } - else - { - closeThrowable.addSuppressed( t ); - } - } + return next( testItem ); } } - if ( closeThrowable != null ) - { - throw new RuntimeException( "Exception closing multiple resources", closeThrowable ); - } + return false; } + + @Override + public abstract boolean test( long testItem ); } } diff --git a/community/primitive-collections/src/test/java/org/neo4j/collection/primitive/PrimitiveLongResourceCollectionsTest.java b/community/primitive-collections/src/test/java/org/neo4j/collection/primitive/PrimitiveLongResourceCollectionsTest.java new file mode 100644 index 0000000000000..19455947a1cdd --- /dev/null +++ b/community/primitive-collections/src/test/java/org/neo4j/collection/primitive/PrimitiveLongResourceCollectionsTest.java @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2002-2017 "Neo Technology," + * Network Engine for Objects in Lund AB [http://neotechnology.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.collection.primitive; + +import org.junit.Test; + +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.LongPredicate; + +import org.neo4j.graphdb.Resource; + +import static org.junit.Assert.assertEquals; + +public class PrimitiveLongResourceCollectionsTest +{ + private static final LongPredicate EVEN = value -> value % 2 == 0; + + // ITERATOR + + @Test + public void simpleIterator() throws Exception + { + // Given + CountingResource resource = new CountingResource(); + PrimitiveLongResourceIterator iterator = PrimitiveLongResourceCollections.iterator( resource, 1, 2, 3, 4 ); + + // Then + assertContent( iterator, 1, 2, 3, 4 ); + + // When + iterator.close(); + + // Then + assertEquals( "exactly one call to close", 1, resource.closeCount() ); + } + + // FILTER + + @Test + public void filterItems() throws Exception + { + // Given + CountingResource resource = new CountingResource(); + PrimitiveLongResourceIterator iterator = PrimitiveLongResourceCollections.iterator( resource, 1, 2, 3, 4 ); + + // When + PrimitiveLongResourceIterator filtered = PrimitiveLongResourceCollections.filter( iterator, EVEN ); + + // Then + assertContent( filtered, 2, 4 ); + + // When + filtered.close(); + + // Then + assertEquals( "exactly one call to close", 1, resource.closeCount() ); + } + + // CONCAT + + @Test + public void concatIterators() throws Exception + { + // Given + CountingResource resource = new CountingResource(); + PrimitiveLongResourceIterator first = PrimitiveLongResourceCollections.iterator( resource, 1, 2 ); + PrimitiveLongResourceIterator second = PrimitiveLongResourceCollections.iterator( resource, 3, 4 ); + + // When + PrimitiveLongResourceIterator concat = PrimitiveLongResourceCollections.concat( first, second ); + + // Then + assertContent( concat, 1, 2, 3, 4 ); + + // When + concat.close(); + + // Then + assertEquals( "all concatenated iterators are closed", 2, resource.closeCount() ); + } + + private void assertContent( PrimitiveLongResourceIterator iterator, long... expected ) + { + int i = 0; + while ( iterator.hasNext() ) + { + assertEquals( "has expected value", expected[i++], iterator.next() ); + } + assertEquals( "has all expected values", expected.length, i ); + } + + private static class CountingResource implements Resource + { + private AtomicInteger closed = new AtomicInteger(); + + @Override + public void close() + { + closed.incrementAndGet(); + } + + int closeCount() + { + return closed.get(); + } + } +} diff --git a/community/resource/src/main/java/org/neo4j/graphdb/ResourceUtils.java b/community/resource/src/main/java/org/neo4j/graphdb/ResourceUtils.java new file mode 100644 index 0000000000000..2589c0140d00e --- /dev/null +++ b/community/resource/src/main/java/org/neo4j/graphdb/ResourceUtils.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2002-2017 "Neo Technology," + * Network Engine for Objects in Lund AB [http://neotechnology.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.graphdb; + +import java.util.Arrays; +import java.util.Objects; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; + +public class ResourceUtils +{ + /** + * @param resources {@link Iterable} over resources to close. + */ + public static void closeAll( Iterable resources ) + { + closeAll( StreamSupport.stream( resources.spliterator(), false ) ); + } + + /** + * @param resources Array of resources to close. + */ + @SafeVarargs + public static void closeAll( T... resources ) + { + closeAll( Arrays.stream( resources ) ); + } + + /** + * Close all resources. Does NOT guarantee all being closed in case of unchecked exception. + * + * @param resources Stream of resources to close. + */ + public static void closeAll( Stream resources ) + { + resources.filter( Objects::nonNull ).forEach( Resource::close ); + } +} diff --git a/community/server/src/main/java/org/neo4j/server/rest/web/DatabaseActions.java b/community/server/src/main/java/org/neo4j/server/rest/web/DatabaseActions.java index 93248276b2d2f..b2548c0af3e88 100644 --- a/community/server/src/main/java/org/neo4j/server/rest/web/DatabaseActions.java +++ b/community/server/src/main/java/org/neo4j/server/rest/web/DatabaseActions.java @@ -1106,7 +1106,7 @@ protected Representation underlyingObjectToObject( Path position ) @Override public Iterator iterator() { - return newResourceIterator( () -> paths.forEach( Path::close ), super.iterator() ); + return newResourceIterator( super.iterator(), () -> paths.forEach( Path::close ) ); } }; return new ListRepresentation( returnType.repType, result );