| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You 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.apache.activemq.artemis.utils.collections; |
| |
| import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; |
| import java.util.function.Consumer; |
| |
| public class UpdatableIterator<E> implements ResettableIterator<E>, RepeatableIterator<E> { |
| |
| private static final AtomicReferenceFieldUpdater<UpdatableIterator, RepeatableIteratorWrapper> changedIteratorFieldUpdater = AtomicReferenceFieldUpdater.newUpdater(UpdatableIterator.class, RepeatableIteratorWrapper.class, "changedIterator"); |
| private volatile RepeatableIteratorWrapper<E> changedIterator; |
| private RepeatableIteratorWrapper<E> currentIterator; |
| |
| public UpdatableIterator(ResettableIterator<E> iterator) { |
| this.currentIterator = new RepeatableIteratorWrapper<>(iterator); |
| } |
| |
| /** |
| * This can be called by another thread. |
| * It sets a new iterator, that will be picked up on the next reset. |
| * |
| * @param iterator the new iterator to update to. |
| */ |
| public void update(ResettableIterator<E> iterator) { |
| changedIteratorFieldUpdater.set(this, new RepeatableIteratorWrapper<>(iterator)); |
| } |
| |
| /* |
| * ---- ResettableIterator Methods ----- |
| * All the below ResettableIterator (including reset) methods MUST be called by the same thread, |
| * this is as any other use of Iterator. |
| */ |
| |
| /** |
| * When reset is called, then if a new iterator has been provided by another thread via update method, |
| * then we switch over to using the new iterator. |
| * |
| * It is important that on nulling off the changedIterator, we atomically compare and set as the |
| * changedIterator could be further updated by another thread whilst we are resetting, |
| * the subsequent update simply would be picked up on the next reset. |
| */ |
| @Override |
| public void reset() { |
| RepeatableIteratorWrapper<E> changedIterator = this.changedIterator; |
| if (changedIterator != null) { |
| currentIterator = changedIterator; |
| changedIteratorFieldUpdater.compareAndSet(this, changedIterator, null); |
| } |
| currentIterator.reset(); |
| } |
| |
| @Override |
| public boolean hasNext() { |
| return currentIterator.hasNext(); |
| } |
| |
| @Override |
| public E next() { |
| return currentIterator.next(); |
| } |
| |
| @Override |
| public void remove() { |
| currentIterator.remove(); |
| } |
| |
| @Override |
| public void forEachRemaining(Consumer<? super E> action) { |
| currentIterator.forEachRemaining(action); |
| } |
| |
| @Override |
| public void repeat() { |
| currentIterator.repeat(); |
| } |
| } |