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}