001/*
002 * ModeShape (http://www.modeshape.org)
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *       http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.modeshape.common.collection.ring;
018
019import java.util.concurrent.Executor;
020import org.modeshape.common.CommonI18n;
021import org.modeshape.common.collection.ring.RingBuffer.ConsumerAdapter;
022import org.modeshape.common.logging.Logger;
023import org.modeshape.common.util.CheckArg;
024
025/**
026 * @param <T> the type of entries stored in the buffer
027 * @param <C> the type of consumer
028 * @author Randall Hauch (rhauch@redhat.com)
029 */
030public class RingBufferBuilder<T, C> {
031
032    /**
033     * Create a builder for ring buffers that use the supplied {@link Executor} to create consumer threads and the supplied
034     * {@link ConsumerAdapter} to adapt to custom consumer implementations. The ring buffer will <i>only</i> allow entries to be
035     * added from a single thread.
036     * 
037     * @param executor the executor that should be used to create threads to run {@link Consumer}s; may not be null
038     * @param adapter the adapter to the desired consumer interface; may not be null
039     * @return the builder for ring buffers; never null
040     */
041    public static <T, C> RingBufferBuilder<T, C> withSingleProducer( Executor executor,
042                                                                     ConsumerAdapter<T, C> adapter ) {
043        return new RingBufferBuilder<>(executor, adapter).singleProducer();
044    }
045
046    /**
047     * Create a builder for ring buffers that use the supplied {@link Executor} to create threads for each {@link Consumer}
048     * instance. The ring buffer will <i>only</i> allow entries to be added from a single thread.
049     * 
050     * @param executor the executor that should be used to create threads to run {@link Consumer}s; may not be null
051     * @param entryClass the type of entry that will be put into and consumed from the buffer; may not be null
052     * @return the builder for ring buffers; never null
053     */
054    public static <T, C extends Consumer<T>> RingBufferBuilder<T, C> withSingleProducer( Executor executor,
055                                                                                         Class<T> entryClass ) {
056        return new RingBufferBuilder<>(executor, StandardConsumerAdapter.<T, C>create()).singleProducer();
057    }
058
059    /**
060     * Create a builder for ring buffers that use the supplied {@link Executor} to create consumer threads and the supplied
061     * {@link ConsumerAdapter} to adapt to custom consumer implementations. The ring buffer will allow entries to be added from
062     * multiple threads.
063     * 
064     * @param executor the executor that should be used to create threads to run {@link Consumer}s; may not be null
065     * @param adapter the adapter to the desired consumer interface; may not be null
066     * @return the builder for ring buffers; never null
067     */
068    public static <T, C> RingBufferBuilder<T, C> withMultipleProducers( Executor executor,
069                                                                        ConsumerAdapter<T, C> adapter ) {
070        return new RingBufferBuilder<>(executor, adapter).multipleProducers();
071    }
072
073    /**
074     * Create a builder for ring buffers that use the supplied {@link Executor} to create threads for each {@link Consumer}
075     * instance. The ring buffer will allow entries to be added from multiple threads.
076     * 
077     * @param executor the executor that should be used to create threads to run {@link Consumer}s; may not be null
078     * @param entryClass the type of entry that will be put into and consumed from the buffer; may not be null
079     * @return the builder for ring buffers; never null
080     */
081    public static <T, C extends Consumer<T>> RingBufferBuilder<T, C> withMultipleProducers( Executor executor,
082                                                                                            Class<T> entryClass ) {
083        return new RingBufferBuilder<>(executor, StandardConsumerAdapter.<T, C>create()).multipleProducers();
084    }
085
086    public static final int DEFAULT_BUFFER_SIZE = 1 << 10; // 1024
087    public static final boolean DEFAULT_GARBAGE_COLLECT_ENTITIES = false;
088    public static final String DEFAULT_NAME = "ringbuffer";
089
090    private final Executor executor;
091    private final ConsumerAdapter<T, C> adapter;
092    private final Logger logger = Logger.getLogger(getClass());
093    private int bufferSize = DEFAULT_BUFFER_SIZE;
094    private boolean garbageCollect = DEFAULT_GARBAGE_COLLECT_ENTITIES;
095    private boolean singleProducer = true;
096    private String name = DEFAULT_NAME;
097    private WaitStrategy waitStrategy;
098    
099    /**
100     * @param executor the executor that should be used to create threads to run {@link Consumer}s; may not be null
101     * @param adapter the adapter for consumers; may not be null
102     */
103    protected RingBufferBuilder( Executor executor,
104                                 ConsumerAdapter<T, C> adapter ) {
105        CheckArg.isNotNull(executor, "executor");
106        CheckArg.isNotNull(adapter, "adapter");
107        this.executor = executor;
108        this.adapter = adapter;
109    }
110
111    public RingBufferBuilder<T, C> ofSize( int bufferSize ) {
112        CheckArg.isPositive(bufferSize, "bufferSize");
113        try {
114            CheckArg.isPowerOfTwo(bufferSize, "bufferSize");
115        } catch (IllegalArgumentException e) {
116            int nextPowerOf2 = nextPowerOf2(bufferSize);
117            logger.warn(CommonI18n.incorrectRingBufferSize, bufferSize, nextPowerOf2);
118            bufferSize = nextPowerOf2;
119        }
120        this.bufferSize = bufferSize;
121        return this;
122    }
123    
124    private int nextPowerOf2(int number) {
125        if (number <= 0) {
126            return 1;
127        }
128        --number;
129        number = number | (number >> 1);
130        number = number | (number >> 2);
131        number = number | (number >> 4);
132        number = number | (number >> 8);
133        number = number | (number >> 16);
134        return ++number;
135    }
136
137    public RingBufferBuilder<T, C> garbageCollect( boolean gcEntries ) {
138        this.garbageCollect = gcEntries;
139        return this;
140    }
141
142    public RingBufferBuilder<T, C> waitUsing( WaitStrategy waitStrategy ) {
143        this.waitStrategy = waitStrategy;
144        return this;
145    }
146
147    protected RingBufferBuilder<T, C> singleProducer() {
148        this.singleProducer = true;
149        return this;
150    }
151
152    protected RingBufferBuilder<T, C> multipleProducers() {
153        this.singleProducer = false;
154        return this;
155    }
156
157    public RingBufferBuilder<T, C> named( String bufferName ) {
158        if (bufferName != null && bufferName.trim().isEmpty()) this.name = bufferName;
159        return this;
160    }
161
162    public RingBuffer<T, C> build() {
163        WaitStrategy waitStrategy = this.waitStrategy;
164        if (waitStrategy == null) waitStrategy = defaultWaitStrategy();
165        Cursor cursor = defaultCursor(bufferSize, waitStrategy);
166        return new RingBuffer<T, C>(name, cursor, executor, adapter, garbageCollect, singleProducer);
167    }
168
169    protected WaitStrategy defaultWaitStrategy() {
170        return new BlockingWaitStrategy();
171    }
172
173    protected Cursor defaultCursor( int bufferSize,
174                                    WaitStrategy waitStrategy ) {
175        return new SingleProducerCursor(bufferSize, waitStrategy);
176    }
177}