com.sun.grizzly.websocket.WebSocketImpl Maven / Gradle / Ivy
The newest version!
/*
*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2007-2009 Sun Microsystems, Inc. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License"). You
* may not use this file except in compliance with the License. You can obtain
* a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
* or glassfish/bootstrap/legal/LICENSE.txt. See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
* Sun designates this particular file as subject to the "Classpath" exception
* as provided by Sun in the GPL Version 2 section of the License file that
* accompanied this code. If applicable, add the following below the License
* Header, with the fields enclosed by brackets [] replaced by your own
* identifying information: "Portions Copyrighted [year]
* [name of copyright owner]"
*
* Contributor(s):
*
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license." If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above. However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*
*/
package com.sun.grizzly.websocket;
import com.sun.grizzly.util.DataStructures;
import com.sun.grizzly.util.LoggerUtils;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.NotYetConnectedException;
import java.nio.channels.SelectionKey;
import java.util.Queue;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;
/**
* TODO: {@link DataFrame} needs QA and feedback about design.
*
* TODO: throttle and concurrent send , user control of the situation.
* if we dont fail on writebuffer limit reached
* we must propagate that info somehow,
* currently there is only closed and its exception as info
* and a boolean return from send method
* so either change boolean to enum or add exception thrown from send method ?
*
* TODO: The current design allows for one eventlistener per context.
* Is there a real need for more at this API level, nothing stops from
* listeners to chain each others. ?
* If more then one, it would be of interest to have
* per listener flags for per eventype to run in threadpool.
* To implement that efficently would require abstract baseclass instead of
* interface that its today.
*
* TODO: should client be allowed to provide origin string?
* if not we must find a suitable hostname to use, how do we choose when
* several hostnames exist, just grab one and be happy ?.
*
* TODO: add serverside {@link WebsocketHandshake#origin} security ?,
* its client provided info.. so can never do any real securiy based on it.
* lists of allowed origins per resourcepath?.
*
* TODO: send() method optional frame validation ?
*
* TODO: RFC 3490. cant use URI as param due to its not compliant.
* can use seperate param for host value, but that seems to rely on that
* the OS is doing the IDNA conversion. we need to provide IDNA conversion even
* for that simple case, full URI/string parser that is IDNA compliant
* would be optimal.
*
* TODO: context lifecycle ? when removed, the websockets still fetches
* config parameters in runtime from the context and keeps working.
* Should we keep a list of websockets per context so we
* can kill them and also allow for easy to use msg broadcast ?.
* its perhaps better to leave that to service implementator ,
* or we can provide another layer ontop that can be used if wanted.
*
* TODO: dont sticky large buffers forever in parseframe().
* impl and configuration needed.
*
* MySwapList, ensure no large datastructure is left behind.
*
* socketchannel.write(ByteBuffer[]) does not perform well.
* Investigate why, and how hard its to fix.
* It should give a perf boost to allow for bulk writes when draining.
*
* TODO: change closedCause to a list ?:
* The cause might not be the actual cause, another thread might cause
* on exception and update state to closed first, hence the true cause
* is hidden.
*
* TODO: is there a need for pluggable handler for uncaught exceptions
* from services ({@link WebSocketListener}) per context or global ?.
* It could be used to detect unhealthy services and remove them, or at least
* gather statistics instead if just filling an error log with hard to use data?.
*
* Implementation is based on :
* http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol
*
* @author gustav trede
* @since 2009
*/
public class WebSocketImpl extends WebSocket implements SelectorLogicHandler{
protected final static Logger logger = LoggerUtils.getLogger();
protected final static byte TEXTTYPE = (byte)0x00;
protected final static byte BINARYTYPE = (byte)0x80;
protected final static byte TEXTterminate = (byte)0xff;
protected enum ReadyState{CONNECTING,OPEN,CLOSED};
private final static AtomicReferenceFieldUpdater
state = AtomicReferenceFieldUpdater.
newUpdater(WebSocketImpl.class, ReadyState.class, "readystate");
private volatile ReadyState readystate = ReadyState.CONNECTING;
private byte frametype = -1;
private int framestart;
private int bindataend;
private int bindatalength;
private int parsed;
/**
* Its value is updated at:
* Once when entering selectthread as a new connection.
* Handshake completion (readystate changes to OPEN).
* Readystate is OPEN and the SelectionKey readyOps contains OP_READ.
*/
private long idleTimestamp;
/**
* Fast access to the idletimeout value at the current readystate.
*/
private long currentTimeOut;
protected volatile int readDataThrottledCounter;
/**
* Socket native read buffer is not included in this value.
*/
private final AtomicInteger bufferedOnMessageRAMbytes = new AtomicInteger();
/**
* Socket native write buffer is not included in this value
*/
private final AtomicInteger bufferedSendBytes = new AtomicInteger();
protected ByteBuffer readBuffer;
private byte[] readByteArray;
private ByteBuffer currentwrite;
private final Queue writeQueue;
private volatile Throwable closedCause;
protected final WebSocketContext ctx;
protected final TCPIOhandler iohandler;
private final WebsocketHandshake handshake;
private SelectThread selthread;
private final Runnable reEnableReadInterest = new Runnable() {
public void run() {
try{
iohandler.addKeyInterest(SelectionKey.OP_READ);
parseFrame();//consume any remaining data.
}catch(Throwable t){
close(t,true);
}
}};
/**
*
* @param uri
* @param listener
* @param origin
* @param protocol
* @param sslctx
* @param copysettingsfrom
* @param st
* @return
* @throws IOException
*/
protected final static WebSocket openClient(String uri,
WebSocketListener listener,String origin,String protocol,
SSLContext sslctx,WebSocketContext copysettingsfrom,SelectThread st)
throws IOException{
try{
WebsocketClientHandshake wsh =
new WebsocketClientHandshake(uri,origin,protocol);
TCPIOhandler ioh;
if (wsh.secureonly){
if (sslctx == null){
throw new MalformedURLException(
"wss but SSLContext is null");
}
SSLEngine ssleEngine = sslctx.createSSLEngine(
wsh.remoteAddress.getHostName(),wsh.remoteAddress.getPort());
ssleEngine.setUseClientMode(true);
ioh = new SSLIOhandler(wsh.remoteAddress,ssleEngine);
}else{
ioh = new TCPIOhandler(wsh.remoteAddress);
}
WebSocketContext ctx =
new WebSocketContext("/notused",protocol,listener);
if (copysettingsfrom!=null)
ctx.copySomeSettingsFrom(copysettingsfrom);
return doOPen(wsh, ioh, ctx, st);
}catch(IOException ie){
throw ie;
}
catch(RuntimeException e){
throw new IOExceptionWrap(e);
}
}
/**
*
* @param handshake
* @param iohandler
* @param ctx
* @param st
* @return
*/
protected final static WebSocket doOPen(WebsocketHandshake handshake,
TCPIOhandler iohandler,WebSocketContext ctx,SelectThread st) {
WebSocketImpl wsi = new WebSocketImpl(handshake,iohandler,ctx);
if (st == null)
st = SelectThread.getNextSelectThread();
st.addConnection(wsi);
return wsi;
}
/**
*
* @param handshake
* @param iohandler
* @param ctx
*/
private WebSocketImpl(WebsocketHandshake handshake,TCPIOhandler iohandler,
WebSocketContext ctx) {
if (ctx == null)
throw new IllegalArgumentException("ctx is null");
if (iohandler == null)
throw new IllegalArgumentException("iohandler is null");
if (handshake == null)
throw new IllegalArgumentException("handshake is null");
handshake.setIOhandler(iohandler);
this.handshake = handshake;
this.iohandler = iohandler;
this.ctx = ctx;
this.writeQueue = DataStructures.getCLQinstance(ByteBuffer.class);
}
@Override
public final int getBufferedSendBytes() {
return bufferedSendBytes.get();
}
@Override
public final int getBufferedOnMessageBytes(){
return bufferedOnMessageRAMbytes.get();
}
@Override
public final int getReadDataThrottledCounter() {
return readDataThrottledCounter;
}
@Override
public final WebSocketContext getWebSocketContext() {
return ctx;
}
@Override
public final SocketAddress getRemotAddress(){
return iohandler.channel.socket().getRemoteSocketAddress();
}
/**
*
* @param st
* @param timestamp
*/
public final void enteredSelector(SelectThread st, long timestamp){
try{
selthread = st;
idleTimestamp = timestamp;
currentTimeOut = ctx.getHandshakeTimeoutSeconds() * 1000000000L;
iohandler.enteredSelectThread(this, st);
//TODO make buffersize adaptive for SSL too, handle SSL overflow.
int bufflength = (iohandler instanceof SSLIOhandler) ?
// ensure room for max dataframesize and for SSL to read without
// overflow
Math.max(ctx.getMaxDataFramelengthBytes(),4096)+
((SSLIOhandler)iohandler).getRequiredReadBuffersize() :
ctx.getInitialReadBufferLength();
readBuffer = ByteBuffer.allocate(bufflength);
readByteArray = readBuffer.array();
handshake.bbf = readBuffer;
if(iohandler.doHandshakeChain(readBuffer)){
setOPENstate(timestamp);
}
}catch(Throwable e){
close(e,true);
}
}
/**
*
* @param key
* @param timestamp
*/
public final void handleSelectedKey(SelectionKey key, long timestamp){
try {
if (readystate != ReadyState.OPEN){
if (readystate == ReadyState.CONNECTING){
if (iohandler.doHandshakeChain(readBuffer)){
setOPENstate(timestamp);
}
}
return;
}
//System.err.println(key+" read:"+key.isReadable()+" write:"+key.isWritable()+" connect:"+key.isConnectable());
final int ops = key.readyOps();
if ((ops & SelectionKey.OP_WRITE)!=0 ){
doSelectThreadWrite();
}
if ((ops & SelectionKey.OP_READ)!=0 ){
idleTimestamp = timestamp;
iohandler.read(readBuffer);
parseFrame();
}
} catch (Throwable ex) {
close(ex,true);
}
}
/**
*
* @param timestamp
* @throws Throwable
*/
private final void setOPENstate(long timestamp) throws Throwable {
parsed = handshake.parsed;
final boolean hasdata = parsed > 0;
if (hasdata){
iohandler.setKeyInterest(0);
}
if(state.compareAndSet(this,ReadyState.CONNECTING,ReadyState.OPEN)){
this.idleTimestamp = timestamp;
currentTimeOut = ctx.getIdleTimeoutSeconds() * 1000000000L;
SelectThread.workers.execute(new Runnable() {
public void run() {
fireOpen(hasdata);
}});
return;
}
}
private final void fireOpen(boolean dataremainstoread){
try{
ctx.eventlistener.onOpen(this);
if (dataremainstoread){
selthread.offerTask(reEnableReadInterest);
}
}catch(Throwable t){
handleListenerException(t);
}
}
/**
*
* @param timestamp
*/
public final void idleCheck(long timestamp){
final long currentTimeOut_ = currentTimeOut;
if (currentTimeOut_ > 0 && (idleTimestamp+currentTimeOut_)-timestamp<0){
close(new TimedOutException(timestamp),true);
}
}
private final void parseFrame() throws Throwable{
final int flimit = ctx.maxDataFramelengthBytes;
final byte[] ba = readByteArray;
final ByteBuffer readbuf = readBuffer;
final int pos = readbuf.position();
int parsed_ = parsed;
int fstart = framestart;
int bend = bindataend;
while(parsed_ < pos ){
if (frametype == -1){
frametype = (byte) (ba[fstart = parsed_++] & BINARYTYPE);
continue;
}
if (frametype == TEXTTYPE){
parsed_ = indexOf(parsed_,TEXTterminate,ba,pos);
final boolean done = parsed_++ > 0;
if (!done){
parsed_ = pos;
}
final int length = parsed_ - fstart;
if (length <= flimit){
if (done && frameisdone(fstart, length, ba)){
break;
}
continue;
}
throw new WebSocketClientSentTooLargeFrame(length,flimit);
}
if (frametype == BINARYTYPE){
while(bend == 0 && parsed_ < pos){
int v = ba[parsed_];
if ((v&0x80)==0x80){
v &= 0x7f;
}else{
bend = 1+parsed_;
}
final int length = ++parsed_-fstart+
(bindatalength = (bindatalength<<7) + v);
if (length <= flimit){
if (bend > 0){
bend += bindatalength;
bindatalength = 0;
break;
}
continue;
}
throw new WebSocketClientSentTooLargeFrame(length,flimit);
}
if (bend > 0 && bend <= pos ){
parsed_ = bend;
bend = 0;
bindataend = 0;
if (frameisdone(fstart,parsed_-fstart,ba)){
break;
}
continue;
}
break;
}
throw new BadWebSocketFrameTypeFromClientException(frametype);
}
if (frametype == -1){
parsed = 0;
readbuf.clear();
return;
}
final int minIncrease =(bend > 0 ? bend-fstart : Math.max(flimit,4096))
-ba.length
//ensuring theres at least +x bytes to read into beyond framedata needed
+ 10000;
readbuf.limit(pos).position(fstart);
if (minIncrease <= 0){
readbuf.compact();
}else{
ByteBuffer bb = ByteBuffer.allocate(ba.length + minIncrease);
readByteArray = bb.put(readbuf).array();
readBuffer = bb;
}
parsed_ -= fstart;
if (bend > 0){
bindataend = bend-fstart;
}
parsed = parsed_;
framestart = 0;
if (readBuffer.remaining()<10000)//TODO remove when QA done
throw new RuntimeException(this.toString());
}
/**
*
* @param framestart
* @param length
* @param ba
* @return true if reads are throttled
*/
private final boolean frameisdone(int framestart,int length,byte[] ba) {
//System.err.println((handshake instanceof WebsocketServerHandshake)+" ** fstart:"+framestart+" fle: "+length+" readbuf: "+readBuffer);
final ByteBuffer bbf = ByteBuffer.allocate(length);
bbf.put(ba,framestart,length);
bbf.flip();
//TODO validate UTF8: user configurable if to trust data or validate.
final DataFrame frame = new DataFrame(frametype==TEXTTYPE, bbf);
frametype = -1;
final int memsize = length + WebSocketContext.memOverheadPerReadFrame;
final boolean throttled = bufferedOnMessageRAMbytes.addAndGet(memsize)
> ctx.dataFrameReadQueueLimitBytes;
if (throttled){
readDataThrottledCounter++;
ctx.readDataThrottledCounter.incrementAndGet();
iohandler.removeKeyInterest(SelectionKey.OP_READ);
}
//TODO: make package private DataFrame subclass thats Runnable
// IF the mem footprint is not increased (the added websocketimpl ref
// fits in the remaining alingnment overhead in 64bit mode) ?
SelectThread.workers.execute(new Runnable() {
public void run() {
fireOnMessage(frame,memsize,throttled);
}
});
return throttled;
}
/**
* TODO: Need to test: Re enable read earlier then queu eempty, at half full
* and calculate consume before the onMessage call.
* @param frame
*/
private final void fireOnMessage(
DataFrame frame,int memsize,boolean throttled){
if(bufferedOnMessageRAMbytes.addAndGet(-memsize) == 0 && throttled){
selthread.offerTask(reEnableReadInterest);
}
try{
ctx.eventlistener.onMessage(this, frame);
}catch(Throwable t){
handleListenerException(t);
}
}
@Override
public final boolean send(DataFrame dataframe) {
if (dataframe == null){
close(new IllegalArgumentException("dataframe is null"),false);
return false;
}
return send(dataframe.rawFrameData);
}
@Override
public final boolean send(String textUTF8){
try{
if (textUTF8 == null){
throw new IllegalArgumentException("textUTF8 is null");
}
return send(WebSocketUtils.encode(textUTF8));
}catch(Throwable t){
close(t,false);
}
return false;
}
/**
*
* @param rawframe with position at 0
* @return
*/
@Override
public final boolean send(ByteBuffer rawframe){
try {
final ReadyState rs = readystate;
if (rs == ReadyState.OPEN){
if (rawframe == null){
throw new IllegalArgumentException("rawdataframe is null");
}
int tosend = rawframe.remaining();
final int buffsize = bufferedSendBytes.addAndGet(tosend);
final int bufflimit = ctx.dataFrameSendQueueLimitBytes;
if (bufflimit-buffsize < 0 ){
throw new WebSocketWriteQueueOverflow(buffsize,bufflimit);
}
if (tosend == buffsize){
final boolean notdone = !iohandler.write(rawframe);
if (notdone){
currentwrite = rawframe.slice();
tosend -= rawframe.remaining() + 1;
}
if (bufferedSendBytes.addAndGet(-tosend) > 0
&& (notdone || !drainWriteQueue(100))){
iohandler.writeInterestTaskOffer();
}
rawframe.rewind();// frame is ready for another send
return true;
}
writeQueue.add(rawframe.duplicate());
return true;
}
if (rs == ReadyState.CONNECTING){
throw new NotYetConnectedException();
}
}catch(java.nio.BufferOverflowException boe){
close(TCPIOhandler.otherpeerclosed,false);
}catch (Throwable ex) {
close(ex,false);
}
rawframe.rewind();// frame is ready for another send
return false;
}
private final boolean drainWriteQueue(int maxwrites) throws IOException{
ByteBuffer bb = currentwrite;
boolean cw = bb != null;
if (!cw){
bb = writeQueue.poll();
}
int written = 0;
while(bb != null){
final int a = bb.position();
final boolean done = iohandler.write(bb);
written += bb.position() - a;
if (done){
if (cw){
cw = false;
currentwrite = null;
written++;
}
/*if (bufferedSendBytes.addAndGet(-written) == 0)
return true;
written = 0;*/
bb = --maxwrites>0 ? writeQueue.poll() : null;
continue;
}
if (!cw){
written--;
currentwrite = bb;
}
break;
}
return bufferedSendBytes.addAndGet(-written) == 0;
}
private final void doSelectThreadWrite(){
try{
if (drainWriteQueue(50)){
iohandler.removeKeyInterest(SelectionKey.OP_WRITE);
}
}catch(java.nio.BufferOverflowException boe){
close(TCPIOhandler.otherpeerclosed,true);
}catch(Throwable t){
close(t,true);
}
}
@Override
public final void close(){
close(new WebSocketPublicClosedMethod(),false);
}
@Override
public final Throwable getClosedCause() {
return closedCause;
}
public void close(Throwable cause) {
close(cause,true);
}
/**
* fires eventlistener.onClosed if readystate is not already closed
* @param cause
*/
private final void close(Throwable cause,boolean callerIsSelectThread){
if (state.getAndSet(this,ReadyState.CLOSED) != ReadyState.CLOSED){
if (cause == null){
cause = new IllegalArgumentException(
"Throwable cause is null. thats a bug and must be fixed,"+
" the cause of closed state is now unknown.");
}
closedCause = cause;
if (cause instanceof WebSocketWriteQueueOverflow){
ctx.writeDataThrottledCounter.incrementAndGet();
}
//log is internal synchronized.hence removed for now.
/*if (needsLoging(cause) || needsLoging(cause.getCause())){
logger.log(Level.SEVERE,"websocket abnormal close cause",cause);
}*/
if (callerIsSelectThread){
if (ctx.doOnCloseEventsInThreadPool){
SelectThread.workers.execute(new Runnable() {
public void run() {
fireClosed();
}});
}else{
fireClosed();
}
doclose();
return;
}
selthread.tasksforSelectThread.offer(new Runnable(){
public void run() {
doclose();//shares lock with selector.select();
}});
selthread.wakeUpSelector();
fireClosed();
}
}
private boolean needsLoging(Throwable t){
return t instanceof Error || t instanceof RuntimeException ;
//&& !(t instanceof java.nio.BufferOverflowException);
}
private final void doclose(){
try {
iohandler.close();
}catch(IOException ie){ }
catch (Throwable t){
logger.log(Level.SEVERE,"WebSocket.doclose()",t);
}
}
private final void fireClosed(){
try{
ctx.eventlistener.onClosed(this);
}catch(Throwable t){
handleListenerException(t);
}
}
private final void handleListenerException(Throwable t){
ctx.eventListenerExceptionCounter.incrementAndGet();
logger.log(Level.SEVERE,"WebSocketLister failed",t);
}
@Override
public String toString(){
return getClass().getSimpleName()
+(handshake!=null?" "+handshake:"")+"\r\n"
+" READYSTATE: "+readystate+(readystate==readystate.CLOSED?(" cause: "+
closedCause):"")+ " idletimeout current state:"+
currentTimeOut/1000000000+" seconds"+"\r\n"
+"parsed:"+parsed+" readbuffer:"+readBuffer+"\r\n"
+"frametype:"+frametype+" framestart:"+framestart+" bindataend:"
+bindataend+" bindatalength:"+bindatalength+"\r\n"
/*+" idletimeout:"+idleTimeout+(idleTimeout!=
WebSocketContext.TIMEOUTDISABLEDVALUE?" seconds:"+
(idleTimeout/1000000000):"")+"\r\n"
+" idleTimestamp:"+idleTimestamp
+" handshaketimeout:"+handshakeTimeout+
(handshakeTimeout!=WebSocketContext.TIMEOUTDISABLEDVALUE?
" seconds:"+(handshakeTimeout/1000000000):"")*/
+" bufferedSendBytes:"+bufferedSendBytes.get()+
" currentwrite:"+currentwrite+"\r\n"
+" bufferedOnMessageRAMbytes:"+bufferedOnMessageRAMbytes+
" readDataThrottledCounter:"+readDataThrottledCounter+"\r\n"
+" iohandler: "+iohandler
;
}
/**
* Would be nice if ByteBuffer had an indexOf method.
* @param start
* @param find
* @param ba
* @param limit
* @return
*/
protected final static int indexOf(int start,int find,byte[] ba,int limit){
for (int i=start;i "+limit;
}
}
public final class WebSocketClientSentTooLargeFrame extends Nostacktrace{
public final int length,limit;
public WebSocketClientSentTooLargeFrame(int length, int limit) {
this.length = length;
this.limit = limit;
}
@Override
public String getMessage() {
return length+">"+limit;
}
}
public final class BadWebSocketFrameTypeFromClientException
extends Nostacktrace{
public final byte frametypebyte;
public BadWebSocketFrameTypeFromClientException(byte frametype) {
frametypebyte = frametype;
}
@Override
public String getMessage() {
return frametypebyte+" != "+TEXTTYPE+" or "+BINARYTYPE;
}
}
public final class TimedOutException extends Nostacktrace{
public final int idleMillisec;
public TimedOutException(long timestamp) {
idleMillisec =(int)
((-((idleTimestamp+currentTimeOut)-timestamp)+currentTimeOut)
/1000000);
}
@Override
public String getMessage() {
return idleMillisec +" milliSec idle";
}
}
protected static class Nostacktrace extends Exception{
public Nostacktrace(){
}
public Nostacktrace(String msg) {
super(msg);
}
@Override
public Throwable fillInStackTrace() {
return this;
}
}
protected final static class IOExceptionWrap extends IOException{
public IOExceptionWrap(Throwable t) {
initCause(t);
}
}
/* @SuppressWarnings("unchecked")
final class MySwapList {
T[] producerItems = (T[]) new Object[8];
int producedCounter;
T[] consumerItems = (T[]) new Object[8];
int consumerCounter = 1;
int consumerDatalength;
public MySwapList() {
}
public final synchronized boolean add(T obj){
if (++producedCounter == producerItems.length){
T[] list2 = (T[])new Object[producerItems.length*2];
System.arraycopy(producerItems,0,list2 ,0,producerItems.length);
producerItems = list2;
}
producerItems[producedCounter] = obj;
return true;
}
public final synchronized void setProducerfirstElement(T obj){
producerItems[0] = obj;
}
final synchronized T[] swaplists(){
final T[] list = producerItems;
producerItems = consumerItems;
consumerDatalength = producedCounter;
producedCounter = 0;
return list;
}
public final void remove() {
consumerItems[consumerCounter++] = null;
}
public final T peek(){
int i = consumerCounter;
if (i > consumerDatalength){
if (consumerItems.length > 32){
consumerItems = (T[]) new Object[32];
}
consumerCounter = i =
(consumerItems = swaplists())[0] != null ? 0 : 1;
}
return consumerItems[i];
}
public synchronized void reset(){
this.consumerCounter = 1;
this.consumerDatalength = 0;
}
}*/
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy