/* * ModeShape (http://www.modeshape.org) * * 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.modeshape.common.collection.ring; import java.util.concurrent.Executor; import org.modeshape.common.CommonI18n; import org.modeshape.common.collection.ring.RingBuffer.ConsumerAdapter; import org.modeshape.common.logging.Logger; import org.modeshape.common.util.CheckArg; /** * @param <T> the type of entries stored in the buffer * @param <C> the type of consumer * @author Randall Hauch (rhauch@redhat.com) */ public class RingBufferBuilder<T, C> { /** * Create a builder for ring buffers that use the supplied {@link Executor} to create consumer threads and the supplied * {@link ConsumerAdapter} to adapt to custom consumer implementations. The ring buffer will <i>only</i> allow entries to be * added from a single thread. * * @param executor the executor that should be used to create threads to run {@link Consumer}s; may not be null * @param adapter the adapter to the desired consumer interface; may not be null * @return the builder for ring buffers; never null */ public static <T, C> RingBufferBuilder<T, C> withSingleProducer( Executor executor, ConsumerAdapter<T, C> adapter ) { return new RingBufferBuilder<>(executor, adapter).singleProducer(); } /** * Create a builder for ring buffers that use the supplied {@link Executor} to create threads for each {@link Consumer} * instance. The ring buffer will <i>only</i> allow entries to be added from a single thread. * * @param executor the executor that should be used to create threads to run {@link Consumer}s; may not be null * @param entryClass the type of entry that will be put into and consumed from the buffer; may not be null * @return the builder for ring buffers; never null */ public static <T, C extends Consumer<T>> RingBufferBuilder<T, C> withSingleProducer( Executor executor, Class<T> entryClass ) { return new RingBufferBuilder<>(executor, StandardConsumerAdapter.<T, C>create()).singleProducer(); } /** * Create a builder for ring buffers that use the supplied {@link Executor} to create consumer threads and the supplied * {@link ConsumerAdapter} to adapt to custom consumer implementations. The ring buffer will allow entries to be added from * multiple threads. * * @param executor the executor that should be used to create threads to run {@link Consumer}s; may not be null * @param adapter the adapter to the desired consumer interface; may not be null * @return the builder for ring buffers; never null */ public static <T, C> RingBufferBuilder<T, C> withMultipleProducers( Executor executor, ConsumerAdapter<T, C> adapter ) { return new RingBufferBuilder<>(executor, adapter).multipleProducers(); } /** * Create a builder for ring buffers that use the supplied {@link Executor} to create threads for each {@link Consumer} * instance. The ring buffer will allow entries to be added from multiple threads. * * @param executor the executor that should be used to create threads to run {@link Consumer}s; may not be null * @param entryClass the type of entry that will be put into and consumed from the buffer; may not be null * @return the builder for ring buffers; never null */ public static <T, C extends Consumer<T>> RingBufferBuilder<T, C> withMultipleProducers( Executor executor, Class<T> entryClass ) { return new RingBufferBuilder<>(executor, StandardConsumerAdapter.<T, C>create()).multipleProducers(); } public static final int DEFAULT_BUFFER_SIZE = 1 << 10; // 1024 public static final boolean DEFAULT_GARBAGE_COLLECT_ENTITIES = false; public static final String DEFAULT_NAME = "ringbuffer"; private final Executor executor; private final ConsumerAdapter<T, C> adapter; private final Logger logger = Logger.getLogger(getClass()); private int bufferSize = DEFAULT_BUFFER_SIZE; private boolean garbageCollect = DEFAULT_GARBAGE_COLLECT_ENTITIES; private boolean singleProducer = true; private String name = DEFAULT_NAME; private WaitStrategy waitStrategy; /** * @param executor the executor that should be used to create threads to run {@link Consumer}s; may not be null * @param adapter the adapter for consumers; may not be null */ protected RingBufferBuilder( Executor executor, ConsumerAdapter<T, C> adapter ) { CheckArg.isNotNull(executor, "executor"); CheckArg.isNotNull(adapter, "adapter"); this.executor = executor; this.adapter = adapter; } public RingBufferBuilder<T, C> ofSize( int bufferSize ) { CheckArg.isPositive(bufferSize, "bufferSize"); try { CheckArg.isPowerOfTwo(bufferSize, "bufferSize"); } catch (IllegalArgumentException e) { int nextPowerOf2 = nextPowerOf2(bufferSize); logger.warn(CommonI18n.incorrectRingBufferSize, bufferSize, nextPowerOf2); bufferSize = nextPowerOf2; } this.bufferSize = bufferSize; return this; } private int nextPowerOf2(int number) { if (number <= 0) { return 1; } --number; number = number | (number >> 1); number = number | (number >> 2); number = number | (number >> 4); number = number | (number >> 8); number = number | (number >> 16); return ++number; } public RingBufferBuilder<T, C> garbageCollect( boolean gcEntries ) { this.garbageCollect = gcEntries; return this; } public RingBufferBuilder<T, C> waitUsing( WaitStrategy waitStrategy ) { this.waitStrategy = waitStrategy; return this; } protected RingBufferBuilder<T, C> singleProducer() { this.singleProducer = true; return this; } protected RingBufferBuilder<T, C> multipleProducers() { this.singleProducer = false; return this; } public RingBufferBuilder<T, C> named( String bufferName ) { if (bufferName != null && bufferName.trim().isEmpty()) this.name = bufferName; return this; } public RingBuffer<T, C> build() { WaitStrategy waitStrategy = this.waitStrategy; if (waitStrategy == null) waitStrategy = defaultWaitStrategy(); Cursor cursor = defaultCursor(bufferSize, waitStrategy); return new RingBuffer<T, C>(name, cursor, executor, adapter, garbageCollect, singleProducer); } protected WaitStrategy defaultWaitStrategy() { return new BlockingWaitStrategy(); } protected Cursor defaultCursor( int bufferSize, WaitStrategy waitStrategy ) { return new SingleProducerCursor(bufferSize, waitStrategy); } }