diff options
author | Benoit Foucher <benoit@zeroc.com> | 2007-11-27 11:58:35 +0100 |
---|---|---|
committer | Benoit Foucher <benoit@zeroc.com> | 2007-11-27 11:58:35 +0100 |
commit | 47f800495093fd7679a315e2d730fea22f6135b7 (patch) | |
tree | a7b8d3488f3841367dd03d10cae293f36fd10481 /java/src | |
parent | Fixed SystemException to no longer derive from LocalException (diff) | |
download | ice-47f800495093fd7679a315e2d730fea22f6135b7.tar.bz2 ice-47f800495093fd7679a315e2d730fea22f6135b7.tar.xz ice-47f800495093fd7679a315e2d730fea22f6135b7.zip |
- Added support for non-blocking AMI/batch requests, connection
creation.
- Added support for AMI oneway requests.
- Changed collocation optimization to not perform any DNS lookups.
Diffstat (limited to 'java/src')
66 files changed, 7367 insertions, 3050 deletions
diff --git a/java/src/Freeze/ObjectStore.java b/java/src/Freeze/ObjectStore.java index bf176a36dff..df7593db2c2 100644 --- a/java/src/Freeze/ObjectStore.java +++ b/java/src/Freeze/ObjectStore.java @@ -313,9 +313,9 @@ class ObjectStore implements IceUtil.Store { IceInternal.BasicStream os = new IceInternal.BasicStream(Ice.Util.getInstance(communicator)); v.__write(os); - java.nio.ByteBuffer buf = os.prepareWrite(); - byte[] r = new byte[buf.limit()]; - buf.get(r); + IceInternal.Buffer buf = os.prepareWrite(); + byte[] r = new byte[buf.size()]; + buf.b.get(r); return r; } @@ -324,10 +324,10 @@ class ObjectStore implements IceUtil.Store { IceInternal.BasicStream is = new IceInternal.BasicStream(Ice.Util.getInstance(communicator)); is.resize(b.length, true); - java.nio.ByteBuffer buf = is.prepareRead(); - buf.position(0); - buf.put(b); - buf.position(0); + IceInternal.Buffer buf = is.getBuffer(); + buf.b.position(0); + buf.b.put(b); + buf.b.position(0); Ice.Identity key = new Ice.Identity(); key.__read(is); return key; @@ -341,9 +341,9 @@ class ObjectStore implements IceUtil.Store v.__write(os); os.writePendingObjects(); os.endWriteEncaps(); - java.nio.ByteBuffer buf = os.prepareWrite(); - byte[] r = new byte[buf.limit()]; - buf.get(r); + IceInternal.Buffer buf = os.prepareWrite(); + byte[] r = new byte[buf.size()]; + buf.b.get(r); return r; } @@ -353,10 +353,10 @@ class ObjectStore implements IceUtil.Store IceInternal.BasicStream is = new IceInternal.BasicStream(Ice.Util.getInstance(communicator)); is.sliceObjects(false); is.resize(b.length, true); - java.nio.ByteBuffer buf = is.prepareRead(); - buf.position(0); - buf.put(b); - buf.position(0); + IceInternal.Buffer buf = is.getBuffer(); + buf.b.position(0); + buf.b.put(b); + buf.b.position(0); ObjectRecord rec= new ObjectRecord(); is.startReadEncaps(); rec.__read(is); diff --git a/java/src/Ice/AMI_Object_ice_flushBatchRequests.java b/java/src/Ice/AMI_Object_ice_flushBatchRequests.java new file mode 100644 index 00000000000..ca68d1c2382 --- /dev/null +++ b/java/src/Ice/AMI_Object_ice_flushBatchRequests.java @@ -0,0 +1,35 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2007 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +package Ice; + +public abstract class AMI_Object_ice_flushBatchRequests extends IceInternal.BatchOutgoingAsync +{ + public abstract void ice_exception(LocalException ex); + + public final void __invoke(Ice.ObjectPrx prx) + { + Ice._ObjectDel delegate; + IceInternal.RequestHandler handler; + try + { + Ice.ObjectPrxHelperBase proxy = (Ice.ObjectPrxHelperBase)prx; + __prepare(proxy.__reference().getInstance()); + delegate = proxy.__getDelegate(true); + handler = delegate.__getRequestHandler(); + } + catch(Ice.LocalException ex) + { + __finished(ex); + return; + } + + handler.flushAsyncBatchRequests(this); + } +} diff --git a/java/src/Ice/ConnectionI.java b/java/src/Ice/ConnectionI.java index e1990c041a7..efdb68da8c1 100644 --- a/java/src/Ice/ConnectionI.java +++ b/java/src/Ice/ConnectionI.java @@ -9,68 +9,118 @@ package Ice; -public final class ConnectionI extends IceInternal.EventHandler implements Connection +public final class ConnectionI extends IceInternal.EventHandler + implements Connection, IceInternal.SelectorThread.SocketReadyCallback { + public interface StartCallback + { + void connectionStartCompleted(ConnectionI connection); + void connectionStartFailed(ConnectionI connection, Ice.LocalException ex); + } + public void - validate() + start(StartCallback callback) { - if(!_endpoint.datagram()) // Datagram connections are always implicitly validated. + try { - boolean active; - synchronized(this) { - if(_thread != null && _thread != Thread.currentThread()) + _startCallback = callback; + + // + // The connection might already be closed if the communicator was destroyed. + // + if(_state == StateClosed) { - // - // In thread per connection mode, this connection's thread - // will take care of connection validation. Therefore all we - // have to do here is to wait until this thread has completed - // validation. - // - while(_state == StateNotValidated) + assert(_exception != null); + throw _exception; + } + + // + // In thread per connection mode, we create the thread for the connection. The + // intialization and validation of the connection is taken care of by the thread + // per connection. If a callback is given, no need to wait, the thread will notify + // the callback, otherwise wait until the connection is validated. + // + if(_threadPerConnection) + { + try { - try + _thread = new ThreadPerConnection(); + _thread.start(); + } + catch(java.lang.Exception ex) + { + java.io.StringWriter sw = new java.io.StringWriter(); + java.io.PrintWriter pw = new java.io.PrintWriter(sw); + ex.printStackTrace(pw); + pw.flush(); + _logger.error("cannot create thread for connection:\n" + sw.toString()); + + // + // Clean up. + // + _thread = null; + _state = StateClosed; + + Ice.SyscallException e = new Ice.SyscallException(); + e.initCause(ex); + throw e; + } + + if(callback == null) // Wait for the connection to be validated. + { + while(_state <= StateNotValidated) { - wait(); + try + { + wait(); + } + catch(InterruptedException ex) + { + } } - catch(InterruptedException ex) + + if(_state >= StateClosing) { + assert(_exception != null); + throw _exception; } } - - if(_state >= StateClosing) - { - assert(_exception != null); - throw _exception; - } - - return; + return; // We're done. } + } - // - // The connection might already be closed (e.g.: the communicator - // was destroyed or object adapter deactivated.) - // - assert(_state == StateNotValidated || _state == StateClosed); + assert(!_threadPerConnection); + + // + // Initialize the connection transceiver and then validate the connection. + // + IceInternal.SocketStatus status = initialize(); + if(status == IceInternal.SocketStatus.Finished) + { + status = validate(); + } + + if(status == IceInternal.SocketStatus.Finished) + { + finishStart(null); + return; // We're done! + } + + // + // If the initialization or validation couldn't be completed without potentially + // blocking, we register the connection with the selector thread and return. + // + + synchronized(this) + { if(_state == StateClosed) { assert(_exception != null); throw _exception; } - - if(_adapter != null) - { - active = true; // The server side has the active role for connection validation. - } - else - { - active = false; // The client side has the passive role for connection validation. - } - } - try - { int timeout; IceInternal.DefaultsAndOverrides defaultsAndOverrides = _instance.defaultsAndOverrides(); if(defaultsAndOverrides.overrideConnectTimeout) @@ -81,159 +131,76 @@ public final class ConnectionI extends IceInternal.EventHandler implements Conne { timeout = _endpoint.timeout(); } + + _sendInProgress = true; + _selectorThread._register(_transceiver.fd(), this, status, timeout); + } + } + catch(Ice.LocalException ex) + { + synchronized(this) + { + setState(StateClosed, ex); - if(active) + // + // If start is called with a callback, the callback is notified either by the + // thread per conncetion or the thread pool. + // + if(callback != null) { - synchronized(_sendMutex) + if(!_threadPerConnection) { - if(_transceiver == null) // Has the transceiver already been closed? - { - assert(_exception != null); - throw _exception; // The exception is immutable at this point. - } - - IceInternal.BasicStream os = new IceInternal.BasicStream(_instance); - os.writeBlob(IceInternal.Protocol.magic); - os.writeByte(IceInternal.Protocol.protocolMajor); - os.writeByte(IceInternal.Protocol.protocolMinor); - os.writeByte(IceInternal.Protocol.encodingMajor); - os.writeByte(IceInternal.Protocol.encodingMinor); - os.writeByte(IceInternal.Protocol.validateConnectionMsg); - os.writeByte((byte)0); // Compression status (always zero for validate connection). - os.writeInt(IceInternal.Protocol.headerSize); // Message size. - IceInternal.TraceUtil.traceHeader("sending validate connection", os, _logger, _traceLevels); - try - { - _transceiver.write(os, timeout); - } - catch(Ice.TimeoutException ex) - { - throw new Ice.ConnectTimeoutException(); - } + registerWithPool(); + unregisterWithPool(); // Let finished() do the close. } + return; } - else + + // + // Close the transceiver if there's no thread per connection. Otherwise, wait + // for the thread per connection to take care of it. + // + if(_thread == null && _transceiver != null) { - IceInternal.BasicStream is = new IceInternal.BasicStream(_instance); - is.resize(IceInternal.Protocol.headerSize, true); - is.pos(0); try { - _transceiver.read(is, timeout); - } - catch(Ice.TimeoutException ex) - { - throw new Ice.ConnectTimeoutException(); - } - assert(is.pos() == IceInternal.Protocol.headerSize); - is.pos(0); - byte[] m = is.readBlob(4); - if(m[0] != IceInternal.Protocol.magic[0] || m[1] != IceInternal.Protocol.magic[1] || - m[2] != IceInternal.Protocol.magic[2] || m[3] != IceInternal.Protocol.magic[3]) - { - BadMagicException ex = new BadMagicException(); - ex.badMagic = m; - throw ex; - } - byte pMajor = is.readByte(); - byte pMinor = is.readByte(); - if(pMajor != IceInternal.Protocol.protocolMajor) - { - UnsupportedProtocolException e = new UnsupportedProtocolException(); - e.badMajor = pMajor < 0 ? pMajor + 255 : pMajor; - e.badMinor = pMinor < 0 ? pMinor + 255 : pMinor; - e.major = IceInternal.Protocol.protocolMajor; - e.minor = IceInternal.Protocol.protocolMinor; - throw e; - } - byte eMajor = is.readByte(); - byte eMinor = is.readByte(); - if(eMajor != IceInternal.Protocol.encodingMajor) - { - UnsupportedEncodingException e = new UnsupportedEncodingException(); - e.badMajor = eMajor < 0 ? eMajor + 255 : eMajor; - e.badMinor = eMinor < 0 ? eMinor + 255 : eMinor; - e.major = IceInternal.Protocol.encodingMajor; - e.minor = IceInternal.Protocol.encodingMinor; - throw e; - } - byte messageType = is.readByte(); - if(messageType != IceInternal.Protocol.validateConnectionMsg) - { - throw new ConnectionNotValidatedException(); + _transceiver.close(); } - byte compress = is.readByte(); // Ignore compression status for validate connection. - int size = is.readInt(); - if(size != IceInternal.Protocol.headerSize) + catch(LocalException e) { - throw new IllegalMessageSizeException(); + // Here we ignore any exceptions in close(). } - IceInternal.TraceUtil.traceHeader("received validate connection", is, _logger, _traceLevels); - } - } - catch(IceInternal.LocalExceptionWrapper ex) // Java-specific workaround in Transceiver.write(). - { - synchronized(this) - { - setState(StateClosed, ex.get()); - assert(_exception != null); - throw _exception; - } - } - catch(LocalException ex) - { - synchronized(this) - { - setState(StateClosed, ex); - assert(_exception != null); - throw _exception; + _transceiver = null; } } - } - - synchronized(this) - { - if(_acmTimeout > 0) - { - _acmAbsoluteTimeoutMillis = IceInternal.Time.currentMonotonicTimeMillis() + _acmTimeout * 1000; - } - - // - // We start out in holding state. - // - setState(StateHolding); + + waitUntilFinished(); + throw ex; } } public synchronized void activate() { - while(_state == StateNotValidated) + if(_state <= StateNotValidated) { - try - { - wait(); - } - catch(InterruptedException ex) - { - } + return; } + if(_acmTimeout > 0) + { + _acmAbsoluteTimeoutMillis = IceInternal.Time.currentMonotonicTimeMillis() + _acmTimeout * 1000; + } + setState(StateActive); } public synchronized void hold() { - while(_state == StateNotValidated) + if(_state <= StateNotValidated) { - try - { - wait(); - } - catch(InterruptedException ex) - { - } + return; } setState(StateHolding); @@ -243,60 +210,92 @@ public final class ConnectionI extends IceInternal.EventHandler implements Conne public final static int ObjectAdapterDeactivated = 0; public final static int CommunicatorDestroyed = 1; - public synchronized void + public void destroy(int reason) { - switch(reason) + boolean send = false; + synchronized(this) { - case ObjectAdapterDeactivated: + switch(reason) { - setState(StateClosing, new ObjectAdapterDeactivatedException()); - break; + case ObjectAdapterDeactivated: + { + send = setState(StateClosing, new ObjectAdapterDeactivatedException()); + break; + } + + case CommunicatorDestroyed: + { + send = setState(StateClosing, new CommunicatorDestroyedException()); + break; + } } - - case CommunicatorDestroyed: + } + + if(send) + { + try { - setState(StateClosing, new CommunicatorDestroyedException()); - break; + finishSendMessage(); + } + catch(Ice.LocalException ex) + { + // Ignore. } } } - public synchronized void + public void close(boolean force) { - if(force) - { - setState(StateClosed, new ForcedCloseConnectionException()); - } - else + boolean send = false; + synchronized(this) { - // - // If we do a graceful shutdown, then we wait until all - // outstanding requests have been completed. Otherwise, - // the CloseConnectionException will cause all outstanding - // requests to be retried, regardless of whether the - // server has processed them or not. - // - while(!_requests.isEmpty() || !_asyncRequests.isEmpty()) + if(force) { - try - { - wait(); - } - catch(InterruptedException ex) + setState(StateClosed, new ForcedCloseConnectionException()); + } + else + { + // + // If we do a graceful shutdown, then we wait until all + // outstanding requests have been completed. Otherwise, + // the CloseConnectionException will cause all outstanding + // requests to be retried, regardless of whether the + // server has processed them or not. + // + while(!_requests.isEmpty() || !_asyncRequests.isEmpty()) { + try + { + wait(); + } + catch(InterruptedException ex) + { + } } + + send = setState(StateClosing, new CloseConnectionException()); } + } - setState(StateClosing, new CloseConnectionException()); + if(send) + { + try + { + finishSendMessage(); + } + catch(Ice.LocalException ex) + { + // Ignore. + } } } public synchronized boolean - isDestroyed() + isActiveOrHolding() { - return _state >= StateClosing; + return _state > StateNotValidated && _state < StateClosing; } public boolean @@ -461,56 +460,57 @@ public final class ConnectionI extends IceInternal.EventHandler implements Conne } } - public synchronized void + public void monitor() { - if(_state != StateActive) + boolean send = false; + synchronized(this) { - return; - } + if(_state != StateActive) + { + return; + } - // - // Check for timed out async requests. - // - java.util.Iterator i = _asyncRequests.entryIterator(); - while(i.hasNext()) - { - IceInternal.IntMap.Entry e = (IceInternal.IntMap.Entry)i.next(); - IceInternal.OutgoingAsync out = (IceInternal.OutgoingAsync)e.getValue(); - if(out.__timedOut()) + // + // Active connection management for idle connections. + // + if(_acmTimeout <= 0 || + !_requests.isEmpty() || !_asyncRequests.isEmpty() || + _batchStreamInUse || !_batchStream.isEmpty() || + _sendInProgress || _dispatchCount > 0) { - setState(StateClosed, new TimeoutException()); return; } + + if(IceInternal.Time.currentMonotonicTimeMillis() >= _acmAbsoluteTimeoutMillis) + { + send = setState(StateClosing, new ConnectionTimeoutException()); + } } - // - // Active connection management for idle connections. - // - if(_acmTimeout > 0 && - _requests.isEmpty() && _asyncRequests.isEmpty() && - !_batchStreamInUse && _batchStream.isEmpty() && - _dispatchCount == 0) + if(send) { - if(IceInternal.Time.currentMonotonicTimeMillis() >= _acmAbsoluteTimeoutMillis) + try { - setState(StateClosing, new ConnectionTimeoutException()); - return; + finishSendMessage(); } - } + catch(Ice.LocalException ex) + { + // Ignore. + } + } } - public void - sendRequest(IceInternal.BasicStream os, IceInternal.Outgoing out, boolean compress) + public boolean + sendRequest(IceInternal.Outgoing out, boolean compress, boolean response) throws IceInternal.LocalExceptionWrapper { int requestId = 0; - IceInternal.BasicStream stream = null; + final IceInternal.BasicStream os = out.os(); + boolean send = false; synchronized(this) { - assert(!(out != null && _endpoint.datagram())); // Twoway requests cannot be datagrams. - if(_exception != null) { // @@ -524,10 +524,7 @@ public final class ConnectionI extends IceInternal.EventHandler implements Conne assert(_state > StateNotValidated); assert(_state < StateClosing); - // - // Only add to the request map if this is a twoway call. - // - if(out != null) + if(response) { // // Create a new unique request ID. @@ -544,127 +541,67 @@ public final class ConnectionI extends IceInternal.EventHandler implements Conne // os.pos(IceInternal.Protocol.headerSize); os.writeInt(requestId); + } + + // + // Send the message. If it can't be sent without blocking the message is added + // to _sendStreams and it will be sent by the selector thread or by this thread + // if flush is true. + // + try + { + send = sendMessage(new OutgoingMessage(out, out.os(), compress, response), false); + } + catch(Ice.LocalException ex) + { + setState(StateClosed, ex); + assert(_exception != null); + throw _exception; + } + if(response) + { // // Add to the requests map. // _requests.put(requestId, out); } - stream = doCompress(os, _overrideCompress ? _overrideCompressValue : compress); - - if(_acmTimeout > 0) + if(!send) { - _acmAbsoluteTimeoutMillis = IceInternal.Time.currentMonotonicTimeMillis() + _acmTimeout * 1000; + return !_sendInProgress && _queuedStreams.isEmpty(); // The request was sent if it's not queued! } } - try + if(send) { - synchronized(_sendMutex) - { - if(_transceiver == null) // Has the transceiver already been closed? - { - assert(_exception != null); - throw _exception; // The exception is immutable at this point. - } - - // - // Send the request. - // - IceInternal.TraceUtil.traceRequest("sending request", os, _logger, _traceLevels); - _transceiver.write(stream, _endpoint.timeout()); - } - } - catch(IceInternal.LocalExceptionWrapper ex) // Java-specific workaround in Transceiver.write(). - { - synchronized(this) + try { - setState(StateClosed, ex.get()); - assert(_exception != null); - - if(out != null) - { - // - // If the request has already been removed from - // the request map, we are out of luck. It would - // mean that finished() has been called already, - // and therefore the exception has been set using - // the Outgoing::finished() callback. In this - // case, we cannot throw the exception here, - // because we must not both raise an exception and - // have Outgoing::finished() called with an - // exception. This means that in some rare cases, - // a request will not be retried even though it - // could. But I honestly don't know how I could - // avoid this, without a very elaborate and - // complex design, which would be bad for - // performance. - // - IceInternal.Outgoing o = (IceInternal.Outgoing)_requests.remove(requestId); - if(o != null) - { - assert(o == out); - throw new IceInternal.LocalExceptionWrapper(_exception, ex.retry()); - } - } - else - { - throw new IceInternal.LocalExceptionWrapper(_exception, ex.retry()); - } + finishSendMessage(); } - } - catch(LocalException ex) - { - synchronized(this) + catch(Ice.LocalException ex) { - setState(StateClosed, ex); assert(_exception != null); - - if(out != null) - { - // - // If the request has already been removed from - // the request map, we are out of luck. It would - // mean that finished() has been called already, - // and therefore the exception has been set using - // the Outgoing::finished() callback. In this - // case, we cannot throw the exception here, - // because we must not both raise an exception and - // have Outgoing::finished() called with an - // exception. This means that in some rare cases, - // a request will not be retried even though it - // could. But I honestly don't know how I could - // avoid this, without a very elaborate and - // complex design, which would be bad for - // performance. - // - IceInternal.Outgoing o = (IceInternal.Outgoing)_requests.remove(requestId); - if(o != null) - { - assert(o == out); - throw _exception; - } - } - else + if(!response) // Twoway calls are notified through finished() { - throw _exception; + throw ex; } } } + + return true; // The request was sent. } public void - sendAsyncRequest(IceInternal.BasicStream os, IceInternal.OutgoingAsync out, boolean compress) + sendAsyncRequest(IceInternal.OutgoingAsync out, boolean compress, boolean response) throws IceInternal.LocalExceptionWrapper { int requestId = 0; - IceInternal.BasicStream stream = null; + final IceInternal.BasicStream os = out.__os(); + boolean send; synchronized(this) { - assert(!_endpoint.datagram()); // Twoway requests cannot be datagrams, and async implies twoway. - if(_exception != null) { // @@ -678,109 +615,57 @@ public final class ConnectionI extends IceInternal.EventHandler implements Conne assert(_state > StateNotValidated); assert(_state < StateClosing); - // - // Create a new unique request ID. - // - requestId = _nextRequestId++; - if(requestId <= 0) + if(response) { - _nextRequestId = 1; + // + // Create a new unique request ID. + // requestId = _nextRequestId++; + if(requestId <= 0) + { + _nextRequestId = 1; + requestId = _nextRequestId++; + } + + // + // Fill in the request ID. + // + os.pos(IceInternal.Protocol.headerSize); + os.writeInt(requestId); } - - // - // Fill in the request ID. - // - os.pos(IceInternal.Protocol.headerSize); - os.writeInt(requestId); - - // - // Add to the async requests map. - // - _asyncRequests.put(requestId, out); - - stream = doCompress(os, _overrideCompress ? _overrideCompressValue : compress); - if(_acmTimeout > 0) + try { - _acmAbsoluteTimeoutMillis = IceInternal.Time.currentMonotonicTimeMillis() + _acmTimeout * 1000; + send = sendMessage(new OutgoingMessage(out, out.__os(), compress, response), false); } - } - - try - { - synchronized(_sendMutex) + catch(Ice.LocalException ex) { - if(_transceiver == null) // Has the transceiver already been closed? - { - assert(_exception != null); - throw _exception; // The exception is immutable at this point. - } + setState(StateClosed, ex); + assert(_exception != null); + throw _exception; + } + if(response) + { // - // Send the request. + // Add to the async requests map. // - IceInternal.TraceUtil.traceRequest("sending asynchronous request", os, _logger, _traceLevels); - _transceiver.write(stream, _endpoint.timeout()); + _asyncRequests.put(requestId, out); } } - catch(IceInternal.LocalExceptionWrapper ex) // Java-specific workaround in Transceiver.write(). + + if(send) { - synchronized(this) + try { - setState(StateClosed, ex.get()); - assert(_exception != null); - - // - // If the request has already been removed from the - // async request map, we are out of luck. It would - // mean that finished() has been called already, and - // therefore the exception has been set using the - // OutgoingAsync::__finished() callback. In this case, - // we cannot throw the exception here, because we must - // not both raise an exception and have - // OutgoingAsync::__finished() called with an - // exception. This means that in some rare cases, a - // request will not be retried even though it - // could. But I honestly don't know how I could avoid - // this, without a very elaborate and complex design, - // which would be bad for performance. - // - IceInternal.OutgoingAsync o = (IceInternal.OutgoingAsync)_asyncRequests.remove(requestId); - if(o != null) - { - assert(o == out); - throw new IceInternal.LocalExceptionWrapper(_exception, ex.retry()); - } + finishSendMessage(); } - } - catch(LocalException ex) - { - synchronized(this) + catch(Ice.LocalException ex) { - setState(StateClosed, ex); assert(_exception != null); - - // - // If the request has already been removed from the - // async request map, we are out of luck. It would - // mean that finished() has been called already, and - // therefore the exception has been set using the - // OutgoingAsync::__finished() callback. In this case, - // we cannot throw the exception here, because we must - // not both raise an exception and have - // OutgoingAsync::__finished() called with an - // exception. This means that in some rare cases, a - // request will not be retried even though it - // could. But I honestly don't know how I could avoid - // this, without a very elaborate and complex design, - // which would be bad for performance. - // - IceInternal.OutgoingAsync o = (IceInternal.OutgoingAsync)_asyncRequests.remove(requestId); - if(o != null) + if(!response) // Twoway calls are notified through finished() { - assert(o == out); - throw _exception; + throw ex; } } } @@ -789,6 +674,9 @@ public final class ConnectionI extends IceInternal.EventHandler implements Conne public synchronized void prepareBatchRequest(IceInternal.BasicStream os) { + // + // Wait if flushing is currently in progress. + // while(_batchStreamInUse && _exception == null) { try @@ -834,60 +722,110 @@ public final class ConnectionI extends IceInternal.EventHandler implements Conne public void finishBatchRequest(IceInternal.BasicStream os, boolean compress) { - boolean autoflush = false; - byte[] lastRequest = null; - - synchronized(this) + boolean send = false; + try { - // - // Get the batch stream back. - // - _batchStream.swap(os); - - if(_batchAutoFlush) + synchronized(this) { - synchronized(_sendMutex) + // + // Get the batch stream back. + // + _batchStream.swap(os); + + if(_exception != null) + { + throw _exception; + } + + boolean flush = false; + if(_batchAutoFlush) { - if(_transceiver == null) - { - assert(_exception != null); - throw _exception; // The exception is immutable at this point. - } // - // Throw memory limit exception if the first - // message added causes us to go over - // limit. Otherwise put aside the marshalled - // message that caused limit to be exceeded and - // rollback stream to the marker. + // Throw memory limit exception if the first message added causes us to go over + // limit. Otherwise put aside the marshalled message that caused limit to be + // exceeded and rollback stream to the marker. try { - _transceiver.checkSendSize(_batchStream, _instance.messageSizeMax()); + _transceiver.checkSendSize(_batchStream.getBuffer(), _instance.messageSizeMax()); } catch(Ice.LocalException ex) { - if(_batchRequestNum == 0) + if(_batchRequestNum > 0) + { + flush = true; + } + else { - resetBatch(true); throw ex; } + } + } + + if(flush) + { + // + // Temporarily save the last request. + // + byte[] lastRequest = new byte[_batchStream.size() - _batchMarker]; + IceInternal.Buffer buffer = _batchStream.getBuffer(); + buffer.b.position(_batchMarker); + buffer.b.get(lastRequest); + _batchStream.resize(_batchMarker, false); - lastRequest = new byte[_batchStream.size() - _batchMarker]; - java.nio.ByteBuffer buffer = _batchStream.prepareRead(); - buffer.position(_batchMarker); - buffer.get(lastRequest); - _batchStream.resize(_batchMarker, false); - autoflush = true; + try + { + // + // Fill in the number of requests in the batch. + // + _batchStream.pos(IceInternal.Protocol.headerSize); + _batchStream.writeInt(_batchRequestNum); + + OutgoingMessage message = new OutgoingMessage(_batchStream, _batchRequestCompress, true); + send = sendMessage(message, false); + if(send) + { + // + // If the request can't be sent immediately and this is a foreground send, + // we adopt the stream to be able to re-use _batchStream immediately. + // + message.adopt(); + } + } + catch(Ice.LocalException ex) + { + setState(StateClosed, ex); + assert(_exception != null); + throw _exception; } + + // + // Reset the batch stream. + // + _batchStream = new IceInternal.BasicStream(_instance, _batchAutoFlush); + _batchRequestNum = 0; + _batchRequestCompress = false; + _batchMarker = 0; + + // + // Check again if the last request doesn't exceed the maximum message size. + // + if(IceInternal.Protocol.requestBatchHdr.length + lastRequest.length > _instance.messageSizeMax()) + { + throw new MemoryLimitException(); + } + + // + // Start a new batch with the last message that caused us to go over the limit. + // + _batchStream.writeBlob(IceInternal.Protocol.requestBatchHdr); + _batchStream.writeBlob(lastRequest); } - } - if(!autoflush) - { // // Increment the number of requests in the batch. // ++_batchRequestNum; - + // // We compress the whole batch if there is at least one compressed // message. @@ -905,258 +843,247 @@ public final class ConnectionI extends IceInternal.EventHandler implements Conne notifyAll(); } } - - if(autoflush) + catch(Ice.LocalException ex) { - // - // We have to keep _batchStreamInUse set until after we insert the - // saved marshalled data into a new stream. - // - flushBatchRequestsInternal(true); - - synchronized(this) + abortBatchRequest(); + if(send) { - // - // Throw memory limit exception if the message that caused us to go over - // limit causes us to exceed the limit by itself. - // - if(IceInternal.Protocol.requestBatchHdr.length + lastRequest.length > _instance.messageSizeMax()) - { - resetBatch(true); - throw new MemoryLimitException(); - } - - // - // Start a new batch with the last message that caused us to - // go over the limit. - // - try - { - _batchStream.writeBlob(IceInternal.Protocol.requestBatchHdr); - _batchStream.writeBlob(lastRequest); - } - catch(LocalException ex) - { - setState(StateClosed, ex); - throw ex; - } - - if(compress) - { - _batchRequestCompress = true; - } - - // - // Notify that the batch stream not in use anymore. - // - ++_batchRequestNum; - _batchStreamInUse = false; - notifyAll(); + finishSendMessage(); // Let exceptions go through to report auto-flush failures to the caller. } + throw ex; + } + + if(send) + { + finishSendMessage(); // Let exceptions go through to report auto-flush failures to the caller. } } public synchronized void abortBatchRequest() { - // - // Reset the batch stream. We cannot save old requests - // in the batch stream, as they might be corrupted due to - // incomplete marshaling. - // - resetBatch(true); + _batchStream = new IceInternal.BasicStream(_instance, _batchAutoFlush); + _batchRequestNum = 0; + _batchRequestCompress = false; + _batchMarker = 0; + + assert(_batchStreamInUse); + _batchStreamInUse = false; + notifyAll(); } public void flushBatchRequests() { - flushBatchRequestsInternal(false); + IceInternal.BatchOutgoing out = new IceInternal.BatchOutgoing(this, _instance); + out.invoke(); } - private void - flushBatchRequestsInternal(boolean ignoreInUse) + public boolean + flushBatchRequests(IceInternal.BatchOutgoing out) { - IceInternal.BasicStream stream = null; - + boolean send = false; synchronized(this) { - if(!ignoreInUse) + while(_batchStreamInUse && _exception == null) { - while(_batchStreamInUse && _exception == null) + try + { + wait(); + } + catch(InterruptedException ex) { - try - { - wait(); - } - catch(InterruptedException ex) - { - } } } - + if(_exception != null) { throw _exception; } - if(_batchStream.isEmpty()) + if(_batchRequestNum == 0) { - return; // Nothing to do. + return true; } - assert(_state > StateNotValidated); - assert(_state < StateClosing); - - // - // Fill in the message size. - // - _batchStream.pos(10); - _batchStream.writeInt(_batchStream.size()); - // // Fill in the number of requests in the batch. // + _batchStream.pos(IceInternal.Protocol.headerSize); _batchStream.writeInt(_batchRequestNum); + + _batchStream.swap(out.os()); - stream = doCompress(_batchStream, _overrideCompress ? _overrideCompressValue : _batchRequestCompress); - - if(_acmTimeout > 0) + try { - _acmAbsoluteTimeoutMillis = IceInternal.Time.currentMonotonicTimeMillis() + _acmTimeout * 1000; + OutgoingMessage message = new OutgoingMessage(out, out.os(), _batchRequestCompress, false); + send = sendMessage(message, false); } - + catch(Ice.LocalException ex) + { + setState(StateClosed, ex); + assert(_exception != null); + throw _exception; + } + // - // Prevent that new batch requests are added while we are - // flushing. + // Reset the batch stream. // - _batchStreamInUse = true; + _batchStream = new IceInternal.BasicStream(_instance, _batchAutoFlush); + _batchRequestNum = 0; + _batchRequestCompress = false; + _batchMarker = 0; + + if(!send) + { + return !_sendInProgress && _queuedStreams.isEmpty(); // The request was sent if it's not queued! + } } - try + if(send) { - synchronized(_sendMutex) + finishSendMessage(); + } + return true; + } + + public void + flushAsyncBatchRequests(IceInternal.BatchOutgoingAsync outAsync) + { + boolean send = false; + synchronized(this) + { + while(_batchStreamInUse && _exception == null) { - if(_transceiver == null) // Has the transceiver already been closed? + try + { + wait(); + } + catch(InterruptedException ex) { - assert(_exception != null); - throw _exception; // The exception is immutable at this point. } - - // - // Send the batch request. - // - IceInternal.TraceUtil.traceBatchRequest("sending batch request", _batchStream, _logger, _traceLevels); - _transceiver.write(stream, _endpoint.timeout()); } - } - catch(IceInternal.LocalExceptionWrapper ex) // Java-specific workaround in Transceiver.write(). - { - synchronized(this) + + if(_exception != null) { - setState(StateClosed, ex.get()); - assert(_exception != null); - - // - // Since batch requests are all oneways (or datagrams), we - // must report the exception to the caller. - // throw _exception; } - } - catch(LocalException ex) - { - synchronized(this) + + if(_batchRequestNum == 0) + { + return; + } + + // + // Fill in the number of requests in the batch. + // + _batchStream.pos(IceInternal.Protocol.headerSize); + _batchStream.writeInt(_batchRequestNum); + + _batchStream.swap(outAsync.__os()); + + try + { + OutgoingMessage message = new OutgoingMessage(outAsync, outAsync.__os(), _batchRequestCompress, false); + send = sendMessage(message, false); + } + catch(Ice.LocalException ex) { setState(StateClosed, ex); assert(_exception != null); - - // - // Since batch requests are all oneways (or datagrams), we - // must report the exception to the caller. - // throw _exception; } - } - - synchronized(this) - { + // - // Reset the batch stream, and notify that flushing is over. + // Reset the batch stream. // - resetBatch(!ignoreInUse); + _batchStream = new IceInternal.BasicStream(_instance, _batchAutoFlush); + _batchRequestNum = 0; + _batchRequestCompress = false; + _batchMarker = 0; } - } - private void - resetBatch(boolean resetInUse) - { - _batchStream = new IceInternal.BasicStream(_instance, _batchAutoFlush); - _batchRequestNum = 0; - _batchRequestCompress = false; - - // - // Notify about the batch stream not being in use anymore. - // - if(resetInUse) + if(send) { - assert(_batchStreamInUse); - _batchStreamInUse = false; - notifyAll(); + finishSendMessage(); } } public void sendResponse(IceInternal.BasicStream os, byte compressFlag) { - IceInternal.BasicStream stream = null; - try + boolean send = false; + synchronized(this) { - synchronized(_sendMutex) + assert(_state > StateNotValidated); + + try { - if(_transceiver == null) // Has the transceiver already been closed? + if(--_dispatchCount == 0) + { + notifyAll(); + } + + if(_state == StateClosed) { assert(_exception != null); - throw _exception; // The exception is immutable at this point. + throw _exception; } - stream = doCompress(os, compressFlag != 0); + send = sendMessage(new OutgoingMessage(os, compressFlag != 0, true), false); + + if(_state == StateClosing && _dispatchCount == 0) + { + initiateShutdown(true); + } - // - // Send the reply. - // - IceInternal.TraceUtil.traceReply("sending reply", os, _logger, _traceLevels); - _transceiver.write(stream, _endpoint.timeout()); + if(_acmTimeout > 0) + { + _acmAbsoluteTimeoutMillis = IceInternal.Time.currentMonotonicTimeMillis() + _acmTimeout * 1000; + } } - } - catch(IceInternal.LocalExceptionWrapper ex) // Java-specific workaround in Transceiver.write(). - { - synchronized(this) + catch(LocalException ex) { - setState(StateClosed, ex.get()); + setState(StateClosed, ex); } } - catch(LocalException ex) + + if(send) { - synchronized(this) + try { - setState(StateClosed, ex); + finishSendMessage(); + } + catch(Ice.LocalException ex) + { + // Ignore. } } + } + public void + sendNoResponse() + { + boolean send = false; synchronized(this) { assert(_state > StateNotValidated); - try { if(--_dispatchCount == 0) { notifyAll(); } - + + if(_state == StateClosed) + { + assert(_exception != null); + throw _exception; + } + if(_state == StateClosing && _dispatchCount == 0) { - initiateShutdown(); + send = initiateShutdown(false); } if(_acmTimeout > 0) @@ -1164,49 +1091,29 @@ public final class ConnectionI extends IceInternal.EventHandler implements Conne _acmAbsoluteTimeoutMillis = IceInternal.Time.currentMonotonicTimeMillis() + _acmTimeout * 1000; } } - catch(IceInternal.LocalExceptionWrapper ex) // Java-specific workaround in Transceiver.write(). - { - setState(StateClosed, ex.get()); - } catch(LocalException ex) { setState(StateClosed, ex); } } - } - public synchronized void - sendNoResponse() - { - assert(_state > StateNotValidated); - - try + if(send) { - if(--_dispatchCount == 0) + try { - notifyAll(); + finishSendMessage(); } - - if(_state == StateClosing && _dispatchCount == 0) + catch(Ice.LocalException ex) { - initiateShutdown(); + // Ignore. } } - catch(IceInternal.LocalExceptionWrapper ex) // Java-specific workaround in Transceiver.write(). - { - setState(StateClosed, ex.get()); - } - catch(LocalException ex) - { - setState(StateClosed, ex); - } } public IceInternal.EndpointI endpoint() { - // No mutex protection necessary, _endpoint is immutable. - return _endpoint; + return _endpoint; // No mutex protection necessary, _endpoint is immutable. } public boolean @@ -1218,11 +1125,15 @@ public final class ConnectionI extends IceInternal.EventHandler implements Conne public synchronized void setAdapter(ObjectAdapter adapter) { - if(_exception != null) + if(_state == StateClosing || _state == StateClosed) { + assert(_exception != null); throw _exception; } - + else if(_state <= StateNotValidated) + { + return; + } assert(_state < StateClosing); _adapter = adapter; @@ -1253,7 +1164,7 @@ public final class ConnectionI extends IceInternal.EventHandler implements Conne return _adapter; } - public synchronized ObjectPrx + public ObjectPrx createProxy(Identity ident) { // @@ -1262,9 +1173,8 @@ public final class ConnectionI extends IceInternal.EventHandler implements Conne // ConnectionI[] connections = new ConnectionI[1]; connections[0] = this; - IceInternal.Reference ref = - _instance.referenceFactory().create(ident, _instance.getDefaultContext(), "", - IceInternal.Reference.ModeTwoway, connections); + IceInternal.Reference ref = _instance.referenceFactory().create(ident, _instance.getDefaultContext(), "", + IceInternal.Reference.ModeTwoway, connections); return _instance.proxyFactory().referenceToProxy(ref); } @@ -1291,7 +1201,7 @@ public final class ConnectionI extends IceInternal.EventHandler implements Conne { assert(!_threadPerConnection); // Only for use with a thread pool. - return _transceiver.read(stream, 0); + return _transceiver.read(stream.getBuffer(), 0, _hasMoreData); // // Updating _acmAbsoluteTimeoutMillis is too expensive here, @@ -1301,6 +1211,12 @@ public final class ConnectionI extends IceInternal.EventHandler implements Conne // } + public boolean + hasMoreData() + { + return _hasMoreData.value; + } + public void message(IceInternal.BasicStream stream, IceInternal.ThreadPool threadPool) { @@ -1355,69 +1271,58 @@ public final class ConnectionI extends IceInternal.EventHandler implements Conne assert(!_threadPerConnection); // Only for use with a thread pool. threadPool.promoteFollower(); - - LocalException localEx = null; - - IceInternal.IntMap requests = null; - IceInternal.IntMap asyncRequests = null; + + Ice.LocalException localEx = null; synchronized(this) { --_finishedCount; - if(_finishedCount == 0 && _state == StateClosed) + if(_finishedCount > 0 || _state != StateClosed || _sendInProgress) { - // - // We must make sure that nobody is sending when we - // close the transceiver. - // - synchronized(_sendMutex) - { - try - { - _transceiver.close(); - } - catch(LocalException ex) - { - localEx = ex; - } - - _transceiver = null; - notifyAll(); - } + return; } - - if(_state == StateClosed || _state == StateClosing) + + try { - requests = _requests; - _requests = new IceInternal.IntMap(); - - asyncRequests = _asyncRequests; - _asyncRequests = new IceInternal.IntMap(); + _transceiver.close(); } + catch(LocalException ex) + { + localEx = ex; + } + + _transceiver = null; + notifyAll(); } + + finishStart(_exception); - if(requests != null) + java.util.Iterator i = _queuedStreams.iterator(); + while(i.hasNext()) { - java.util.Iterator i = requests.entryIterator(); - while(i.hasNext()) - { - IceInternal.IntMap.Entry e = (IceInternal.IntMap.Entry)i.next(); - IceInternal.Outgoing out = (IceInternal.Outgoing)e.getValue(); - out.finished(_exception); // The exception is immutable at this point. - } + OutgoingMessage message = (OutgoingMessage)i.next(); + message.finished(_exception); } + _queuedStreams.clear(); - if(asyncRequests != null) + i = _requests.entryIterator(); // _requests is immutable at this point. + while(i.hasNext()) { - java.util.Iterator i = asyncRequests.entryIterator(); - while(i.hasNext()) - { - IceInternal.IntMap.Entry e = (IceInternal.IntMap.Entry)i.next(); - IceInternal.OutgoingAsync out = (IceInternal.OutgoingAsync)e.getValue(); - out.__finished(_exception); // The exception is immutable at this point. - } + IceInternal.IntMap.Entry e = (IceInternal.IntMap.Entry)i.next(); + IceInternal.Outgoing out = (IceInternal.Outgoing)e.getValue(); + out.finished(_exception); // The exception is immutable at this point. } + _requests.clear(); + i = _asyncRequests.entryIterator(); // _asyncRequests is immutable at this point. + while(i.hasNext()) + { + IceInternal.IntMap.Entry e = (IceInternal.IntMap.Entry)i.next(); + IceInternal.OutgoingAsync out = (IceInternal.OutgoingAsync)e.getValue(); + out.__finished(_exception); // The exception is immutable at this point. + } + _asyncRequests.clear(); + if(localEx != null) { throw localEx; @@ -1477,6 +1382,147 @@ public final class ConnectionI extends IceInternal.EventHandler implements Conne } // + // Operations from SocketReadyCallback + // + public IceInternal.SocketStatus + socketReady(boolean finished) + { + if(!finished) + { + try + { + // + // First, we check if there's something to send. If that's the case, the connection + // must be active and the only thing to do is send the queued streams. + // + if(!_sendStreams.isEmpty()) + { + if(!send(0)) + { + return IceInternal.SocketStatus.NeedWrite; + } + assert(_sendStreams.isEmpty()); + } + else + { + // + // If there's nothing to send, we're still validating the connection. + // + int state; + synchronized(this) + { + assert(_state == StateClosed || _state <= StateNotValidated); + + if(_state == StateClosed) + { + assert(_exception != null); + throw _exception; + } + + state = _state; + } + + if(state == StateNotInitialized) + { + IceInternal.SocketStatus status = initialize(); + if(status != IceInternal.SocketStatus.Finished) + { + return status; + } + } + + if(state <= StateNotValidated) + { + IceInternal.SocketStatus status = validate(); + if(status != IceInternal.SocketStatus.Finished) + { + return status; + } + } + + finishStart(null); + } + } + catch(Ice.LocalException ex) + { + synchronized(this) + { + setState(StateClosed, ex); + } + } + } + + // + // If there's no more data to send or if connection validation is finished, we checkout + // the connection state to figure out whether or not it's time to unregister with the + // selector thread. + // + + synchronized(this) + { + assert(_sendInProgress); + if(_state == StateClosed) + { + assert(_startCallback == null || (!_threadPerConnection && !_registeredWithPool)); + + _queuedStreams.addAll(0, _sendStreams); + _sendInProgress = false; + + if(_threadPerConnection) + { + _transceiver.shutdownReadWrite(); + } + else + { + registerWithPool(); + unregisterWithPool(); // Let finished() do the close. + } + + notifyAll(); + return IceInternal.SocketStatus.Finished; + } + else if(_waitingForSend > 0) + { + _sendInProgress = false; + notifyAll(); + return IceInternal.SocketStatus.Finished; + } + else if(_queuedStreams.isEmpty()) + { + _sendInProgress = false; + if(_acmTimeout > 0) + { + _acmAbsoluteTimeoutMillis = IceInternal.Time.currentMonotonicTimeMillis() + _acmTimeout * 1000; + } + return IceInternal.SocketStatus.Finished; + } + else + { + java.util.LinkedList streams = _queuedStreams; + _queuedStreams = _sendStreams; + _sendStreams = streams; + return IceInternal.SocketStatus.NeedWrite; // We're not finished yet, there's more data to send! + } + } + } + + public void + socketTimeout() + { + synchronized(this) + { + if(_state <= StateNotValidated) + { + setState(StateClosed, new ConnectTimeoutException()); + } + else if(_state <= StateClosing) + { + setState(StateClosed, new TimeoutException()); + } + } + } + + // // Only used by the SSL plug-in. // // The external party has to synchronize the connection, since the @@ -1492,29 +1538,32 @@ public final class ConnectionI extends IceInternal.EventHandler implements Conne IceInternal.EndpointI endpoint, ObjectAdapter adapter, boolean threadPerConnection) { super(instance); + + final Ice.InitializationData initData = instance.initializationData(); _threadPerConnection = threadPerConnection; _transceiver = transceiver; _desc = transceiver.toString(); _type = transceiver.type(); _endpoint = endpoint; _adapter = adapter; - _logger = instance.initializationData().logger; // Cached for better performance. + _logger = initData.logger; // Cached for better performance. _traceLevels = instance.traceLevels(); // Cached for better performance. _registeredWithPool = false; _finishedCount = 0; - _warn = _instance.initializationData().properties.getPropertyAsInt("Ice.Warn.Connections") > 0 ? true : false; - _cacheBuffers = _instance.initializationData().properties.getPropertyAsIntWithDefault( - "Ice.CacheMessageBuffers", 1) == 1; + _warn = initData.properties.getPropertyAsInt("Ice.Warn.Connections") > 0 ? true : false; + _cacheBuffers = initData.properties.getPropertyAsIntWithDefault("Ice.CacheMessageBuffers", 1) == 1; _acmAbsoluteTimeoutMillis = 0; _nextRequestId = 1; - _batchAutoFlush = _instance.initializationData().properties.getPropertyAsIntWithDefault( - "Ice.BatchAutoFlush", 1) > 0 ? true : false; + _batchAutoFlush = initData.properties.getPropertyAsIntWithDefault("Ice.BatchAutoFlush", 1) > 0 ? true : false; _batchStream = new IceInternal.BasicStream(instance, _batchAutoFlush); _batchStreamInUse = false; _batchRequestNum = 0; _batchRequestCompress = false; + _batchMarker = 0; + _sendInProgress = false; + _waitingForSend = 0; _dispatchCount = 0; - _state = StateNotValidated; + _state = StateNotInitialized; _stateTime = IceInternal.Time.currentMonotonicTimeMillis(); if(_endpoint.datagram()) @@ -1533,8 +1582,7 @@ public final class ConnectionI extends IceInternal.EventHandler implements Conne } } - int compressionLevel = - _instance.initializationData().properties.getPropertyAsIntWithDefault("Ice.Compression.Level", 1); + int compressionLevel = initData.properties.getPropertyAsIntWithDefault("Ice.Compression.Level", 1); if(compressionLevel < 1) { compressionLevel = 1; @@ -1553,17 +1601,17 @@ public final class ConnectionI extends IceInternal.EventHandler implements Conne { _servantManager = null; } - - if(!threadPerConnection) + + try { - // - // Only set _threadPool if we really need it, i.e., if we are - // not in thread per connection mode. Thread pools have lazy - // initialization in Instance, and we don't want them to be - // created if they are not needed. - // - try + if(!threadPerConnection) { + // + // Only set _threadPool if we really need it, i.e., if we are + // not in thread per connection mode. Thread pools have lazy + // initialization in Instance, and we don't want them to be + // created if they are not needed. + // if(_adapter != null) { _threadPool = ((ObjectAdapterI)_adapter).getThreadPool(); @@ -1573,94 +1621,52 @@ public final class ConnectionI extends IceInternal.EventHandler implements Conne _threadPool = _instance.clientThreadPool(); } } - catch(java.lang.Exception ex) + else { - try - { - _transceiver.close(); - } - catch(LocalException e) - { - // Here we ignore any exceptions in close(). - } - - Ice.SyscallException e = new Ice.SyscallException(); - e.initCause(ex); - throw e; + _threadPool = null; // To satisfy the compiler. } + + _selectorThread = _instance.selectorThread(); + + _overrideCompress = _instance.defaultsAndOverrides().overrideCompress; + _overrideCompressValue = _instance.defaultsAndOverrides().overrideCompressValue; } - else + catch(Ice.LocalException ex) { - _threadPool = null; // To satisfy the compiler. + throw ex; + } + catch(java.lang.Exception ex) + { + Ice.SyscallException e = new Ice.SyscallException(); + e.initCause(ex); + throw e; } - - _overrideCompress = _instance.defaultsAndOverrides().overrideCompress; - _overrideCompressValue = _instance.defaultsAndOverrides().overrideCompressValue; } protected synchronized void finalize() throws Throwable { + IceUtil.Assert.FinalizerAssert(_startCallback == null); IceUtil.Assert.FinalizerAssert(_state == StateClosed); IceUtil.Assert.FinalizerAssert(_transceiver == null); IceUtil.Assert.FinalizerAssert(_dispatchCount == 0); IceUtil.Assert.FinalizerAssert(_thread == null); + IceUtil.Assert.FinalizerAssert(_queuedStreams.isEmpty()); + IceUtil.Assert.FinalizerAssert(_requests.isEmpty()); + IceUtil.Assert.FinalizerAssert(_asyncRequests.isEmpty()); super.finalize(); } - public void - start() - { - // - // If we are in thread per connection mode, create the thread for this connection. - // - if(_threadPerConnection) - { - try - { - _thread = new ThreadPerConnection(); - _thread.start(); - } - catch(java.lang.Exception ex) - { - java.io.StringWriter sw = new java.io.StringWriter(); - java.io.PrintWriter pw = new java.io.PrintWriter(sw); - ex.printStackTrace(pw); - pw.flush(); - _logger.error("cannot create thread for connection:\n" + sw.toString()); + private static final int StateNotInitialized = 0; + private static final int StateNotValidated = 1; + private static final int StateActive = 2; + private static final int StateHolding = 3; + private static final int StateClosing = 4; + private static final int StateClosed = 5; - try - { - _transceiver.close(); - } - catch(LocalException e) - { - // Here we ignore any exceptions in close(). - } - - // - // Clean up. - // - _transceiver = null; - _thread = null; - _state = StateClosed; - - Ice.SyscallException e = new Ice.SyscallException(); - e.initCause(ex); - throw e; - } - } - } - - private static final int StateNotValidated = 0; - private static final int StateActive = 1; - private static final int StateHolding = 2; - private static final int StateClosing = 3; - private static final int StateClosed = 4; - - private void + private boolean setState(int state, LocalException ex) { // @@ -1671,7 +1677,7 @@ public final class ConnectionI extends IceInternal.EventHandler implements Conne if(_state == state) // Don't switch twice. { - return; + return false; } if(_exception == null) @@ -1706,10 +1712,10 @@ public final class ConnectionI extends IceInternal.EventHandler implements Conne // exceptions. Otherwise new requests may retry on a // connection that is not yet marked as closed or closing. // - setState(state); + return setState(state); } - private void + private boolean setState(int state) { // @@ -1724,24 +1730,34 @@ public final class ConnectionI extends IceInternal.EventHandler implements Conne // // Skip graceful shutdown if we are destroyed before validation. // - if(_state == StateNotValidated && state == StateClosing) + if(_state <= StateNotValidated && state == StateClosing) { state = StateClosed; } if(_state == state) // Don't switch twice. { - return; + return false; } switch(state) { - case StateNotValidated: + case StateNotInitialized: { assert(false); break; } + case StateNotValidated: + { + if(_state != StateNotInitialized) + { + assert(_state == StateClosed); + return false; + } + break; + } + case StateActive: { // @@ -1750,7 +1766,7 @@ public final class ConnectionI extends IceInternal.EventHandler implements Conne // if(_state != StateHolding && _state != StateNotValidated) { - return; + return false; } if(!_threadPerConnection) { @@ -1767,7 +1783,7 @@ public final class ConnectionI extends IceInternal.EventHandler implements Conne // if(_state != StateActive && _state != StateNotValidated) { - return; + return false; } if(!_threadPerConnection) { @@ -1783,7 +1799,7 @@ public final class ConnectionI extends IceInternal.EventHandler implements Conne // if(_state == StateClosed) { - return; + return false; } if(!_threadPerConnection) { @@ -1794,60 +1810,43 @@ public final class ConnectionI extends IceInternal.EventHandler implements Conne case StateClosed: { - if(_threadPerConnection) - { - // - // If we are in thread per connection mode, we - // shutdown both for reading and writing. This will - // unblock and read call with an exception. The thread - // per connection then closes the transceiver. - // - _transceiver.shutdownReadWrite(); - } - else if(_state == StateNotValidated) + if(_sendInProgress) { // - // If we change from not validated, we can close right - // away. - // - assert(!_registeredWithPool); - + // Unregister with both the pool and the selector thread. We unregister with + // the pool to ensure that it stops reading on the socket (otherwise, if the + // socket is closed the thread pool would spin always reading 0 from the FD). + // The selector thread will register again the FD with the pool once it's + // done. // - // We must make sure that nobody is sending when we - // close the transceiver. - // - synchronized(_sendMutex) + _selectorThread.unregister(_transceiver.fd()); + if(!_threadPerConnection) { - try - { - _transceiver.close(); - } - catch(LocalException ex) - { - // Here we ignore any exceptions in close(). - } + unregisterWithPool(); + } - _transceiver = null; - //notifyAll(); // We notify already below. + if(_state >= StateNotValidated) + { + _transceiver.shutdownWrite(); } } - else + else if(_startCallback == null && _state <= StateNotValidated || _threadPerConnection) { // - // Otherwise we first must make sure that we are - // registered, then we unregister, and let finished() - // do the close. + // If we are in thread per connection mode and the thread is started, we + // shutdown both for reading and writing. This will unblock the read call + // with an exception. The thread per connection closes the transceiver. // + _transceiver.shutdownReadWrite(); + } + else + { registerWithPool(); - unregisterWithPool(); - - // - // We must prevent any further writes when _state == StateClosed. - // However, functions such as sendResponse cannot acquire the main - // mutex in order to check _state. Therefore we shut down the write - // end of the transceiver, which causes subsequent write attempts - // to fail with an exception. + unregisterWithPool(); // Let finished() do the close. + // + // Prevent further writes. + // _transceiver.shutdownWrite(); } break; @@ -1882,58 +1881,536 @@ public final class ConnectionI extends IceInternal.EventHandler implements Conne { try { - initiateShutdown(); - } - catch(IceInternal.LocalExceptionWrapper ex) // Java-specific workaround in Transceiver.write(). - { - setState(StateClosed, ex.get()); + return initiateShutdown(false); } catch(LocalException ex) { setState(StateClosed, ex); } } + + return false; } - private void - initiateShutdown() - throws IceInternal.LocalExceptionWrapper // Java-specific workaround in Transceiver.write(). + private boolean + initiateShutdown(boolean queue) { assert(_state == StateClosing); assert(_dispatchCount == 0); if(!_endpoint.datagram()) { - synchronized(_sendMutex) + // + // Before we shut down, we send a close connection + // message. + // + IceInternal.BasicStream os = new IceInternal.BasicStream(_instance); + os.writeBlob(IceInternal.Protocol.magic); + os.writeByte(IceInternal.Protocol.protocolMajor); + os.writeByte(IceInternal.Protocol.protocolMinor); + os.writeByte(IceInternal.Protocol.encodingMajor); + os.writeByte(IceInternal.Protocol.encodingMinor); + os.writeByte(IceInternal.Protocol.closeConnectionMsg); + os.writeByte(_compressionSupported ? (byte)1 : (byte)0); + os.writeInt(IceInternal.Protocol.headerSize); // Message size. + + return sendMessage(new OutgoingMessage(os, false, false), queue); + + // + // The CloseConnection message should be sufficient. Closing the write + // end of the socket is probably an artifact of how things were done + // in IIOP. In fact, shutting down the write end of the socket causes + // problems on Windows by preventing the peer from using the socket. + // For example, the peer is no longer able to continue writing a large + // message after the socket is shutdown. + // + //_transceiver.shutdownWrite(); + } + + return false; + } + + private IceInternal.SocketStatus + initialize() + { + int timeout = 0; + if(_startCallback == null || _threadPerConnection) + { + IceInternal.DefaultsAndOverrides defaultsAndOverrides = _instance.defaultsAndOverrides(); + if(defaultsAndOverrides.overrideConnectTimeout) { - // - // Before we shut down, we send a close connection - // message. - // - IceInternal.BasicStream os = new IceInternal.BasicStream(_instance); - os.writeBlob(IceInternal.Protocol.magic); - os.writeByte(IceInternal.Protocol.protocolMajor); - os.writeByte(IceInternal.Protocol.protocolMinor); - os.writeByte(IceInternal.Protocol.encodingMajor); - os.writeByte(IceInternal.Protocol.encodingMinor); - os.writeByte(IceInternal.Protocol.closeConnectionMsg); - os.writeByte(_compressionSupported ? (byte)1 : (byte)0); - os.writeInt(IceInternal.Protocol.headerSize); // Message size. + timeout = defaultsAndOverrides.overrideConnectTimeoutValue; + } + else + { + timeout = _endpoint.timeout(); + } + } + + try + { + IceInternal.SocketStatus status = _transceiver.initialize(timeout); + if(status != IceInternal.SocketStatus.Finished) + { + if(_startCallback == null || _threadPerConnection) + { + throw new Ice.TimeoutException(); + } + return status; + } + } + catch(Ice.TimeoutException ex) + { + throw new Ice.ConnectTimeoutException(); + } + + synchronized(this) + { + if(_state == StateClosed) + { + assert(_exception != null); + throw _exception; + } + + // + // Update the connection description once the transceiver is initialized. + // + _desc = _transceiver.toString(); + + setState(StateNotValidated); + } + + return IceInternal.SocketStatus.Finished; + } + + private IceInternal.SocketStatus + validate() + { + if(!_endpoint.datagram()) // Datagram connections are always implicitly validated. + { + int timeout = 0; + if(_startCallback == null || _threadPerConnection) + { + IceInternal.DefaultsAndOverrides defaultsAndOverrides = _instance.defaultsAndOverrides(); + if(defaultsAndOverrides.overrideConnectTimeout) + { + timeout = defaultsAndOverrides.overrideConnectTimeoutValue; + } + else + { + timeout = _endpoint.timeout(); + } + } + + if(_adapter != null) // The server side has the active role for connection validation. + { + IceInternal.BasicStream os = _stream; + if(os.size() == 0) + { + os.writeBlob(IceInternal.Protocol.magic); + os.writeByte(IceInternal.Protocol.protocolMajor); + os.writeByte(IceInternal.Protocol.protocolMinor); + os.writeByte(IceInternal.Protocol.encodingMajor); + os.writeByte(IceInternal.Protocol.encodingMinor); + os.writeByte(IceInternal.Protocol.validateConnectionMsg); + os.writeByte((byte)0); // Compression status (always zero for validate connection). + os.writeInt(IceInternal.Protocol.headerSize); // Message size. + IceInternal.TraceUtil.traceSend(os, _logger, _traceLevels); + os.prepareWrite(); + } + else + { + // The stream can only be non-empty if we're doing a non-blocking connection validation. + assert(_startCallback != null && !_threadPerConnection); + } + try + { + if(!_transceiver.write(os.getBuffer(), timeout)) + { + if(_startCallback == null || _threadPerConnection) + { + throw new Ice.TimeoutException(); + } + return IceInternal.SocketStatus.NeedWrite; + } + } + catch(IceInternal.LocalExceptionWrapper ex) // Java-specific workaround in Transceiver.write(). + { + throw ex.get(); + } + catch(Ice.TimeoutException ex) + { + throw new Ice.ConnectTimeoutException(); + } + } + else // The client side has the passive role for connection validation. + { + IceInternal.BasicStream is = _stream; + if(is.size() == 0) + { + is.resize(IceInternal.Protocol.headerSize, true); + is.pos(0); + } + else + { + // The stream can only be non-empty if we're doing a non-blocking connection validation. + assert(_startCallback != null && !_threadPerConnection); + } + + try + { + if(!_transceiver.read(is.getBuffer(), timeout, _hasMoreData)) + { + if(_startCallback == null || _threadPerConnection) + { + throw new Ice.TimeoutException(); + } + return IceInternal.SocketStatus.NeedRead; + } + } + catch(Ice.TimeoutException ex) + { + throw new Ice.ConnectTimeoutException(); + } + + assert(is.pos() == IceInternal.Protocol.headerSize); + is.pos(0); + byte[] m = is.readBlob(4); + if(m[0] != IceInternal.Protocol.magic[0] || m[1] != IceInternal.Protocol.magic[1] || + m[2] != IceInternal.Protocol.magic[2] || m[3] != IceInternal.Protocol.magic[3]) + { + BadMagicException ex = new BadMagicException(); + ex.badMagic = m; + throw ex; + } + byte pMajor = is.readByte(); + byte pMinor = is.readByte(); + if(pMajor != IceInternal.Protocol.protocolMajor) + { + UnsupportedProtocolException e = new UnsupportedProtocolException(); + e.badMajor = pMajor < 0 ? pMajor + 255 : pMajor; + e.badMinor = pMinor < 0 ? pMinor + 255 : pMinor; + e.major = IceInternal.Protocol.protocolMajor; + e.minor = IceInternal.Protocol.protocolMinor; + throw e; + } + byte eMajor = is.readByte(); + byte eMinor = is.readByte(); + if(eMajor != IceInternal.Protocol.encodingMajor) + { + UnsupportedEncodingException e = new UnsupportedEncodingException(); + e.badMajor = eMajor < 0 ? eMajor + 255 : eMajor; + e.badMinor = eMinor < 0 ? eMinor + 255 : eMinor; + e.major = IceInternal.Protocol.encodingMajor; + e.minor = IceInternal.Protocol.encodingMinor; + throw e; + } + byte messageType = is.readByte(); + if(messageType != IceInternal.Protocol.validateConnectionMsg) + { + throw new ConnectionNotValidatedException(); + } + byte compress = is.readByte(); // Ignore compression status for validate connection. + int size = is.readInt(); + if(size != IceInternal.Protocol.headerSize) + { + throw new IllegalMessageSizeException(); + } + IceInternal.TraceUtil.traceRecv(is, _logger, _traceLevels); + } + } + + synchronized(this) + { + _stream.reset(); + + if(_state == StateClosed) + { + assert(_exception != null); + throw _exception; + } + + // + // We start out in holding state. + // + setState(StateHolding); + } + + return IceInternal.SocketStatus.Finished; + } + + private boolean + send(int timeout) + { + assert(_transceiver != null); + assert(!_sendStreams.isEmpty()); + + while(!_sendStreams.isEmpty()) + { + OutgoingMessage message = (OutgoingMessage)_sendStreams.getFirst(); + if(!message.prepared) + { + IceInternal.BasicStream stream = message.stream; + + boolean compress = _overrideCompress ? _overrideCompressValue : message.compress; + message.stream = doCompress(stream, compress); + message.stream.prepareWrite(); + message.prepared = true; + + if(message.outAsync != null) + { + IceInternal.TraceUtil.trace("sending asynchronous request", stream, _logger, _traceLevels); + } + else + { + IceInternal.TraceUtil.traceSend(stream, _logger, _traceLevels); + } + + } + + try + { + if(!_transceiver.write(message.stream.getBuffer(), timeout)) + { + assert(timeout == 0); + return false; + } + } + catch(IceInternal.LocalExceptionWrapper ex) // Java-specific workaround in Transceiver.write(). + { + if(!ex.retry()) + { + message.sent(this, timeout == 0); + } + throw ex.get(); + } + + message.sent(this, timeout == 0); // timeout == 0 indicates that this is called by the selector thread. + _sendStreams.removeFirst(); + } + + return true; + } + + private boolean + sendMessage(OutgoingMessage message, boolean queue) + { + assert(_state != StateClosed); + + // + // TODO: Remove support for foreground send? If set to true, messages are sent + // by the calling thread. Foreground send might still be useful for transports + // that don't support non-blocking send. + // + boolean foreground = false; + + // + // If another thread is currently sending messages, we queue the message in _queuedStreams + // if we're not required to send the message in the foreground. If we're required to send + // the request in the foreground we wait until no more threads send messages. + // + if(_sendInProgress) + { + if(!foreground) + { + message.adopt(); + _queuedStreams.addLast(message); + return false; + } + else if(queue) + { // - // Send the message. - // - IceInternal.TraceUtil.traceHeader("sending close connection", os, _logger, _traceLevels); - _transceiver.write(os, _endpoint.timeout()); - // - // The CloseConnection message should be sufficient. Closing the write - // end of the socket is probably an artifact of how things were done - // in IIOP. In fact, shutting down the write end of the socket causes - // problems on Windows by preventing the peer from using the socket. - // For example, the peer is no longer able to continue writing a large - // message after the socket is shutdown. + // Add the message to _sendStreams if requested, this is useful for sendResponse() to + // send the close connection message after sending the response. // - //_transceiver.shutdownWrite(); + _sendStreams.addLast(message); + return true; // The calling thread must send the messages by calling finishSendMessage() + } + else + { + ++_waitingForSend; + while(_sendInProgress) + { + try + { + wait(); + } + catch(java.lang.InterruptedException ex) + { + } + } + --_waitingForSend; + + if(_state == StateClosed) + { + assert(_exception != null); + throw _exception; + } + } + } + + assert(!_sendInProgress); + + // + // Attempt to send the message without blocking. If the send blocks, we register + // the connection with the selector thread or we request the caller to call + // finishSendMessage() outside the synchronization. + // + + assert(!message.prepared); + + IceInternal.BasicStream stream = message.stream; + + boolean compress = _overrideCompress ? _overrideCompressValue : message.compress; + message.stream = doCompress(stream, compress); + message.stream.prepareWrite(); + message.prepared = true; + + if(message.outAsync != null) + { + IceInternal.TraceUtil.trace("sending asynchronous request", stream, _logger, _traceLevels); + } + else + { + IceInternal.TraceUtil.traceSend(stream, _logger, _traceLevels); + } + + try + { + if(!foreground && _transceiver.write(message.stream.getBuffer(), 0)) + { + message.sent(this, false); + if(_acmTimeout > 0) + { + _acmAbsoluteTimeoutMillis = IceInternal.Time.currentMonotonicTimeMillis() + _acmTimeout * 1000; + } + return false; + } + } + catch(IceInternal.LocalExceptionWrapper ex) // Java-specific workaround in Transceiver.write(). + { + if(!ex.retry()) + { + message.sent(this, false); + } + throw ex.get(); + } + + _sendStreams.addLast(message); + _sendInProgress = true; + if(!foreground) + { + message.adopt(); + _selectorThread._register(_transceiver.fd(), this, IceInternal.SocketStatus.NeedWrite, _endpoint.timeout()); + return false; // The selector thread will send the message. + } + else + { + return true; // The calling thread must send the message by calling finishSendMessage() + } + } + + private void + finishSendMessage() + { + try + { + // + // Send the queued messages with a blocking write(). + // + boolean finished = send(_endpoint.timeout()); + assert(finished); + } + catch(Ice.LocalException ex) + { + synchronized(this) + { + setState(StateClosed, ex); + + _sendStreams.clear(); + _sendInProgress = false; + + if(_threadPerConnection) + { + _transceiver.shutdownReadWrite(); + } + else + { + registerWithPool(); + unregisterWithPool(); // Let finished() do the close. + } + + notifyAll(); + + assert(_exception != null); + throw _exception; + } + } + + synchronized(this) + { + assert(_sendStreams.isEmpty()); + if(_state == StateClosed) + { + _sendInProgress = false; + if(_threadPerConnection) + { + _transceiver.shutdownReadWrite(); + } + else + { + registerWithPool(); + unregisterWithPool(); // Let finished() do the close. + } + notifyAll(); + } + if(_waitingForSend > 0) + { + _sendInProgress = false; + notifyAll(); + } + else if(_queuedStreams.isEmpty()) + { + if(_acmTimeout > 0) + { + _acmAbsoluteTimeoutMillis = IceInternal.Time.currentMonotonicTimeMillis() + _acmTimeout * 1000; + } + _sendInProgress = false; + } + else + { + _selectorThread._register(_transceiver.fd(), this, IceInternal.SocketStatus.NeedWrite, + _endpoint.timeout()); + } + } + } + + private void + finishStart(Ice.LocalException ex) + { + // + // We set _startCallback to null to break potential cyclic reference count + // and because the finalizer checks for it to ensure that we always invoke + // on the callback. + // + + StartCallback callback = null; + synchronized(this) + { + callback = _startCallback; + _startCallback = null; + } + + if(callback != null) + { + if(ex == null) + { + callback.connectionStartCompleted(this); + } + else + { + callback.connectionStartFailed(this, ex); } } } @@ -2075,7 +2552,7 @@ public final class ConnectionI extends IceInternal.EventHandler implements Conne { case IceInternal.Protocol.closeConnectionMsg: { - IceInternal.TraceUtil.traceHeader("received close connection", info.stream, _logger, _traceLevels); + IceInternal.TraceUtil.traceRecv(info.stream, _logger, _traceLevels); if(_endpoint.datagram()) { if(_warn) @@ -2094,13 +2571,13 @@ public final class ConnectionI extends IceInternal.EventHandler implements Conne { if(_state == StateClosing) { - IceInternal.TraceUtil.traceRequest("received request during closing\n" + - "(ignored by server, client will retry)", - info.stream, _logger, _traceLevels); + IceInternal.TraceUtil.trace("received request during closing\n" + + "(ignored by server, client will retry)", + info.stream, _logger, _traceLevels); } else { - IceInternal.TraceUtil.traceRequest("received request", info.stream, _logger, _traceLevels); + IceInternal.TraceUtil.traceRecv(info.stream, _logger, _traceLevels); info.requestId = info.stream.readInt(); info.invokeNum = 1; info.servantManager = _servantManager; @@ -2114,14 +2591,13 @@ public final class ConnectionI extends IceInternal.EventHandler implements Conne { if(_state == StateClosing) { - IceInternal.TraceUtil.traceBatchRequest("received batch request during closing\n" + - "(ignored by server, client will retry)", - info.stream, _logger, _traceLevels); + IceInternal.TraceUtil.trace("received batch request during closing\n" + + "(ignored by server, client will retry)", + info.stream, _logger, _traceLevels); } else { - IceInternal.TraceUtil.traceBatchRequest("received batch request", info.stream, _logger, - _traceLevels); + IceInternal.TraceUtil.traceRecv(info.stream, _logger, _traceLevels); info.invokeNum = info.stream.readInt(); if(info.invokeNum < 0) { @@ -2137,7 +2613,7 @@ public final class ConnectionI extends IceInternal.EventHandler implements Conne case IceInternal.Protocol.replyMsg: { - IceInternal.TraceUtil.traceReply("received reply", info.stream, _logger, _traceLevels); + IceInternal.TraceUtil.traceRecv(info.stream, _logger, _traceLevels); info.requestId = info.stream.readInt(); IceInternal.Outgoing out = (IceInternal.Outgoing)_requests.remove(info.requestId); if(out != null) @@ -2157,8 +2633,7 @@ public final class ConnectionI extends IceInternal.EventHandler implements Conne case IceInternal.Protocol.validateConnectionMsg: { - IceInternal.TraceUtil.traceHeader("received validate connection", info.stream, _logger, - _traceLevels); + IceInternal.TraceUtil.traceRecv(info.stream, _logger, _traceLevels); if(_warn) { _logger.warning("ignoring unexpected validate connection message:\n" + _desc); @@ -2168,9 +2643,8 @@ public final class ConnectionI extends IceInternal.EventHandler implements Conne default: { - IceInternal.TraceUtil.traceHeader("received unknown message\n" + - "(invalid, closing connection)", info.stream, _logger, - _traceLevels); + IceInternal.TraceUtil.trace("received unknown message\n(invalid, closing connection)", + info.stream, _logger, _traceLevels); throw new UnknownMessageException(); } } @@ -2274,63 +2748,58 @@ public final class ConnectionI extends IceInternal.EventHandler implements Conne private void run() { - // - // For non-datagram connections, the thread-per-connection - // must validate and activate this connection, and not in the - // connection factory. Please see the comments in the - // connection factory for details. - // - if(!_endpoint.datagram()) + try { - try - { - validate(); - } - catch(LocalException ex) + // + // Initialize the connection transceiver and validate the connection using + // blocking operations. + // + + IceInternal.SocketStatus status; + + status = initialize(); + assert(status == IceInternal.SocketStatus.Finished); + + status = validate(); + assert(status == IceInternal.SocketStatus.Finished); + } + catch(LocalException ex) + { + synchronized(this) { - synchronized(this) + setState(StateClosed, ex); + + if(_transceiver != null) { - assert(_state == StateClosed); - - // - // We must make sure that nobody is sending when - // we close the transceiver. - // - synchronized(_sendMutex) + try { - if(_transceiver != null) - { - try - { - _transceiver.close(); - } - catch(LocalException e) - { - // Here we ignore any exceptions in close(). - } - - _transceiver = null; - } - notifyAll(); + _transceiver.close(); + } + catch(LocalException e) + { + // Here we ignore any exceptions in close(). } + + _transceiver = null; } - return; + notifyAll(); } - - activate(); + + finishStart(_exception); + return; } + finishStart(null); + boolean warnUdp = _instance.initializationData().properties.getPropertyAsInt("Ice.Warn.Datagrams") > 0; boolean closed = false; IceInternal.BasicStream stream = new IceInternal.BasicStream(_instance); - while(!closed) { // - // We must accept new connections outside the thread - // synchronization, because we use blocking accept. + // We must read new messages outside the thread synchronization because we use blocking reads. // try @@ -2339,7 +2808,7 @@ public final class ConnectionI extends IceInternal.EventHandler implements Conne { stream.resize(IceInternal.Protocol.headerSize, true); stream.pos(0); - _transceiver.read(stream, -1); + _transceiver.read(stream.getBuffer(), -1, _hasMoreData); int pos = stream.pos(); if(pos < IceInternal.Protocol.headerSize) @@ -2409,7 +2878,7 @@ public final class ConnectionI extends IceInternal.EventHandler implements Conne } else { - _transceiver.read(stream, -1); + _transceiver.read(stream.getBuffer(), -1, _hasMoreData); assert(stream.pos() == stream.size()); } } @@ -2442,9 +2911,6 @@ public final class ConnectionI extends IceInternal.EventHandler implements Conne LocalException localEx = null; - IceInternal.IntMap requests = null; - IceInternal.IntMap asyncRequests = null; - synchronized(this) { while(_state == StateHolding) @@ -2469,25 +2935,38 @@ public final class ConnectionI extends IceInternal.EventHandler implements Conne // if(_state == StateClosed) { + if(_sendInProgress) + { + _selectorThread.unregister(_transceiver.fd()); + } + // - // We must make sure that nobody is sending when we close - // the transceiver. + // Prevent further writes. // - synchronized(_sendMutex) + _transceiver.shutdownWrite(); + + while(_sendInProgress) { try { - _transceiver.close(); + wait(); } - catch(LocalException ex) + catch(java.lang.Exception ex) { - localEx = ex; } - - _transceiver = null; - notifyAll(); } + try + { + _transceiver.close(); + } + catch(LocalException ex) + { + localEx = ex; + } + _transceiver = null; + notifyAll(); + // // We cannot simply return here. We have to make sure // that all requests (regular and async) are notified @@ -2495,15 +2974,6 @@ public final class ConnectionI extends IceInternal.EventHandler implements Conne // closed = true; } - - if(_state == StateClosed || _state == StateClosing) - { - requests = _requests; - _requests = new IceInternal.IntMap(); - - asyncRequests = _asyncRequests; - _asyncRequests = new IceInternal.IntMap(); - } } // @@ -2520,29 +2990,36 @@ public final class ConnectionI extends IceInternal.EventHandler implements Conne // must be done outside the thread synchronization, so that nested // calls are possible. // - invokeAll(info.stream, info.invokeNum, info.requestId, info.compress, info.servantManager, + invokeAll(info.stream, info.invokeNum, info.requestId, info.compress, info.servantManager, info.adapter); - if(requests != null) + if(closed) { - java.util.Iterator i = requests.entryIterator(); + java.util.Iterator i = _queuedStreams.iterator(); + while(i.hasNext()) + { + OutgoingMessage message = (OutgoingMessage)i.next(); + message.finished(_exception); + } + _queuedStreams.clear(); + + i = _requests.entryIterator(); while(i.hasNext()) { IceInternal.IntMap.Entry e = (IceInternal.IntMap.Entry)i.next(); IceInternal.Outgoing out = (IceInternal.Outgoing)e.getValue(); out.finished(_exception); // The exception is immutable at this point. } - } + _requests.clear(); - if(asyncRequests != null) - { - java.util.Iterator i = asyncRequests.entryIterator(); + i = _asyncRequests.entryIterator(); while(i.hasNext()) { IceInternal.IntMap.Entry e = (IceInternal.IntMap.Entry)i.next(); IceInternal.OutgoingAsync out = (IceInternal.OutgoingAsync)e.getValue(); out.__finished(_exception); // The exception is immutable at this point. } + _asyncRequests.clear(); } if(localEx != null) @@ -2628,8 +3105,7 @@ public final class ConnectionI extends IceInternal.EventHandler implements Conne } public IceInternal.Outgoing - getOutgoing(IceInternal.Reference reference, String operation, OperationMode mode, java.util.Map context, - boolean compress) + getOutgoing(IceInternal.RequestHandler handler, String operation, OperationMode mode, java.util.Map context) throws IceInternal.LocalExceptionWrapper { IceInternal.Outgoing out = null; @@ -2640,20 +3116,20 @@ public final class ConnectionI extends IceInternal.EventHandler implements Conne { if(_outgoingCache == null) { - out = new IceInternal.Outgoing(this, reference, operation, mode, context, compress); + out = new IceInternal.Outgoing(handler, operation, mode, context); } else { out = _outgoingCache; _outgoingCache = _outgoingCache.next; - out.reset(reference, operation, mode, context, compress); + out.reset(handler, operation, mode, context); out.next = null; } } } else { - out = new IceInternal.Outgoing(this, reference, operation, mode, context, compress); + out = new IceInternal.Outgoing(handler, operation, mode, context); } return out; @@ -2704,11 +3180,96 @@ public final class ConnectionI extends IceInternal.EventHandler implements Conne } } } + + private static class OutgoingMessage + { + OutgoingMessage(IceInternal.BasicStream stream, boolean compress, boolean adopt) + { + this.stream = stream; + this.compress = compress; + this.adopt = adopt; + } + + OutgoingMessage(IceInternal.OutgoingMessageCallback out, IceInternal.BasicStream stream, boolean compress, + boolean resp) + { + this.stream = stream; + this.compress = compress; + this.out = out; + this.response = resp; + } + + OutgoingMessage(IceInternal.OutgoingAsyncMessageCallback out, IceInternal.BasicStream stream, boolean compress, + boolean resp) + { + this.stream = stream; + this.compress = compress; + this.outAsync = out; + this.response = resp; + } + + public void + adopt() + { + if(adopt) + { + IceInternal.BasicStream stream = new IceInternal.BasicStream(this.stream.instance()); + stream.swap(this.stream); + this.stream = stream; + adopt = false; + } + } + + public void + sent(ConnectionI connection, boolean notify) + { + if(out != null) + { + out.sent(notify); // true = notify the waiting thread that the request was sent. + } + else if(outAsync != null) + { + outAsync.__sent(connection); + } + } + + public void + finished(Ice.LocalException ex) + { + // + // Only notify oneway requests. The connection keeps track of twoway + // requests in the _requests/_asyncRequests maps and will notify them + // of the connection exceptions. + // + if(!response) + { + if(out != null) + { + out.finished(ex); + } + else if(outAsync != null) + { + outAsync.__finished(ex); + } + } + } + + public IceInternal.BasicStream stream; + public IceInternal.OutgoingMessageCallback out; + public IceInternal.OutgoingAsyncMessageCallback outAsync; + public boolean compress; + public boolean response; + boolean adopt; + boolean prepared; + } + private Thread _thread; private final boolean _threadPerConnection; private IceInternal.Transceiver _transceiver; - private final String _desc; + private Ice.BooleanHolder _hasMoreData = new Ice.BooleanHolder(false); + + private String _desc; private final String _type; private final IceInternal.EndpointI _endpoint; @@ -2721,6 +3282,9 @@ public final class ConnectionI extends IceInternal.EventHandler implements Conne private boolean _registeredWithPool; private int _finishedCount; private final IceInternal.ThreadPool _threadPool; + private final IceInternal.SelectorThread _selectorThread; + + private StartCallback _startCallback = null; private final boolean _warn; @@ -2743,17 +3307,16 @@ public final class ConnectionI extends IceInternal.EventHandler implements Conne private boolean _batchRequestCompress; private int _batchMarker; + private java.util.LinkedList _queuedStreams = new java.util.LinkedList(); + private java.util.LinkedList _sendStreams = new java.util.LinkedList(); + private boolean _sendInProgress; + private int _waitingForSend; + private int _dispatchCount; private int _state; // The current state. private long _stateTime; // The last time when the state was changed. - // - // We have a separate mutex for sending, so that we don't block - // the whole connection when we do a blocking send. - // - private java.lang.Object _sendMutex = new java.lang.Object(); - private IceInternal.Incoming _incomingCache; private java.lang.Object _incomingCacheMutex = new java.lang.Object(); diff --git a/java/src/Ice/InputStreamI.java b/java/src/Ice/InputStreamI.java index 4cd30cbd4aa..eaa75550cf9 100644 --- a/java/src/Ice/InputStreamI.java +++ b/java/src/Ice/InputStreamI.java @@ -19,10 +19,10 @@ public class InputStreamI implements InputStream _is = new IceInternal.BasicStream(Util.getInstance(communicator)); _is.closure(this); _is.resize(data.length, true); - java.nio.ByteBuffer buf = _is.prepareRead(); - buf.position(0); - buf.put(data); - buf.position(0); + IceInternal.Buffer buf = _is.getBuffer(); + buf.b.position(0); + buf.b.put(data); + buf.b.position(0); } public Communicator diff --git a/java/src/Ice/ObjectAdapterI.java b/java/src/Ice/ObjectAdapterI.java index 8d3e8bf8779..adb890ae473 100644 --- a/java/src/Ice/ObjectAdapterI.java +++ b/java/src/Ice/ObjectAdapterI.java @@ -370,7 +370,6 @@ public final class ObjectAdapterI implements ObjectAdapter _routerInfo = null; _publishedEndpoints = null; _locatorInfo = null; - _connectors = null; objectAdapterFactory = _objectAdapterFactory; _objectAdapterFactory = null; @@ -538,22 +537,17 @@ public final class ObjectAdapterI implements ObjectAdapter { IceInternal.IncomingConnectionFactory factory = (IceInternal.IncomingConnectionFactory)_incomingConnectionFactories.get(i); - ConnectionI[] conns = factory.connections(); - for(int j = 0; j < conns.length; ++j) - { - connections.add(conns[j]); - } + connections.addAll(factory.connections()); } // // Create a reference and return a reverse proxy for this // reference. // - IceInternal.EndpointI[] endpoints = new IceInternal.EndpointI[0]; ConnectionI[] arr = new ConnectionI[connections.size()]; connections.toArray(arr); IceInternal.Reference ref = - _instance.referenceFactory().create(ident, _instance.getDefaultContext(), "", + _instance.referenceFactory().create(ident, _instance.getDefaultContext(), "", IceInternal.Reference.ModeTwoway, arr); return _instance.proxyFactory().referenceToProxy(ref); } @@ -580,14 +574,6 @@ public final class ObjectAdapterI implements ObjectAdapter oldPublishedEndpoints = _publishedEndpoints; _publishedEndpoints = parsePublishedEndpoints(); - _connectors.clear(); - java.util.Iterator p = _incomingConnectionFactories.iterator(); - while(p.hasNext()) - { - IceInternal.IncomingConnectionFactory factory = (IceInternal.IncomingConnectionFactory)p.next(); - _connectors.addAll(factory.endpoint().connectors()); - } - locatorInfo = _locatorInfo; if(!_noConfig) { @@ -654,7 +640,6 @@ public final class ObjectAdapterI implements ObjectAdapter endpoints = ref.getEndpoints(); } - synchronized(this) { checkForDeactivation(); @@ -666,15 +651,24 @@ public final class ObjectAdapterI implements ObjectAdapter // for(int i = 0; i < endpoints.length; ++i) { - final int sz = _connectors.size(); - for(int j = 0; j < sz; j++) + java.util.Iterator p; + p = _publishedEndpoints.iterator(); + while(p.hasNext()) + { + if(endpoints[i].equivalent((IceInternal.EndpointI)p.next())) + { + return true; + } + } + p = _incomingConnectionFactories.iterator(); + while(p.hasNext()) { - IceInternal.Connector connector = (IceInternal.Connector)_connectors.get(j); - if(endpoints[i].equivalent(connector)) + if(endpoints[i].equivalent(((IceInternal.IncomingConnectionFactory)p.next()).endpoint())) { return true; } } + } // @@ -842,7 +836,7 @@ public final class ObjectAdapterI implements ObjectAdapter _id = properties.getProperty(_name + ".AdapterId"); _replicaGroupId = properties.getProperty(_name + ".ReplicaGroupId"); - + try { _threadPerConnection = properties.getPropertyAsInt(_name + ".ThreadPerConnection") > 0; @@ -932,9 +926,8 @@ public final class ObjectAdapterI implements ObjectAdapter else { // - // Parse the endpoints, but don't store them in the adapter. - // The connection factory might change it, for example, to - // fill in the real port number. + // Parse the endpoints, but don't store them in the adapter. The connection + // factory might change it, for example, to fill in the real port number. // java.util.ArrayList endpoints; if(endpointInfo.length() == 0) @@ -957,11 +950,10 @@ public final class ObjectAdapterI implements ObjectAdapter ex.unsupportedFeature = "endpoint requires thread-per-connection:\n" + endp.toString(); throw ex; } + IceInternal.IncomingConnectionFactory factory = new IceInternal.IncomingConnectionFactory(instance, endp, this, _name); _incomingConnectionFactories.add(factory); - - _connectors.addAll(factory.endpoint().connectors()); } if(endpoints.size() == 0) { @@ -1193,7 +1185,7 @@ public final class ObjectAdapterI implements ObjectAdapter while(p.hasNext()) { IceInternal.EndpointI endp = (IceInternal.EndpointI)p.next(); - java.util.ArrayList endps = endp.expand(); + java.util.List endps = endp.expand(); expandedEndpoints.addAll(endps); } return expandedEndpoints; @@ -1373,7 +1365,6 @@ public final class ObjectAdapterI implements ObjectAdapter final private String _id; final private String _replicaGroupId; private java.util.ArrayList _incomingConnectionFactories = new java.util.ArrayList(); - private java.util.ArrayList _connectors = new java.util.ArrayList(); private java.util.ArrayList _routerEndpoints = new java.util.ArrayList(); private IceInternal.RouterInfo _routerInfo = null; private java.util.ArrayList _publishedEndpoints = new java.util.ArrayList(); diff --git a/java/src/Ice/ObjectPrx.java b/java/src/Ice/ObjectPrx.java index b0f187de430..a33669161df 100644 --- a/java/src/Ice/ObjectPrx.java +++ b/java/src/Ice/ObjectPrx.java @@ -138,5 +138,8 @@ public interface ObjectPrx Connection ice_getConnection(); Connection ice_getCachedConnection(); + void ice_flushBatchRequests(); + void ice_flushBatchRequests_async(AMI_Object_ice_flushBatchRequests cb); + boolean equals(java.lang.Object r); } diff --git a/java/src/Ice/ObjectPrxHelperBase.java b/java/src/Ice/ObjectPrxHelperBase.java index 72df0844773..87c3b86f3d2 100644 --- a/java/src/Ice/ObjectPrxHelperBase.java +++ b/java/src/Ice/ObjectPrxHelperBase.java @@ -82,7 +82,7 @@ public class ObjectPrxHelperBase implements ObjectPrx try { __checkTwowayOnly("ice_isA"); - __del = __getDelegate(); + __del = __getDelegate(false); return __del.ice_isA(__id, __context); } catch(IceInternal.LocalExceptionWrapper __ex) @@ -122,7 +122,7 @@ public class ObjectPrxHelperBase implements ObjectPrx _ObjectDel __del = null; try { - __del = __getDelegate(); + __del = __getDelegate(false); __del.ice_ping(__context); return; } @@ -164,7 +164,7 @@ public class ObjectPrxHelperBase implements ObjectPrx try { __checkTwowayOnly("ice_ids"); - __del = __getDelegate(); + __del = __getDelegate(false); return __del.ice_ids(__context); } catch(IceInternal.LocalExceptionWrapper __ex) @@ -205,7 +205,7 @@ public class ObjectPrxHelperBase implements ObjectPrx try { __checkTwowayOnly("ice_id"); - __del = __getDelegate(); + __del = __getDelegate(false); return __del.ice_id(__context); } catch(IceInternal.LocalExceptionWrapper __ex) @@ -247,7 +247,7 @@ public class ObjectPrxHelperBase implements ObjectPrx _ObjectDel __del = null; try { - __del = __getDelegate(); + __del = __getDelegate(false); return __del.ice_invoke(operation, mode, inParams, outParams, context); } catch(IceInternal.LocalExceptionWrapper __ex) @@ -776,8 +776,8 @@ public class ObjectPrxHelperBase implements ObjectPrx **/ public final Connection ice_connection() - { - return ice_getConnection(); + { + return ice_getConnection(); } public final Connection @@ -789,8 +789,9 @@ public class ObjectPrxHelperBase implements ObjectPrx _ObjectDel __del = null; try { - __del = __getDelegate(); - return __del.__getConnection(new BooleanHolder()); + __del = __getDelegate(false); + // Wait for the connection to be established. + return __del.__getRequestHandler().getConnection(true); } catch(LocalException __ex) { @@ -812,7 +813,8 @@ public class ObjectPrxHelperBase implements ObjectPrx { try { - return __del.__getConnection(new BooleanHolder()); + // Don't wait for the connection to be established. + return __del.__getRequestHandler().getConnection(false); } catch(CollocationOptimizationException ex) { @@ -821,6 +823,36 @@ public class ObjectPrxHelperBase implements ObjectPrx return null; } + public void + ice_flushBatchRequests() + { + int __cnt = 0; + while(true) + { + _ObjectDel __del = null; + try + { + __del = __getDelegate(false); + __del.ice_flushBatchRequests(); + return; + } + catch(IceInternal.LocalExceptionWrapper __ex) + { + __handleExceptionWrapper(__del, __ex); + } + catch(LocalException __ex) + { + __cnt = __handleException(__del, __ex, __cnt); + } + } + } + + public void + ice_flushBatchRequests_async(AMI_Object_ice_flushBatchRequests cb) + { + cb.__invoke(this); + } + public final boolean equals(java.lang.Object r) { @@ -988,7 +1020,7 @@ public class ObjectPrxHelperBase implements ObjectPrx } public final synchronized _ObjectDel - __getDelegate() + __getDelegate(boolean async) { if(_delegate != null) { @@ -1010,19 +1042,8 @@ public class ObjectPrxHelperBase implements ObjectPrx if(delegate == null) { _ObjectDelM d = __createDelegateM(); - d.setup(_reference); + d.setup(_reference, this, async); delegate = d; - - // - // If this proxy is for a non-local object, and we are - // using a router, then add this proxy to the router info - // object. - // - IceInternal.RouterInfo ri = _reference.getRouterInfo(); - if(ri != null) - { - ri.addProxy(this); - } } if(_reference.getCacheConnection()) @@ -1038,6 +1059,24 @@ public class ObjectPrxHelperBase implements ObjectPrx return delegate; } + synchronized public void + __setRequestHandler(_ObjectDel delegate, IceInternal.RequestHandler handler) + { + if(delegate == _delegate) + { + if(_delegate instanceof _ObjectDelM) + { + _delegate = __createDelegateM(); + _delegate.__setRequestHandler(handler); + } + else if(_delegate instanceof _ObjectDelD) + { + _delegate = __createDelegateD(); + _delegate.__setRequestHandler(handler); + } + } + } + protected _ObjectDelM __createDelegateM() { diff --git a/java/src/Ice/OutputStreamI.java b/java/src/Ice/OutputStreamI.java index 2a0884014ef..0393caf4689 100644 --- a/java/src/Ice/OutputStreamI.java +++ b/java/src/Ice/OutputStreamI.java @@ -190,9 +190,9 @@ public class OutputStreamI implements OutputStream public byte[] finished() { - java.nio.ByteBuffer buf = _os.prepareWrite(); - byte[] result = new byte[buf.limit()]; - buf.get(result); + IceInternal.Buffer buf = _os.prepareWrite(); + byte[] result = new byte[buf.b.limit()]; + buf.b.get(result); return result; } diff --git a/java/src/Ice/_ObjectDel.java b/java/src/Ice/_ObjectDel.java index e17416dfe8a..8eefc506fca 100644 --- a/java/src/Ice/_ObjectDel.java +++ b/java/src/Ice/_ObjectDel.java @@ -27,5 +27,9 @@ public interface _ObjectDel java.util.Map context) throws IceInternal.LocalExceptionWrapper; - ConnectionI __getConnection(BooleanHolder compress); + void ice_flushBatchRequests() + throws IceInternal.LocalExceptionWrapper; + + IceInternal.RequestHandler __getRequestHandler(); + void __setRequestHandler(IceInternal.RequestHandler handler); } diff --git a/java/src/Ice/_ObjectDelD.java b/java/src/Ice/_ObjectDelD.java index 7caece078cd..0248419607d 100644 --- a/java/src/Ice/_ObjectDelD.java +++ b/java/src/Ice/_ObjectDelD.java @@ -215,8 +215,20 @@ public class _ObjectDelD implements _ObjectDel throw new CollocationOptimizationException(); } - public ConnectionI - __getConnection(BooleanHolder compress) + public void + ice_flushBatchRequests() + { + throw new CollocationOptimizationException(); + } + + public IceInternal.RequestHandler + __getRequestHandler() + { + throw new CollocationOptimizationException(); + } + + public void + __setRequestHandler(IceInternal.RequestHandler handler) { throw new CollocationOptimizationException(); } diff --git a/java/src/Ice/_ObjectDelM.java b/java/src/Ice/_ObjectDelM.java index b37bd91733e..8bc56496b33 100644 --- a/java/src/Ice/_ObjectDelM.java +++ b/java/src/Ice/_ObjectDelM.java @@ -15,8 +15,7 @@ public class _ObjectDelM implements _ObjectDel ice_isA(String __id, java.util.Map __context) throws IceInternal.LocalExceptionWrapper { - IceInternal.Outgoing __og = __connection.getOutgoing(__reference, "ice_isA", OperationMode.Nonmutating, - __context, __compress); + IceInternal.Outgoing __og = __handler.getOutgoing("ice_isA", OperationMode.Nonmutating, __context); try { try @@ -52,7 +51,7 @@ public class _ObjectDelM implements _ObjectDel } finally { - __connection.reclaimOutgoing(__og); + __handler.reclaimOutgoing(__og); } } @@ -60,8 +59,7 @@ public class _ObjectDelM implements _ObjectDel ice_ping(java.util.Map __context) throws IceInternal.LocalExceptionWrapper { - IceInternal.Outgoing __og = __connection.getOutgoing(__reference, "ice_ping", OperationMode.Nonmutating, - __context, __compress); + IceInternal.Outgoing __og = __handler.getOutgoing("ice_ping", OperationMode.Nonmutating, __context); try { boolean __ok = __og.invoke(); @@ -87,7 +85,7 @@ public class _ObjectDelM implements _ObjectDel } finally { - __connection.reclaimOutgoing(__og); + __handler.reclaimOutgoing(__og); } } @@ -95,8 +93,7 @@ public class _ObjectDelM implements _ObjectDel ice_ids(java.util.Map __context) throws IceInternal.LocalExceptionWrapper { - IceInternal.Outgoing __og = __connection.getOutgoing(__reference, "ice_ids", OperationMode.Nonmutating, - __context, __compress); + IceInternal.Outgoing __og = __handler.getOutgoing("ice_ids", OperationMode.Nonmutating, __context); try { boolean __ok = __og.invoke(); @@ -123,7 +120,7 @@ public class _ObjectDelM implements _ObjectDel } finally { - __connection.reclaimOutgoing(__og); + __handler.reclaimOutgoing(__og); } } @@ -131,8 +128,7 @@ public class _ObjectDelM implements _ObjectDel ice_id(java.util.Map __context) throws IceInternal.LocalExceptionWrapper { - IceInternal.Outgoing __og = __connection.getOutgoing(__reference, "ice_id", OperationMode.Nonmutating, - __context, __compress); + IceInternal.Outgoing __og = __handler.getOutgoing("ice_id", OperationMode.Nonmutating, __context); try { boolean __ok = __og.invoke(); @@ -159,7 +155,7 @@ public class _ObjectDelM implements _ObjectDel } finally { - __connection.reclaimOutgoing(__og); + __handler.reclaimOutgoing(__og); } } @@ -167,7 +163,7 @@ public class _ObjectDelM implements _ObjectDel ice_invoke(String operation, OperationMode mode, byte[] inParams, ByteSeqHolder outParams, java.util.Map __context) throws IceInternal.LocalExceptionWrapper { - IceInternal.Outgoing __og = __connection.getOutgoing(__reference, operation, mode, __context, __compress); + IceInternal.Outgoing __og = __handler.getOutgoing(operation, mode, __context); try { if(inParams != null) @@ -183,7 +179,7 @@ public class _ObjectDelM implements _ObjectDel } } boolean ok = __og.invoke(); - if(__reference.getMode() == IceInternal.Reference.ModeTwoway) + if(__handler.getReference().getMode() == IceInternal.Reference.ModeTwoway) { try { @@ -203,15 +199,40 @@ public class _ObjectDelM implements _ObjectDel } finally { - __connection.reclaimOutgoing(__og); + __handler.reclaimOutgoing(__og); } } - public ConnectionI - __getConnection(BooleanHolder compress) + public void + ice_flushBatchRequests() + throws IceInternal.LocalExceptionWrapper + { + IceInternal.BatchOutgoing out = new IceInternal.BatchOutgoing(__handler); + try + { + out.invoke(); + } + catch(Ice.LocalException ex) + { + // + // We never retry flusing the batch requests as the connection batched + // requests were discarded and the caller needs to be notified of the + // failure. + // + throw new IceInternal.LocalExceptionWrapper(ex, false); + } + } + + public IceInternal.RequestHandler + __getRequestHandler() { - compress.value = __compress; - return __connection; + return __handler; + } + + public void + __setRequestHandler(IceInternal.RequestHandler handler) + { + __handler = handler; } // @@ -230,32 +251,38 @@ public class _ObjectDelM implements _ObjectDel // upon initialization. // - assert(__reference == null); - assert(__connection == null); + assert(__handler == null); - __reference = from.__reference; - __connection = from.__connection; - __compress = from.__compress; + __handler = from.__handler; } - protected IceInternal.Reference __reference; - protected ConnectionI __connection; - protected boolean __compress; + protected IceInternal.RequestHandler __handler; public void - setup(IceInternal.Reference ref) + setup(IceInternal.Reference ref, Ice.ObjectPrx proxy, boolean async) { // // No need to synchronize, as this operation is only called // upon initialization. // - assert(__reference == null); - assert(__connection == null); + assert(__handler == null); - __reference = ref; - BooleanHolder compress = new BooleanHolder(); - __connection = __reference.getConnection(compress); - __compress = compress.value; + // + // If the delegate is created as a result of an AMI call or if the proxy is + // a batch proxy we use the connect request handler to connect the in the + // background. + // + if(async || + ref.getMode() == IceInternal.Reference.ModeBatchOneway || + ref.getMode() == IceInternal.Reference.ModeBatchDatagram) + { + IceInternal.ConnectRequestHandler handler = new IceInternal.ConnectRequestHandler(ref, proxy, this); + __handler = handler.connect(); + } + else + { + __handler = new IceInternal.ConnectionRequestHandler(ref, proxy); + } } } diff --git a/java/src/IceInternal/BasicStream.java b/java/src/IceInternal/BasicStream.java index d7a33113069..16409506b80 100644 --- a/java/src/IceInternal/BasicStream.java +++ b/java/src/IceInternal/BasicStream.java @@ -12,27 +12,24 @@ package IceInternal; public class BasicStream { public - BasicStream(IceInternal.Instance instance) + BasicStream(Instance instance) { initialize(instance, false); } public - BasicStream(IceInternal.Instance instance, boolean unlimited) + BasicStream(Instance instance, boolean unlimited) { initialize(instance, unlimited); } private void - initialize(IceInternal.Instance instance, boolean unlimited) + initialize(Instance instance, boolean unlimited) { _instance = instance; + _buf = new Buffer(_instance.messageSizeMax()); _closure = null; _unlimited = unlimited; - allocate(1500); - _capacity = _buf.capacity(); - _limit = 0; - assert(_buf.limit() == _capacity); _readEncapsStack = null; _writeEncapsStack = null; @@ -56,9 +53,7 @@ public class BasicStream public void reset() { - _limit = 0; - _buf.limit(_capacity); - _buf.position(0); + _buf.reset(); if(_readEncapsStack != null) { @@ -75,7 +70,7 @@ public class BasicStream } } - public IceInternal.Instance + public Instance instance() { return _instance; @@ -104,18 +99,10 @@ public class BasicStream other._closure = _closure; _closure = tmpClosure; - java.nio.ByteBuffer tmpBuf = other._buf; + Buffer tmpBuf = other._buf; other._buf = _buf; _buf = tmpBuf; - int tmpCapacity = other._capacity; - other._capacity = _capacity; - _capacity = tmpCapacity; - - int tmpLimit = other._limit; - other._limit = _limit; - _limit = tmpLimit; - ReadEncaps tmpRead = other._readEncapsStack; other._readEncapsStack = _readEncapsStack; _readEncapsStack = tmpRead; @@ -154,49 +141,31 @@ public class BasicStream } public void - resize(int total, boolean reading) + resize(int sz, boolean reading) { - if(!_unlimited && total > _messageSizeMax) - { - throw new Ice.MemoryLimitException(); - } - if(total > _capacity) - { - final int cap2 = _capacity << 1; - int newCapacity = cap2 > total ? cap2 : total; - _buf.limit(_limit); - reallocate(newCapacity); - _capacity = _buf.capacity(); - } // - // If this stream is used for reading, then we want to set the - // buffer's limit to the new total size. If this buffer is - // used for writing, then we must set the buffer's limit to - // the buffer's capacity. + // Check memory limit if stream is not unlimited. // - if(reading) + if(!_unlimited && sz > _messageSizeMax) { - _buf.limit(total); - } - else - { - _buf.limit(_capacity); + throw new Ice.MemoryLimitException(); } - _buf.position(total); - _limit = total; + + _buf.resize(sz, reading); + _buf.b.position(sz); } - public java.nio.ByteBuffer - prepareRead() + public Buffer + prepareWrite() { + _buf.b.limit(_buf.size()); + _buf.b.position(0); return _buf; } - public java.nio.ByteBuffer - prepareWrite() + public Buffer + getBuffer() { - _buf.limit(_limit); - _buf.position(0); return _buf; } @@ -261,7 +230,7 @@ public class BasicStream sd.previous = _seqDataStack; _seqDataStack = sd; - int bytesLeft = _buf.remaining(); + int bytesLeft = _buf.b.remaining(); if(_seqDataStack.previous == null) // Outermost sequence { // @@ -287,7 +256,7 @@ public class BasicStream public void checkSeq() { - checkSeq(_buf.remaining()); + checkSeq(_buf.b.remaining()); } public void @@ -311,7 +280,7 @@ public class BasicStream public void checkFixedSeq(int numElements, int elemSize) { - int bytesLeft = _buf.remaining(); + int bytesLeft = _buf.b.remaining(); if(_seqDataStack == null) // Outermost sequence { // @@ -376,7 +345,7 @@ public class BasicStream _writeEncapsStack = curr; } - _writeEncapsStack.start = _buf.position(); + _writeEncapsStack.start = _buf.size(); writeBlob(_encapsBlob); } @@ -385,8 +354,8 @@ public class BasicStream { assert(_writeEncapsStack != null); int start = _writeEncapsStack.start; - int sz = _buf.position() - start; // Size includes size and version. - _buf.putInt(start, sz); + int sz = _buf.size() - start; // Size includes size and version. + _buf.b.putInt(start, sz); WriteEncaps curr = _writeEncapsStack; _writeEncapsStack = curr.next; @@ -413,8 +382,8 @@ public class BasicStream _readEncapsStack = curr; } - _readEncapsStack.start = _buf.position(); - + _readEncapsStack.start = _buf.b.position(); + // // I don't use readSize() and writeSize() for encapsulations, // because when creating an encapsulation, I must know in @@ -428,7 +397,7 @@ public class BasicStream throw new Ice.NegativeSizeException(); } - if(sz - 4 > _buf.limit()) + if(sz - 4 > _buf.b.limit()) { throw new Ice.UnmarshalOutOfBoundsException(); } @@ -457,7 +426,7 @@ public class BasicStream int sz = _readEncapsStack.sz; try { - _buf.position(start + sz); + _buf.b.position(start + sz); } catch(IllegalArgumentException ex) { @@ -477,7 +446,7 @@ public class BasicStream assert(_readEncapsStack != null); int start = _readEncapsStack.start; int sz = _readEncapsStack.sz; - if(_buf.position() != start + sz) + if(_buf.b.position() != start + sz) { throw new Ice.EncapsulationException(); } @@ -500,7 +469,7 @@ public class BasicStream } try { - _buf.position(_buf.position() + sz - 4); + _buf.b.position(_buf.b.position() + sz - 4); } catch(IllegalArgumentException ex) { @@ -512,13 +481,13 @@ public class BasicStream startWriteSlice() { writeInt(0); // Placeholder for the slice length. - _writeSlice = _buf.position(); + _writeSlice = _buf.size(); } public void endWriteSlice() { - final int sz = _buf.position() - _writeSlice + 4; - _buf.putInt(_writeSlice - 4, sz); + final int sz = _buf.size() - _writeSlice + 4; + _buf.b.putInt(_writeSlice - 4, sz); } public void startReadSlice() @@ -528,7 +497,7 @@ public class BasicStream { throw new Ice.NegativeSizeException(); } - _readSlice = _buf.position(); + _readSlice = _buf.b.position(); } public void endReadSlice() @@ -544,7 +513,7 @@ public class BasicStream } try { - _buf.position(_buf.position() + sz - 4); + _buf.b.position(_buf.b.position() + sz - 4); } catch(IllegalArgumentException ex) { @@ -558,13 +527,13 @@ public class BasicStream if(v > 254) { expand(5); - _buf.put((byte)-1); - _buf.putInt(v); + _buf.b.put((byte)-1); + _buf.b.putInt(v); } else { expand(1); - _buf.put((byte)v); + _buf.b.put((byte)v); } } @@ -573,10 +542,10 @@ public class BasicStream { try { - byte b = _buf.get(); + byte b = _buf.b.get(); if(b == -1) { - int v = _buf.getInt(); + int v = _buf.b.getInt(); if(v < 0) { throw new Ice.NegativeSizeException(); @@ -640,14 +609,14 @@ public class BasicStream writeBlob(byte[] v) { expand(v.length); - _buf.put(v); + _buf.b.put(v); } public void writeBlob(byte[] v, int off, int len) { expand(len); - _buf.put(v, off, len); + _buf.b.put(v, off, len); } public byte[] @@ -656,7 +625,7 @@ public class BasicStream byte[] v = new byte[sz]; try { - _buf.get(v); + _buf.b.get(v); return v; } catch(java.nio.BufferUnderflowException ex) @@ -669,7 +638,7 @@ public class BasicStream writeByte(byte v) { expand(1); - _buf.put(v); + _buf.b.put(v); } public void @@ -693,7 +662,7 @@ public class BasicStream { writeSize(v.length); expand(v.length); - _buf.put(v); + _buf.b.put(v); } } @@ -702,7 +671,7 @@ public class BasicStream { try { - return _buf.get(); + return _buf.b.get(); } catch(java.nio.BufferUnderflowException ex) { @@ -729,7 +698,7 @@ public class BasicStream final int sz = readSize(); checkFixedSeq(sz, 1); byte[] v = new byte[sz]; - _buf.get(v); + _buf.b.get(v); return v; } catch(java.nio.BufferUnderflowException ex) @@ -742,7 +711,7 @@ public class BasicStream writeBool(boolean v) { expand(1); - _buf.put(v ? (byte)1 : (byte)0); + _buf.b.put(v ? (byte)1 : (byte)0); } public void @@ -758,7 +727,7 @@ public class BasicStream expand(v.length); for(int i = 0; i < v.length; i++) { - _buf.put(v[i] ? (byte)1 : (byte)0); + _buf.b.put(v[i] ? (byte)1 : (byte)0); } } } @@ -768,7 +737,7 @@ public class BasicStream { try { - return _buf.get() == 1; + return _buf.b.get() == 1; } catch(java.nio.BufferUnderflowException ex) { @@ -786,7 +755,7 @@ public class BasicStream boolean[] v = new boolean[sz]; for(int i = 0; i < sz; i++) { - v[i] = _buf.get() == 1; + v[i] = _buf.b.get() == 1; } return v; } @@ -800,7 +769,7 @@ public class BasicStream writeShort(short v) { expand(2); - _buf.putShort(v); + _buf.b.putShort(v); } public void @@ -824,9 +793,9 @@ public class BasicStream { writeSize(v.length); expand(v.length * 2); - java.nio.ShortBuffer shortBuf = _buf.asShortBuffer(); + java.nio.ShortBuffer shortBuf = _buf.b.asShortBuffer(); shortBuf.put(v); - _buf.position(_buf.position() + v.length * 2); + _buf.b.position(_buf.b.position() + v.length * 2); } } @@ -835,7 +804,7 @@ public class BasicStream { try { - return _buf.getShort(); + return _buf.b.getShort(); } catch(java.nio.BufferUnderflowException ex) { @@ -862,9 +831,9 @@ public class BasicStream final int sz = readSize(); checkFixedSeq(sz, 2); short[] v = new short[sz]; - java.nio.ShortBuffer shortBuf = _buf.asShortBuffer(); + java.nio.ShortBuffer shortBuf = _buf.b.asShortBuffer(); shortBuf.get(v); - _buf.position(_buf.position() + sz * 2); + _buf.b.position(_buf.b.position() + sz * 2); return v; } catch(java.nio.BufferUnderflowException ex) @@ -877,7 +846,7 @@ public class BasicStream writeInt(int v) { expand(4); - _buf.putInt(v); + _buf.b.putInt(v); } public void @@ -901,9 +870,9 @@ public class BasicStream { writeSize(v.length); expand(v.length * 4); - java.nio.IntBuffer intBuf = _buf.asIntBuffer(); + java.nio.IntBuffer intBuf = _buf.b.asIntBuffer(); intBuf.put(v); - _buf.position(_buf.position() + v.length * 4); + _buf.b.position(_buf.b.position() + v.length * 4); } } @@ -912,7 +881,7 @@ public class BasicStream { try { - return _buf.getInt(); + return _buf.b.getInt(); } catch(java.nio.BufferUnderflowException ex) { @@ -939,9 +908,9 @@ public class BasicStream final int sz = readSize(); checkFixedSeq(sz, 4); int[] v = new int[sz]; - java.nio.IntBuffer intBuf = _buf.asIntBuffer(); + java.nio.IntBuffer intBuf = _buf.b.asIntBuffer(); intBuf.get(v); - _buf.position(_buf.position() + sz * 4); + _buf.b.position(_buf.b.position() + sz * 4); return v; } catch(java.nio.BufferUnderflowException ex) @@ -954,7 +923,7 @@ public class BasicStream writeLong(long v) { expand(8); - _buf.putLong(v); + _buf.b.putLong(v); } public void @@ -968,9 +937,9 @@ public class BasicStream { writeSize(v.length); expand(v.length * 8); - java.nio.LongBuffer longBuf = _buf.asLongBuffer(); + java.nio.LongBuffer longBuf = _buf.b.asLongBuffer(); longBuf.put(v); - _buf.position(_buf.position() + v.length * 8); + _buf.b.position(_buf.b.position() + v.length * 8); } } @@ -979,7 +948,7 @@ public class BasicStream { try { - return _buf.getLong(); + return _buf.b.getLong(); } catch(java.nio.BufferUnderflowException ex) { @@ -995,9 +964,9 @@ public class BasicStream final int sz = readSize(); checkFixedSeq(sz, 8); long[] v = new long[sz]; - java.nio.LongBuffer longBuf = _buf.asLongBuffer(); + java.nio.LongBuffer longBuf = _buf.b.asLongBuffer(); longBuf.get(v); - _buf.position(_buf.position() + sz * 8); + _buf.b.position(_buf.b.position() + sz * 8); return v; } catch(java.nio.BufferUnderflowException ex) @@ -1010,7 +979,7 @@ public class BasicStream writeFloat(float v) { expand(4); - _buf.putFloat(v); + _buf.b.putFloat(v); } public void @@ -1024,9 +993,9 @@ public class BasicStream { writeSize(v.length); expand(v.length * 4); - java.nio.FloatBuffer floatBuf = _buf.asFloatBuffer(); + java.nio.FloatBuffer floatBuf = _buf.b.asFloatBuffer(); floatBuf.put(v); - _buf.position(_buf.position() + v.length * 4); + _buf.b.position(_buf.b.position() + v.length * 4); } } @@ -1035,7 +1004,7 @@ public class BasicStream { try { - return _buf.getFloat(); + return _buf.b.getFloat(); } catch(java.nio.BufferUnderflowException ex) { @@ -1051,9 +1020,9 @@ public class BasicStream final int sz = readSize(); checkFixedSeq(sz, 4); float[] v = new float[sz]; - java.nio.FloatBuffer floatBuf = _buf.asFloatBuffer(); + java.nio.FloatBuffer floatBuf = _buf.b.asFloatBuffer(); floatBuf.get(v); - _buf.position(_buf.position() + sz * 4); + _buf.b.position(_buf.b.position() + sz * 4); return v; } catch(java.nio.BufferUnderflowException ex) @@ -1066,7 +1035,7 @@ public class BasicStream writeDouble(double v) { expand(8); - _buf.putDouble(v); + _buf.b.putDouble(v); } public void @@ -1080,9 +1049,9 @@ public class BasicStream { writeSize(v.length); expand(v.length * 8); - java.nio.DoubleBuffer doubleBuf = _buf.asDoubleBuffer(); + java.nio.DoubleBuffer doubleBuf = _buf.b.asDoubleBuffer(); doubleBuf.put(v); - _buf.position(_buf.position() + v.length * 8); + _buf.b.position(_buf.b.position() + v.length * 8); } } @@ -1091,7 +1060,7 @@ public class BasicStream { try { - return _buf.getDouble(); + return _buf.b.getDouble(); } catch(java.nio.BufferUnderflowException ex) { @@ -1107,9 +1076,9 @@ public class BasicStream final int sz = readSize(); checkFixedSeq(sz, 8); double[] v = new double[sz]; - java.nio.DoubleBuffer doubleBuf = _buf.asDoubleBuffer(); + java.nio.DoubleBuffer doubleBuf = _buf.b.asDoubleBuffer(); doubleBuf.get(v); - _buf.position(_buf.position() + sz * 8); + _buf.b.position(_buf.b.position() + sz * 8); return v; } catch(java.nio.BufferUnderflowException ex) @@ -1170,14 +1139,14 @@ public class BasicStream } writeSize(b.limit()); expand(b.limit()); - _buf.put(b); + _buf.b.put(b); return; } _stringBytes[i] = (byte)_stringChars[i]; } writeSize(len); expand(len); - _buf.put(_stringBytes, 0, len); + _buf.b.put(_stringBytes, 0, len); } else { @@ -1228,7 +1197,7 @@ public class BasicStream { _stringChars = new char[len]; } - _buf.get(_stringBytes, 0, len); + _buf.b.get(_stringBytes, 0, len); // // It's more efficient to construct a string using a @@ -1356,7 +1325,7 @@ public class BasicStream } public void - readObject(IceInternal.Patcher patcher) + readObject(Patcher patcher) { Ice.Object v = null; @@ -1747,7 +1716,7 @@ public class BasicStream // for(java.util.Iterator i = patchlist.iterator(); i.hasNext(); ) { - IceInternal.Patcher p = (IceInternal.Patcher)i.next(); + Patcher p = (Patcher)i.next(); try { p.patch(v); @@ -1771,25 +1740,25 @@ public class BasicStream public int pos() { - return _buf.position(); + return _buf.b.position(); } public void pos(int n) { - _buf.position(n); + _buf.b.position(n); } public int size() { - return _limit; + return _buf.size(); } public boolean isEmpty() { - return _limit == 0; + return _buf.empty(); } private static class BufferedOutputStream extends java.io.OutputStream @@ -1865,8 +1834,8 @@ public class BasicStream // If the ByteBuffer is backed by an array then we can avoid // an extra copy by using the array directly. // - data = _buf.array(); - offset = _buf.arrayOffset(); + data = _buf.b.array(); + offset = _buf.b.arrayOffset(); } catch(Exception ex) { @@ -1874,7 +1843,7 @@ public class BasicStream // Otherwise, allocate an array to hold a copy of the uncompressed data. // data = new byte[size()]; - _buf.get(data); + _buf.b.get(data); } try @@ -1922,7 +1891,7 @@ public class BasicStream // Copy the header from the uncompressed stream to the // compressed one. // - cstream._buf.put(data, offset, headerSize); + cstream._buf.b.put(data, offset, headerSize); // // Add the size of the uncompressed stream before the @@ -1933,7 +1902,7 @@ public class BasicStream // // Add the compressed message body. // - cstream._buf.put(compressed, 0, compressedLen); + cstream._buf.b.put(compressed, 0, compressedLen); return cstream; } @@ -1960,8 +1929,8 @@ public class BasicStream // If the ByteBuffer is backed by an array then we can avoid // an extra copy by using the array directly. // - compressed = _buf.array(); - offset = _buf.arrayOffset(); + compressed = _buf.b.array(); + offset = _buf.b.arrayOffset(); } catch(Exception ex) { @@ -1969,7 +1938,7 @@ public class BasicStream // Otherwise, allocate an array to hold a copy of the compressed data. // compressed = new byte[size()]; - _buf.get(compressed); + _buf.b.get(compressed); } BasicStream ucStream = new BasicStream(_instance); @@ -2020,34 +1989,19 @@ public class BasicStream // Copy the header from the compressed stream to the uncompressed one. // ucStream.pos(0); - ucStream._buf.put(compressed, offset, headerSize); + ucStream._buf.b.put(compressed, offset, headerSize); return ucStream; } private void - expand(int size) + expand(int n) { - if(_buf.position() == _limit) + if(!_unlimited && _buf.b != null && _buf.b.position() + n > _messageSizeMax) { - int oldLimit = _limit; - _limit += size; - if(!_unlimited && _limit > _messageSizeMax) - { - throw new Ice.MemoryLimitException(); - } - if(_limit > _capacity) - { - final int cap2 = _capacity << 1; - int newCapacity = cap2 > _limit ? cap2 : _limit; - _buf.limit(oldLimit); - int pos = _buf.position(); - reallocate(newCapacity); - _capacity = _buf.capacity(); - _buf.limit(_capacity); - _buf.position(pos); - } + throw new Ice.MemoryLimitException(); } + _buf.expand(n); } private static final class DynamicObjectFactory implements Ice.ObjectFactory @@ -2334,52 +2288,9 @@ public class BasicStream return buf.toString(); } - private void - allocate(int size) - { - java.nio.ByteBuffer buf = null; - try - { - //buf = java.nio.ByteBuffer.allocateDirect(size); - buf = java.nio.ByteBuffer.allocate(size); - } - catch(OutOfMemoryError ex) - { - Ice.MarshalException e = new Ice.MarshalException(); - e.reason = "OutOfMemoryError occurred while allocating a ByteBuffer"; - e.initCause(ex); - throw e; - } - buf.order(java.nio.ByteOrder.LITTLE_ENDIAN); - _buf = buf; - } - - private void - reallocate(int size) - { - // - // Limit the buffer size to MessageSizeMax - // - if(!_unlimited) - { - size = size > _messageSizeMax ? _messageSizeMax : size; - } - - java.nio.ByteBuffer old = _buf; - assert(old != null); - - allocate(size); - assert(_buf != null); - - old.position(0); - _buf.put(old); - } - - private IceInternal.Instance _instance; + private Instance _instance; + private Buffer _buf; private Object _closure; - private java.nio.ByteBuffer _buf; - private int _capacity; // Cache capacity to avoid excessive method calls. - private int _limit; // Cache limit to avoid excessive method calls. private byte[] _stringBytes; // Reusable array for reading strings. private char[] _stringChars; // Reusable array for reading strings. diff --git a/java/src/IceInternal/BatchOutgoing.java b/java/src/IceInternal/BatchOutgoing.java new file mode 100644 index 00000000000..1e0071f047a --- /dev/null +++ b/java/src/IceInternal/BatchOutgoing.java @@ -0,0 +1,92 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2007 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +package IceInternal; + +public final class BatchOutgoing implements OutgoingMessageCallback +{ + public + BatchOutgoing(Ice.ConnectionI connection, Instance instance) + { + _connection = connection; + _sent = false; + _os = new BasicStream(instance); + } + + public + BatchOutgoing(RequestHandler handler) + { + _handler = handler; + _sent = false; + _os = new BasicStream(handler.getReference().getInstance()); + } + + public void + invoke() + { + assert(_handler != null || _connection != null); + + if(_handler != null && !_handler.flushBatchRequests(this) || + _connection != null && !_connection.flushBatchRequests(this)) + synchronized(this) + { + while(_exception == null && !_sent) + { + try + { + wait(); + } + catch(java.lang.InterruptedException ex) + { + } + } + + if(_exception != null) + { + throw _exception; + } + } + } + + public void + sent(boolean notify) + { + if(notify) + { + synchronized(this) + { + _sent = true; + notify(); + } + } + else + { + _sent = true; + } + } + + public synchronized void + finished(Ice.LocalException ex) + { + _exception = ex; + notify(); + } + + public BasicStream + os() + { + return _os; + } + + private RequestHandler _handler; + private Ice.ConnectionI _connection; + private BasicStream _os; + private boolean _sent; + private Ice.LocalException _exception; +} diff --git a/java/src/IceInternal/BatchOutgoingAsync.java b/java/src/IceInternal/BatchOutgoingAsync.java new file mode 100644 index 00000000000..94e15c2dca3 --- /dev/null +++ b/java/src/IceInternal/BatchOutgoingAsync.java @@ -0,0 +1,106 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2007 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +package IceInternal; + +public abstract class BatchOutgoingAsync implements OutgoingAsyncMessageCallback +{ + public + BatchOutgoingAsync() + { + } + + public abstract void ice_exception(Ice.LocalException ex); + + public final BasicStream + __os() + { + return __os; + } + + public final void + __sent(final Ice.ConnectionI connection) + { + synchronized(_monitor) + { + cleanup(); + } + } + + public final void + __finished(Ice.LocalException exc) + { + try + { + ice_exception(exc); + } + catch(java.lang.Exception ex) + { + warning(ex); + } + finally + { + synchronized(_monitor) + { + cleanup(); + } + } + } + + protected final void + __prepare(Instance instance) + { + synchronized(_monitor) + { + while(__os != null) + { + try + { + _monitor.wait(); + } + catch(InterruptedException ex) + { + } + } + + assert(__os == null); + __os = new BasicStream(instance); + } + } + + private final void + warning(java.lang.Exception ex) + { + if(__os != null) // Don't print anything if cleanup() was already called. + { + if(__os.instance().initializationData().properties.getPropertyAsIntWithDefault( + "Ice.Warn.AMICallback", 1) > 0) + { + java.io.StringWriter sw = new java.io.StringWriter(); + java.io.PrintWriter pw = new java.io.PrintWriter(sw); + IceUtil.OutputBase out = new IceUtil.OutputBase(pw); + out.setUseTab(false); + out.print("exception raised by AMI callback:\n"); + ex.printStackTrace(pw); + pw.flush(); + __os.instance().initializationData().logger.warning(sw.toString()); + } + } + } + + private final void + cleanup() + { + __os = null; + _monitor.notify(); + } + + protected BasicStream __os; + private final java.lang.Object _monitor = new java.lang.Object(); +} diff --git a/java/src/IceInternal/Buffer.java b/java/src/IceInternal/Buffer.java new file mode 100644 index 00000000000..8795f5fccde --- /dev/null +++ b/java/src/IceInternal/Buffer.java @@ -0,0 +1,183 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2007 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +package IceInternal; + +// +// An instance of java.nio.ByteBuffer cannot grow beyond its initial capacity. +// This class wraps a ByteBuffer and supports reallocation. +// +public class Buffer +{ + public + Buffer(int maxCapacity) + { + b = null; + _size = 0; + _capacity = 0; + _maxCapacity = maxCapacity; + } + + public int + size() + { + return _size; + } + + public boolean + empty() + { + return _size == 0; + } + + public void + clear() + { + b = null; + _size = 0; + _capacity = 0; + } + + // + // Call expand(n) to add room for n additional bytes. Note that expand() + // examines the current position of the buffer first; we don't want to + // expand the buffer if the caller is writing to a location that is + // already in the buffer. + // + public void + expand(int n) + { + final int sz = b == null ? n : b.position() + n; + if(sz > _size) + { + resize(sz, false); + } + } + + public void + resize(int n, boolean reading) + { + if(n == 0) + { + clear(); + } + else if(n > _capacity) + { + reserve(n); + } + _size = n; + + // + // When used for reading, we want to set the buffer's limit to the new size. + // + if(reading) + { + b.limit(_size); + } + } + + public void + reset() + { + if(_size > 0 && _size * 2 < _capacity) + { + // + // If the current buffer size is smaller than the + // buffer capacity, we shrink the buffer memory to the + // current size. This is to avoid holding on to too much + // memory if it's not needed anymore. + // + if(++_shrinkCounter > 2) + { + reserve(_size); + _shrinkCounter = 0; + } + } + else + { + _shrinkCounter = 0; + } + _size = 0; + if(b != null) + { + b.limit(b.capacity()); + b.position(0); + } + } + + private void + reserve(int n) + { + if(n > _capacity) + { + _capacity = java.lang.Math.max(n, java.lang.Math.min(2 * _capacity, _maxCapacity)); + _capacity = java.lang.Math.max(240, _capacity); + } + else if(n < _capacity) + { + _capacity = n; + } + else + { + return; + } + + try + { + java.nio.ByteBuffer buf; + + if(DIRECT) + { + buf = java.nio.ByteBuffer.allocateDirect(_capacity); + } + else + { + buf = java.nio.ByteBuffer.allocate(_capacity); + } + + if(b == null) + { + b = buf; + } + else + { + final int pos = b.position(); + b.position(0); + b.limit(java.lang.Math.min(_capacity, b.capacity())); + buf.put(b); + b = buf; + b.limit(b.capacity()); + b.position(pos); + } + + b.order(java.nio.ByteOrder.LITTLE_ENDIAN); + } + catch(OutOfMemoryError ex) + { + Ice.MarshalException e = new Ice.MarshalException(); + e.reason = "OutOfMemoryError occurred while allocating a ByteBuffer"; + e.initCause(ex); + throw e; + } + } + + public java.nio.ByteBuffer b; + + private int _size; + private int _capacity; // Cache capacity to avoid excessive method calls. + private int _maxCapacity; + private int _shrinkCounter; + + // + // This flag indicates whether we should use direct byte buffers. It is set to false + // because direct byte buffers have been known to cause problems in the past (for + // example, see bug 1582). + // + private static final boolean DIRECT = false; +} diff --git a/java/src/IceInternal/ConnectRequestHandler.java b/java/src/IceInternal/ConnectRequestHandler.java new file mode 100644 index 00000000000..45aa6c959b9 --- /dev/null +++ b/java/src/IceInternal/ConnectRequestHandler.java @@ -0,0 +1,480 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2007 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +package IceInternal; + +public class ConnectRequestHandler + implements RequestHandler, Reference.GetConnectionCallback, RouterInfo.AddProxyCallback +{ + static class Request + { + Request(BasicStream os) + { + this.os = new BasicStream(os.instance()); + this.os.swap(os); + } + + Request(OutgoingAsync out) + { + this.out = out; + } + + Request(BatchOutgoingAsync out) + { + this.batchOut = out; + } + + OutgoingAsync out = null; + BatchOutgoingAsync batchOut = null; + BasicStream os = null; + } + + public void + prepareBatchRequest(BasicStream os) + { + synchronized(this) + { + while(_batchRequestInProgress) + { + try + { + wait(); + } + catch(java.lang.InterruptedException ex) + { + } + } + + if(!initialized()) + { + _batchStream.swap(os); + _batchRequestInProgress = true; + return; + } + } + + _connection.prepareBatchRequest(os); + } + + public void + finishBatchRequest(BasicStream os) + { + synchronized(this) + { + if(!initialized()) + { + assert(_batchRequestInProgress); + _batchRequestInProgress = false; + notifyAll(); + + _batchStream.swap(os); + + if(!_batchAutoFlush && + _batchStream.size() + _batchRequestsSize > _reference.getInstance().messageSizeMax()) + { + throw new Ice.MemoryLimitException(); + } + + _requests.add(new Request(_batchStream)); + return; + } + } + + _connection.finishBatchRequest(os, _compress); + } + + public void + abortBatchRequest() + { + synchronized(this) + { + if(!initialized()) + { + assert(_batchRequestInProgress); + _batchRequestInProgress = false; + notifyAll(); + + BasicStream dummy = new BasicStream(_reference.getInstance(), _batchAutoFlush); + _batchStream.swap(dummy); + _batchRequestsSize = Protocol.requestBatchHdr.length; + return; + } + } + + _connection.abortBatchRequest(); + } + + public Ice.ConnectionI + sendRequest(Outgoing out) + throws LocalExceptionWrapper + { + return (!getConnection(true).sendRequest(out, _compress, _response) || _response) ? _connection : null; + } + + public void + sendAsyncRequest(OutgoingAsync out) + { + try + { + synchronized(this) + { + if(!initialized()) + { + _requests.add(new Request(out)); + return; + } + } + + _connection.sendAsyncRequest(out, _compress, _response); + } + catch(LocalExceptionWrapper ex) + { + out.__finished(ex); + } + catch(Ice.LocalException ex) + { + out.__finished(ex); + } + } + + public boolean + flushBatchRequests(BatchOutgoing out) + { + return getConnection(true).flushBatchRequests(out); + } + + public void + flushAsyncBatchRequests(BatchOutgoingAsync out) + { + try + { + synchronized(this) + { + if(!initialized()) + { + _requests.add(new Request(out)); + return; + } + } + + _connection.flushAsyncBatchRequests(out); + } + catch(Ice.LocalException ex) + { + out.__finished(ex); + } + } + + public Outgoing + getOutgoing(String operation, Ice.OperationMode mode, java.util.Map context) + throws LocalExceptionWrapper + { + synchronized(this) + { + if(!initialized()) + { + return new IceInternal.Outgoing(this, operation, mode, context); + } + } + + return _connection.getOutgoing(this, operation, mode, context); + } + + public void + reclaimOutgoing(Outgoing out) + { + synchronized(this) + { + if(_connection == null) + { + return; + } + } + + _connection.reclaimOutgoing(out); + } + + public Reference + getReference() + { + return _reference; + } + + synchronized public Ice.ConnectionI + getConnection(boolean wait) + { + if(wait) + { + // + // Wait for the connection establishment to complete or fail. + // + while(!_initialized && _exception == null) + { + try + { + wait(); + } + catch(java.lang.InterruptedException ex) + { + } + } + } + + if(_exception != null) + { + throw _exception; + } + else + { + assert(!wait || _initialized); + return _connection; + } + } + + // + // Implementation of Reference.GetConnectionCallback + // + + public void + setConnection(Ice.ConnectionI connection, boolean compress) + { + synchronized(this) + { + _connection = connection; + _compress = compress; + } + + // + // If this proxy is for a non-local object, and we are using a router, then + // add this proxy to the router info object. + // + RouterInfo ri = _reference.getRouterInfo(); + if(ri != null) + { + if(!ri.addProxy(_proxy, this)) + { + return; // The request handler will be initialized once addProxy returns. + } + } + + flushRequests(); + } + + public void + setException(Ice.LocalException ex) + { + synchronized(this) + { + _exception = ex; + _proxy = null; // Break cyclic reference count. + _delegate = null; // Break cyclic reference count. + notifyAll(); + } + + java.util.Iterator p = _requests.iterator(); + while(p.hasNext()) + { + Request request = (Request)p.next(); + if(request.out != null) + { + request.out.__finished(ex); + } + else if(request.batchOut != null) + { + request.batchOut.__finished(ex); + } + } + _requests.clear(); + } + + // + // Implementation of RouterInfo.AddProxyCallback + // + public void + addedProxy() + { + flushRequests(); + } + + public + ConnectRequestHandler(Reference ref, Ice.ObjectPrx proxy, Ice._ObjectDelM delegate) + { + _reference = ref; + _response = _reference.getMode() == Reference.ModeTwoway; + _proxy = (Ice.ObjectPrxHelperBase)proxy; + _delegate = delegate; + _batchAutoFlush = ref.getInstance().initializationData().properties.getPropertyAsIntWithDefault( + "Ice.BatchAutoFlush", 1) > 0 ? true : false; + _batchStream = new BasicStream(ref.getInstance(), _batchAutoFlush); + _batchRequestInProgress = false; + _batchRequestsSize = Protocol.requestBatchHdr.length; + _updateRequestHandler = false; + } + + public RequestHandler + connect() + { + _reference.getConnection(this); + + synchronized(this) + { + if(_connection != null) + { + return new ConnectionRequestHandler(_reference, _connection, _compress); + } + else + { + _updateRequestHandler = true; + return this; + } + } + } + + private boolean + initialized() + { + if(_initialized) + { + assert(_connection != null); + return true; + } + else + { + while(_flushing) + { + try + { + wait(); + } + catch(java.lang.InterruptedException ex) + { + } + } + + if(_exception != null) + { + throw _exception; + } + else + { + return _initialized; + } + } + } + + private void + flushRequests() + { + synchronized(this) + { + assert(_connection != null); + + if(_batchRequestInProgress) + { + try + { + wait(); + } + catch(java.lang.InterruptedException ex) + { + } + } + + // + // We set the _flushing flag to true to prevent any additional queuing. Callers + // might block for a little while as the queued requests are being sent but this + // shouldn't be an issue as the request sends are non-blocking. + // + _flushing = true; + } + + java.util.Iterator p = _requests.iterator(); // _requests is immutable when _flushing = true + while(p.hasNext()) + { + Request request = (Request)p.next(); + if(request.out != null) + { + try + { + _connection.sendAsyncRequest(request.out, _compress, _response); + } + catch(LocalExceptionWrapper ex) + { + request.out.__finished(ex); + } + catch(Ice.LocalException ex) + { + request.out.__finished(ex); + } + } + else if(request.batchOut != null) + { + try + { + _connection.flushAsyncBatchRequests(request.batchOut); + } + catch(Ice.LocalException ex) + { + request.batchOut.__finished(ex); + } + } + else + { + // + // TODO: Add sendBatchRequest() method to ConnectionI? + // + try + { + BasicStream os = new BasicStream(request.os.instance()); + _connection.prepareBatchRequest(os); + request.os.pos(0); + os.writeBlob(request.os.readBlob(request.os.size())); + _connection.finishBatchRequest(os, _compress); + } + catch(Ice.LocalException ex) + { + _connection.abortBatchRequest(); + _exception = ex; + } + } + } + _requests.clear(); + + synchronized(this) + { + _initialized = true; + _flushing = false; + notifyAll(); + } + + if(_updateRequestHandler && _exception == null) + { + _proxy.__setRequestHandler(_delegate, new ConnectionRequestHandler(_reference, _connection, _compress)); + } + _proxy = null; // Break cyclic reference count. + _delegate = null; // Break cyclic reference count. + } + + private final Reference _reference; + private final boolean _batchAutoFlush; + private Ice.ObjectPrxHelperBase _proxy; + private Ice._ObjectDelM _delegate; + private boolean _initialized = false; + private boolean _flushing = false; + private Ice.ConnectionI _connection = null; + private boolean _compress = false; + private boolean _response; + private Ice.LocalException _exception = null; + + private java.util.ArrayList _requests = new java.util.ArrayList(); + private boolean _batchRequestInProgress; + private int _batchRequestsSize; + private BasicStream _batchStream; + private boolean _updateRequestHandler; +} diff --git a/java/src/IceInternal/ConnectionMonitor.java b/java/src/IceInternal/ConnectionMonitor.java index 7f46d20f19b..3806b077b13 100644 --- a/java/src/IceInternal/ConnectionMonitor.java +++ b/java/src/IceInternal/ConnectionMonitor.java @@ -59,7 +59,7 @@ public final class ConnectionMonitor implements IceInternal.TimerTask } public void - run() + runTimerTask() { java.util.HashSet connections = new java.util.HashSet(); diff --git a/java/src/IceInternal/ConnectionRequestHandler.java b/java/src/IceInternal/ConnectionRequestHandler.java new file mode 100644 index 00000000000..2f1b1c031ed --- /dev/null +++ b/java/src/IceInternal/ConnectionRequestHandler.java @@ -0,0 +1,134 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2007 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +package IceInternal; + +public class ConnectionRequestHandler implements RequestHandler +{ + public void + prepareBatchRequest(BasicStream out) + { + _connection.prepareBatchRequest(out); + } + + public void + finishBatchRequest(BasicStream out) + { + _connection.finishBatchRequest(out, _compress); + } + + public void + abortBatchRequest() + { + _connection.abortBatchRequest(); + } + + public Ice.ConnectionI + sendRequest(Outgoing out) + throws LocalExceptionWrapper + { + return (!_connection.sendRequest(out, _compress, _response) || _response) ? _connection : null; + } + + public void + sendAsyncRequest(OutgoingAsync out) + { + try + { + _connection.sendAsyncRequest(out, _compress, _response); + } + catch(LocalExceptionWrapper ex) + { + out.__finished(ex); + } + catch(Ice.LocalException ex) + { + out.__finished(ex); + } + } + + public boolean + flushBatchRequests(BatchOutgoing out) + { + return _connection.flushBatchRequests(out); + } + + public void + flushAsyncBatchRequests(BatchOutgoingAsync out) + { + try + { + _connection.flushAsyncBatchRequests(out); + } + catch(Ice.LocalException ex) + { + out.__finished(ex); + } + } + + public Outgoing + getOutgoing(String operation, Ice.OperationMode mode, java.util.Map context) + throws LocalExceptionWrapper + { + return _connection.getOutgoing(this, operation, mode, context); + } + + public void + reclaimOutgoing(Outgoing out) + { + _connection.reclaimOutgoing(out); + } + + public Reference + getReference() + { + return _reference; + } + + public Ice.ConnectionI + getConnection(boolean wait) + { + return _connection; + } + + public + ConnectionRequestHandler(Reference ref, Ice.ObjectPrx proxy) + { + _reference = ref; + _response = _reference.getMode() == Reference.ModeTwoway; + + Ice.BooleanHolder compress = new Ice.BooleanHolder(); + _connection = _reference.getConnection(compress); + _compress = compress.value; + + // + // If this proxy is for a non-local object, and we are using a router, then + // add this proxy to the router info object. + // + IceInternal.RouterInfo ri = _reference.getRouterInfo(); + if(ri != null) + { + ri.addProxy(proxy); + } + } + + public + ConnectionRequestHandler(Reference ref, Ice.ConnectionI connection, boolean compress) + { + _reference = ref; + _response = _reference.getMode() == Reference.ModeTwoway; + _connection = connection; + _compress = compress; + } + + private final Reference _reference; + private final boolean _response; + private final Ice.ConnectionI _connection; + private final boolean _compress; +} diff --git a/java/src/IceInternal/Connector.java b/java/src/IceInternal/Connector.java index 7e4c5d16f37..52b71a84a6a 100644 --- a/java/src/IceInternal/Connector.java +++ b/java/src/IceInternal/Connector.java @@ -12,6 +12,7 @@ package IceInternal; public interface Connector { Transceiver connect(int timeout); + short type(); String toString(); diff --git a/java/src/IceInternal/DirectReference.java b/java/src/IceInternal/DirectReference.java index 56224453de5..2a60fda7e7a 100644 --- a/java/src/IceInternal/DirectReference.java +++ b/java/src/IceInternal/DirectReference.java @@ -116,7 +116,7 @@ public class DirectReference extends RoutableReference return getInstance().referenceFactory().create(getIdentity(), getContext(), getFacet(), getMode(), getSecure(), getPreferSecure(), newAdapterId, getRouterInfo(), locatorInfo, getCollocationOptimization(), getCacheConnection(), - getEndpointSelection(), getThreadPerConnection(), + getEndpointSelection(), getThreadPerConnection(), getLocatorCacheTimeout()); } @@ -188,28 +188,57 @@ public class DirectReference extends RoutableReference public Ice.ConnectionI getConnection(Ice.BooleanHolder comp) { - EndpointI[] endpts = super.getRoutedEndpoints(); - applyOverrides(endpts); - - if(endpts.length == 0) + if(getRouterInfo() != null) { - endpts = _endpoints; // Endpoint overrides are already applied on these endpoints. + // + // If we route, we send everything to the router's client + // proxy endpoints. + // + EndpointI[] endpts = getRouterInfo().getClientEndpoints(); + if(endpts.length > 0) + { + applyOverrides(endpts); + return createConnection(endpts, comp); + } } - Ice.ConnectionI connection = createConnection(endpts, comp); + return createConnection(_endpoints, comp); + } - // - // If we have a router, set the object adapter for this router - // (if any) to the new connection, so that callbacks from the - // router can be received over this new connection. - // + public void + getConnection(final GetConnectionCallback callback) + { if(getRouterInfo() != null) { - connection.setAdapter(getRouterInfo().getAdapter()); + // + // If we route, we send everything to the router's client + // proxy endpoints. + // + getRouterInfo().getClientEndpoints(new RouterInfo.GetClientEndpointsCallback() + { + public void + setEndpoints(EndpointI[] endpts) + { + if(endpts.length > 0) + { + applyOverrides(endpts); + createConnection(endpts, callback); + return; + } + + createConnection(_endpoints, callback); + } + + public void + setException(Ice.LocalException ex) + { + callback.setException(ex); + } + }); + return; } - assert(connection != null); - return connection; + createConnection(_endpoints, callback); } public boolean diff --git a/java/src/IceInternal/EndpointFactoryManager.java b/java/src/IceInternal/EndpointFactoryManager.java index 52b2e21888e..fc772b3f6fe 100644 --- a/java/src/IceInternal/EndpointFactoryManager.java +++ b/java/src/IceInternal/EndpointFactoryManager.java @@ -80,7 +80,7 @@ public final class EndpointFactoryManager EndpointI e = f.create(s.substring(m.end()), server); BasicStream bs = new BasicStream(_instance, true); e.streamWrite(bs); - java.nio.ByteBuffer buf = bs.prepareRead(); + java.nio.ByteBuffer buf = bs.getBuffer(); buf.position(0); short type = bs.readShort(); EndpointI ue = new IceInternal.UnknownEndpointI(type, bs); @@ -110,8 +110,8 @@ public final class EndpointFactoryManager // BasicStream bs = new BasicStream(_instance, true); ue.streamWrite(bs); - java.nio.ByteBuffer buf = bs.prepareRead(); - buf.position(0); + Buffer buf = bs.getBuffer(); + buf.b.position(0); short type = bs.readShort(); return f.read(bs); } @@ -136,7 +136,6 @@ public final class EndpointFactoryManager return f.read(s); } } - return new UnknownEndpointI(type, s); } diff --git a/java/src/IceInternal/EndpointHostResolver.java b/java/src/IceInternal/EndpointHostResolver.java new file mode 100644 index 00000000000..4ef6d756e0f --- /dev/null +++ b/java/src/IceInternal/EndpointHostResolver.java @@ -0,0 +1,166 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2007 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +package IceInternal; + +public class EndpointHostResolver +{ + EndpointHostResolver(Instance instance) + { + _instance = instance; + + _thread = new HelperThread(); + _thread.start(); + } + + synchronized public void + resolve(String host, int port, EndpointI endpoint, EndpointI_connectors callback) + { + // + // TODO: Optimize to avoid the lookup if the given host is a textual IPv4 or IPv6 + // address. This requires implementing parsing of IPv4/IPv6 addresses (Java does + // not provide such methods). + // + + assert(!_destroyed); + + ResolveEntry entry = new ResolveEntry(); + entry.host = host; + entry.port = port; + entry.endpoint = endpoint; + entry.callback = callback; + _queue.add(entry); + notify(); + } + + synchronized public void + destroy() + { + assert(!_destroyed); + _destroyed = true; + notify(); + } + + public void + joinWithThread() + { + if(_thread != null) + { + try + { + _thread.join(); + } + catch(InterruptedException ex) + { + } + } + } + + public void + run() + { + while(true) + { + ResolveEntry resolve; + synchronized(this) + { + while(!_destroyed && _queue.isEmpty()) + { + try + { + wait(); + } + catch(java.lang.InterruptedException ex) + { + } + } + + if(_destroyed) + { + break; + } + + resolve = (ResolveEntry)_queue.removeFirst(); + } + + resolve.callback.connectors(resolve.endpoint.connectors(Network.getAddresses(resolve.host, resolve.port))); + } + + java.util.Iterator p = _queue.iterator(); + while(p.hasNext()) + { + ((ResolveEntry)p.next()).callback.exception(new Ice.CommunicatorDestroyedException()); + } + _queue.clear(); + } + + class ResolveEntry + { + String host; + int port; + EndpointI endpoint; + EndpointI_connectors callback; + } + + private final Instance _instance; + private boolean _destroyed; + private java.util.LinkedList _queue = new java.util.LinkedList(); + + private final class HelperThread extends Thread + { + HelperThread() + { + String threadName = _instance.initializationData().properties.getProperty("Ice.ProgramName"); + if(threadName.length() > 0) + { + threadName += "-"; + } + setName(threadName + "Ice.EndpointHostResolverThread"); + } + + public void + run() + { + if(_instance.initializationData().threadHook != null) + { + _instance.initializationData().threadHook.start(); + } + + try + { + EndpointHostResolver.this.run(); + } + catch(Ice.LocalException ex) + { + java.io.StringWriter sw = new java.io.StringWriter(); + java.io.PrintWriter pw = new java.io.PrintWriter(sw); + ex.printStackTrace(pw); + pw.flush(); + String s = "exception in endpoint host resolver thread " + getName() + ":\n" + sw.toString(); + _instance.initializationData().logger.error(s); + } + catch(java.lang.Exception ex) + { + java.io.StringWriter sw = new java.io.StringWriter(); + java.io.PrintWriter pw = new java.io.PrintWriter(sw); + ex.printStackTrace(pw); + pw.flush(); + String s = "unknown exception in endpoint host resolver thread " + getName() + ":\n" + sw.toString(); + _instance.initializationData().logger.error(s); + } + + if(_instance.initializationData().threadHook != null) + { + _instance.initializationData().threadHook.stop(); + } + } + } + + private HelperThread _thread; +} diff --git a/java/src/IceInternal/EndpointI.java b/java/src/IceInternal/EndpointI.java index c0a29e2187e..8086bf138fd 100644 --- a/java/src/IceInternal/EndpointI.java +++ b/java/src/IceInternal/EndpointI.java @@ -86,7 +86,8 @@ abstract public class EndpointI implements Ice.Endpoint, java.lang.Comparable // Return connectors for this endpoint, or empty list if no connector // is available. // - public abstract java.util.ArrayList connectors(); + public abstract java.util.List connectors(); + public abstract void connectors_async(EndpointI_connectors callback); // // Return an acceptor for this endpoint, or null if no acceptors @@ -101,12 +102,12 @@ abstract public class EndpointI implements Ice.Endpoint, java.lang.Comparable // Expand endpoint out in to separate endpoints for each local // host if listening on INADDR_NAY. // - public abstract java.util.ArrayList expand(); + public abstract java.util.List expand(); // - // Check whether the endpoint is equivalent to a specific Connector. + // Check whether the endpoint is equivalent to another one. // - public abstract boolean equivalent(Connector connector); + public abstract boolean equivalent(EndpointI endpoint); // // Compare endpoints for sorting purposes. @@ -120,4 +121,15 @@ abstract public class EndpointI implements Ice.Endpoint, java.lang.Comparable // TODO: Remove this when we no longer support SSL for JDK 1.4. // public abstract boolean requiresThreadPerConnection(); + + public java.util.List + connectors(java.util.List addresses) + { + // + // This method must be extended by endpoints which use the EndpointHostResolver to create + // connectors from IP addresses. + // + assert(false); + return null; + } } diff --git a/java/src/IceInternal/EndpointI_connectors.java b/java/src/IceInternal/EndpointI_connectors.java new file mode 100644 index 00000000000..dd4f8bba305 --- /dev/null +++ b/java/src/IceInternal/EndpointI_connectors.java @@ -0,0 +1,16 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2007 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +package IceInternal; + +public interface EndpointI_connectors +{ + void connectors(java.util.List connectors); + void exception(Ice.LocalException ex); +}
\ No newline at end of file diff --git a/java/src/IceInternal/EventHandler.java b/java/src/IceInternal/EventHandler.java index 3a0ed6231a2..11241725083 100644 --- a/java/src/IceInternal/EventHandler.java +++ b/java/src/IceInternal/EventHandler.java @@ -25,12 +25,17 @@ public abstract class EventHandler // Read data via the event handler. May only be called if // readable() returns true. // - // NOTE: In Java, read returns true if the handler has more data - // data available, and therefore read should be called again. - // abstract public boolean read(BasicStream is); // + // In Java, it's possible that the transceiver reads more data + // than what was really asked. If this is the case, hasMoreData() + // returns true and the handler read() method should be called + // again (without doing a select()). + // + abstract public boolean hasMoreData(); + + // // A complete message has been received. // abstract public void message(BasicStream stream, ThreadPool threadPool); @@ -68,7 +73,8 @@ public abstract class EventHandler protected Instance _instance; // - // The _stream data member is for use by ThreadPool only. + // The _stream data member is only for use by the ThreadPool or by the + // connection for validation. // - BasicStream _stream; + protected BasicStream _stream; } diff --git a/java/src/IceInternal/FixedReference.java b/java/src/IceInternal/FixedReference.java index 055dc12b288..d86791e7fb0 100644 --- a/java/src/IceInternal/FixedReference.java +++ b/java/src/IceInternal/FixedReference.java @@ -198,6 +198,20 @@ public class FixedReference extends Reference return connection; } + public void + getConnection(GetConnectionCallback callback) + { + try + { + Ice.BooleanHolder compress = new Ice.BooleanHolder(); + callback.setConnection(getConnection(compress), compress.value); + } + catch(Ice.LocalException ex) + { + callback.setException(ex); + } + } + public boolean equals(java.lang.Object obj) { diff --git a/java/src/IceInternal/IncomingConnectionFactory.java b/java/src/IceInternal/IncomingConnectionFactory.java index 5f2fdb15201..2d01a11be95 100644 --- a/java/src/IceInternal/IncomingConnectionFactory.java +++ b/java/src/IceInternal/IncomingConnectionFactory.java @@ -9,7 +9,7 @@ package IceInternal; -public final class IncomingConnectionFactory extends EventHandler +public final class IncomingConnectionFactory extends EventHandler implements Ice.ConnectionI.StartCallback { public synchronized void activate() @@ -73,7 +73,7 @@ public final class IncomingConnectionFactory extends EventHandler waitUntilFinished() { Thread threadPerIncomingConnectionFactory = null; - java.util.LinkedList connections; + java.util.LinkedList connections = null; synchronized(this) { @@ -104,12 +104,10 @@ public final class IncomingConnectionFactory extends EventHandler // We want to wait until all connections are finished outside the // thread synchronization. // - // For consistency with C#, we set _connections to null rather than to a - // new empty list so that our finalizer does not try to invoke any - // methods on member objects. - // - connections = _connections; - _connections = null; + if(_connections != null) + { + connections = new java.util.LinkedList(_connections); + } } if(threadPerIncomingConnectionFactory != null) @@ -136,6 +134,16 @@ public final class IncomingConnectionFactory extends EventHandler connection.waitUntilFinished(); } } + + synchronized(this) + { + // + // For consistency with C#, we set _connections to null rather than to a + // new empty list so that our finalizer does not try to invoke any + // methods on member objects. + // + _connections = null; + } } public EndpointI @@ -145,7 +153,7 @@ public final class IncomingConnectionFactory extends EventHandler return _endpoint; } - public synchronized Ice.ConnectionI[] + public synchronized java.util.LinkedList connections() { java.util.LinkedList connections = new java.util.LinkedList(); @@ -157,26 +165,24 @@ public final class IncomingConnectionFactory extends EventHandler while(p.hasNext()) { Ice.ConnectionI connection = (Ice.ConnectionI)p.next(); - if(!connection.isDestroyed()) + if(connection.isActiveOrHolding()) { connections.add(connection); } } - - Ice.ConnectionI[] arr = new Ice.ConnectionI[connections.size()]; - connections.toArray(arr); - return arr; + + return connections; } public void flushBatchRequests() { - Ice.ConnectionI[] c = connections(); // connections() is synchronized, so no need to synchronize here. - for(int i = 0; i < c.length; i++) + java.util.Iterator p = connections().iterator(); // connections() is synchronized, no need to synchronize here. + while(p.hasNext()) { try { - c[i].flushBatchRequests(); + ((Ice.ConnectionI)p.next()).flushBatchRequests(); } catch(Ice.LocalException ex) { @@ -211,6 +217,14 @@ public final class IncomingConnectionFactory extends EventHandler return false; } + public boolean + hasMoreData() + { + assert(!_threadPerConnection); // Only for use with a thread pool. + assert(false); // Must not be called. + return false; + } + public void message(BasicStream unused, ThreadPool threadPool) { @@ -218,9 +232,9 @@ public final class IncomingConnectionFactory extends EventHandler Ice.ConnectionI connection = null; - synchronized(this) + try { - try + synchronized(this) { if(_state != StateActive) { @@ -244,7 +258,7 @@ public final class IncomingConnectionFactory extends EventHandler // // Now accept a new connection. // - Transceiver transceiver; + Transceiver transceiver = null; try { transceiver = _acceptor.accept(0); @@ -275,47 +289,38 @@ public final class IncomingConnectionFactory extends EventHandler { assert(!_threadPerConnection); connection = new Ice.ConnectionI(_instance, transceiver, _endpoint, _adapter, false); - connection.start(); } catch(Ice.LocalException ex) { + try + { + transceiver.close(); + } + catch(Ice.LocalException exc) + { + // Ignore + } + + if(_warn) + { + warning(ex); + } return; } _connections.add(connection); } - finally - { - // - // This makes sure that we promote a follower before - // we leave the scope of the mutex above, but after we - // call accept() (if we call it). - // - threadPool.promoteFollower(); - } } - - assert(connection != null); - - // - // We validate and activate outside the thread - // synchronization, to not block the factory. - // - try + finally { - connection.validate(); - } - catch(Ice.LocalException ex) - { - synchronized(this) - { - connection.waitUntilFinished(); // We must call waitUntilFinished() for cleanup. - _connections.remove(connection); - return; - } + // + // This makes sure that we promote a follower before we leave the scope of the mutex + // above, but after we call accept() (if we call it). + // + threadPool.promoteFollower(); } - connection.activate(); + connection.start(this); } public synchronized void @@ -354,9 +359,48 @@ public final class IncomingConnectionFactory extends EventHandler return _acceptor.toString(); } + // + // Operations from ConnectionI.StartCallback + // + public synchronized void + connectionStartCompleted(Ice.ConnectionI connection) + { + // + // Initially, connections are in the holding state. If the factory is active + // we activate the connection. + // + if(_state == StateActive) + { + connection.activate(); + } + } + + public synchronized void + connectionStartFailed(Ice.ConnectionI connection, Ice.LocalException ex) + { + if(_state == StateClosed) + { + return; + } + + if(_warn) + { + warning(ex); + } + + // + // If the connection is finished, remove it right away from + // the connection map. Otherwise, we keep it in the map, it + // will eventually be reaped. + // + if(connection.isFinished()) + { + _connections.remove(connection); + } + } + public - IncomingConnectionFactory(Instance instance, EndpointI endpoint, Ice.ObjectAdapter adapter, - String adapterName) + IncomingConnectionFactory(Instance instance, EndpointI endpoint, Ice.ObjectAdapter adapter, String adapterName) { super(instance); _endpoint = endpoint; @@ -390,28 +434,25 @@ public final class IncomingConnectionFactory extends EventHandler { _endpoint = h.value; - Ice.ConnectionI connection = null; - - try - { - connection = new Ice.ConnectionI(_instance, _transceiver, _endpoint, _adapter, + Ice.ConnectionI connection; + try + { + connection = new Ice.ConnectionI(_instance, _transceiver, _endpoint, _adapter, _threadPerConnection); - connection.start(); - connection.validate(); - } - catch(Ice.LocalException ex) - { - // - // If a connection object was constructed, then - // validate() must have raised the exception. - // - if(connection != null) - { - connection.waitUntilFinished(); // We must call waitUntilFinished() for cleanup. - } - - return; - } + } + catch(Ice.LocalException ex) + { + try + { + _transceiver.close(); + } + catch(Ice.LocalException exc) + { + // Ignore + } + throw ex; + } + connection.start(null); _connections.add(connection); } @@ -654,8 +695,7 @@ public final class IncomingConnectionFactory extends EventHandler } catch(Ice.SocketException ex) { - // Do not ignore SocketException in Java. - throw ex; + // Ignore socket exceptions. } catch(Ice.TimeoutException ex) { @@ -669,9 +709,8 @@ public final class IncomingConnectionFactory extends EventHandler warning(ex); } } - + Ice.ConnectionI connection = null; - synchronized(this) { while(_state == StateHolding) @@ -730,37 +769,42 @@ public final class IncomingConnectionFactory extends EventHandler } } - // - // Create a connection object for the connection. - // - if(transceiver != null) + if(transceiver == null) { - try - { - connection = new Ice.ConnectionI(_instance, transceiver, _endpoint, _adapter, - _threadPerConnection); - connection.start(); - } - catch(Ice.LocalException ex) + continue; + } + + try + { + connection = new Ice.ConnectionI(_instance, transceiver, _endpoint, _adapter, _threadPerConnection); + } + catch(Ice.LocalException ex) + { + try + { + transceiver.close(); + } + catch(Ice.LocalException exc) + { + // Ignore + } + + if(_warn) { - return; + warning(ex); } - - _connections.add(connection); + continue; } + _connections.add(connection); } // - // In thread per connection mode, the connection's thread - // will take care of connection validation and activation - // (for non-datagram connections). We don't want to block - // this thread waiting until validation is complete, - // because in contrast to thread pool mode, it is the only - // thread that can accept connections with this factory's - // acceptor. Therefore we don't call validate() and - // activate() from the connection factory in thread per - // connection mode. + // In thread-per-connection mode and regardless of the background mode, + // start() doesn't block. The connection thread is started and takes + // care of the connection validation and notifies the factory through + // the callback when it's done. // + connection.start(this); } } @@ -796,5 +840,5 @@ public final class IncomingConnectionFactory extends EventHandler private int _state; - private boolean _threadPerConnection; + private final boolean _threadPerConnection; } diff --git a/java/src/IceInternal/IndirectReference.java b/java/src/IceInternal/IndirectReference.java index 004d63350dc..55eb6f60c13 100644 --- a/java/src/IceInternal/IndirectReference.java +++ b/java/src/IceInternal/IndirectReference.java @@ -163,23 +163,38 @@ public class IndirectReference extends RoutableReference public Ice.ConnectionI getConnection(Ice.BooleanHolder comp) { - Ice.ConnectionI connection; + if(getRouterInfo() != null) + { + // + // If we route, we send everything to the router's client + // proxy endpoints. + // + EndpointI[] endpts = getRouterInfo().getClientEndpoints(); + if(endpts.length > 0) + { + applyOverrides(endpts); + return createConnection(endpts, comp); + } + } while(true) { - EndpointI[] endpts = super.getRoutedEndpoints(); Ice.BooleanHolder cached = new Ice.BooleanHolder(false); - if(endpts.length == 0 && _locatorInfo != null) + EndpointI[] endpts = null; + if(_locatorInfo != null) { endpts = _locatorInfo.getEndpoints(this, _locatorCacheTimeout, cached); + applyOverrides(endpts); } - applyOverrides(endpts); + if(endpts == null || endpts.length == 0) + { + throw new Ice.NoEndpointException(toString()); + } try { - connection = createConnection(endpts, comp); - assert(connection != null); + return createConnection(endpts, comp); } catch(Ice.NoEndpointException ex) { @@ -187,44 +202,132 @@ public class IndirectReference extends RoutableReference } catch(Ice.LocalException ex) { - if(getRouterInfo() == null) + assert(_locatorInfo != null); + _locatorInfo.clearCache(this); + if(cached.value) { - assert(_locatorInfo != null); - _locatorInfo.clearCache(this); - - if(cached.value) + TraceLevels traceLevels = getInstance().traceLevels(); + if(traceLevels.retry >= 2) { - TraceLevels traceLevels = getInstance().traceLevels(); - - if(traceLevels.retry >= 2) - { - String s = "connection to cached endpoints failed\n" + - "removing endpoints from cache and trying one more time\n" + ex; - getInstance().initializationData().logger.trace(traceLevels.retryCat, s); - } - - continue; + String s = "connection to cached endpoints failed\n" + + "removing endpoints from cache and trying one more time\n" + ex; + getInstance().initializationData().logger.trace(traceLevels.retryCat, s); } + continue; // Try again if the endpoints were cached. } - throw ex; } - - break; } + } - // - // If we have a router, set the object adapter for this router - // (if any) to the new connection, so that callbacks from the - // router can be received over this new connection. - // + public void + getConnection(final GetConnectionCallback callback) + { if(getRouterInfo() != null) { - connection.setAdapter(getRouterInfo().getAdapter()); + // + // If we route, we send everything to the router's client + // proxy endpoints. + // + getRouterInfo().getClientEndpoints(new RouterInfo.GetClientEndpointsCallback() + { + public void + setEndpoints(EndpointI[] endpts) + { + if(endpts.length > 0) + { + applyOverrides(endpts); + createConnection(endpts, callback); + } + else + { + getConnectionNoRouterInfo(callback); + } + } + + public void + setException(Ice.LocalException ex) + { + callback.setException(ex); + } + }); } + else + { + getConnectionNoRouterInfo(callback); + } + } - assert(connection != null); - return connection; + private void + getConnectionNoRouterInfo(final GetConnectionCallback callback) + { + final IndirectReference self = this; + if(_locatorInfo != null) + { + _locatorInfo.getEndpoints(this, _locatorCacheTimeout, new LocatorInfo.GetEndpointsCallback() + { + public void + setEndpoints(EndpointI[] endpoints, final boolean cached) + { + if(endpoints.length == 0) + { + callback.setException(new Ice.NoEndpointException(self.toString())); + return; + } + + applyOverrides(endpoints); + createConnection(endpoints, new GetConnectionCallback() + { + public void + setConnection(Ice.ConnectionI connection, boolean compress) + { + callback.setConnection(connection, compress); + } + + public void + setException(Ice.LocalException exc) + { + try + { + throw exc; + } + catch(Ice.NoEndpointException ex) + { + callback.setException(ex); // No need to retry if there's no endpoints. + } + catch(Ice.LocalException ex) + { + assert(_locatorInfo != null); + _locatorInfo.clearCache(self); + if(cached) + { + TraceLevels traceLvls = getInstance().traceLevels(); + if(traceLvls.retry >= 2) + { + String s = "connection to cached endpoints failed\n" + + "removing endpoints from cache and trying one more time\n" + ex; + getInstance().initializationData().logger.trace(traceLvls.retryCat, s); + } + getConnectionNoRouterInfo(callback); // Retry. + return; + } + callback.setException(ex); + } + } + }); + } + + public void + setException(Ice.LocalException ex) + { + callback.setException(ex); + } + }); + } + else + { + callback.setException(new Ice.NoEndpointException(toString())); + } } public synchronized int diff --git a/java/src/IceInternal/Instance.java b/java/src/IceInternal/Instance.java index 3051450518d..aea0de5b8e7 100644 --- a/java/src/IceInternal/Instance.java +++ b/java/src/IceInternal/Instance.java @@ -158,14 +158,46 @@ public final class Instance return _serverThreadPool; } + public synchronized SelectorThread + selectorThread() + { + if(_state == StateDestroyed) + { + throw new Ice.CommunicatorDestroyedException(); + } + + if(_selectorThread == null) // Lazy initialization. + { + _selectorThread = new SelectorThread(this); + } + + return _selectorThread; + } + + public synchronized EndpointHostResolver + endpointHostResolver() + { + if(_state == StateDestroyed) + { + throw new Ice.CommunicatorDestroyedException(); + } + + if(_endpointHostResolver == null) // Lazy initialization. + { + _endpointHostResolver = new EndpointHostResolver(this); + } + + return _endpointHostResolver; + } + synchronized public Timer timer() { if(_state == StateDestroyed) { throw new Ice.CommunicatorDestroyedException(); - } - + } + if(_timer == null) // Lazy initialization. { _timer = new Timer(this); @@ -186,6 +218,12 @@ public final class Instance return _threadPerConnectionStackSize; } + public boolean + background() + { + return _background; + } + public synchronized EndpointFactoryManager endpointFactoryManager() { @@ -598,6 +636,8 @@ public final class Instance _threadPerConnectionStackSize = stackSize; } + _background = _initData.properties.getPropertyAsInt("Ice.Background") > 0; + _routerManager = new RouterManager(); _locatorManager = new LocatorManager(); @@ -655,6 +695,8 @@ public final class Instance IceUtil.Assert.FinalizerAssert(_objectAdapterFactory == null); IceUtil.Assert.FinalizerAssert(_clientThreadPool == null); IceUtil.Assert.FinalizerAssert(_serverThreadPool == null); + IceUtil.Assert.FinalizerAssert(_selectorThread == null); + IceUtil.Assert.FinalizerAssert(_endpointHostResolver == null); IceUtil.Assert.FinalizerAssert(_timer == null); IceUtil.Assert.FinalizerAssert(_routerManager == null); IceUtil.Assert.FinalizerAssert(_locatorManager == null); @@ -777,6 +819,8 @@ public final class Instance ThreadPool serverThreadPool = null; ThreadPool clientThreadPool = null; + SelectorThread selectorThread = null; + EndpointHostResolver endpointHostResolver = null; synchronized(this) { @@ -804,6 +848,20 @@ public final class Instance _clientThreadPool = null; } + if(_selectorThread != null) + { + _selectorThread.destroy(); + selectorThread = _selectorThread; + _selectorThread = null; + } + + if(_endpointHostResolver != null) + { + _endpointHostResolver.destroy(); + endpointHostResolver = _endpointHostResolver; + _endpointHostResolver = null; + } + if(_timer != null) { _timer._destroy(); @@ -857,8 +915,7 @@ public final class Instance } // - // Join with the thread pool threads outside the - // synchronization. + // Join with threads outside the synchronization. // if(clientThreadPool != null) { @@ -868,6 +925,14 @@ public final class Instance { serverThreadPool.joinWithAllThreads(); } + if(selectorThread != null) + { + selectorThread.joinWithThread(); + } + if(endpointHostResolver != null) + { + endpointHostResolver.joinWithThread(); + } if(_initData.properties.getPropertyAsInt("Ice.Warn.UnusedProperties") > 0) { @@ -935,9 +1000,12 @@ public final class Instance private ObjectAdapterFactory _objectAdapterFactory; private ThreadPool _clientThreadPool; private ThreadPool _serverThreadPool; + private SelectorThread _selectorThread; + private EndpointHostResolver _endpointHostResolver; private Timer _timer; private final boolean _threadPerConnection; private final int _threadPerConnectionStackSize; + private final boolean _background; private EndpointFactoryManager _endpointFactoryManager; private Ice.PluginManager _pluginManager; private java.util.Map _defaultContext; diff --git a/java/src/IceInternal/LocatorInfo.java b/java/src/IceInternal/LocatorInfo.java index 0d54d87555a..c5b4b1d0060 100644 --- a/java/src/IceInternal/LocatorInfo.java +++ b/java/src/IceInternal/LocatorInfo.java @@ -11,6 +11,12 @@ package IceInternal; public final class LocatorInfo { + interface GetEndpointsCallback + { + void setEndpoints(EndpointI[] endpoints, boolean cached); + void setException(Ice.LocalException ex); + } + LocatorInfo(Ice.LocatorPrx locator, LocatorTable table) { _locator = locator; @@ -158,94 +164,137 @@ public final class LocatorInfo cached.value = objectCached || endpointsCached; } } - catch(Ice.AdapterNotFoundException ex) + catch(Exception ex) { - if(ref.getInstance().traceLevels().location >= 1) - { - StringBuffer s = new StringBuffer(); - s.append("adapter not found\n"); - s.append("adapter = " + adapterId); - ref.getInstance().initializationData().logger.trace( - ref.getInstance().traceLevels().locationCat, s.toString()); - } - - Ice.NotRegisteredException e = new Ice.NotRegisteredException(); - e.kindOfObject = "object adapter"; - e.id = adapterId; - throw e; + getEndpointsException(ref, ex); } - catch(Ice.ObjectNotFoundException ex) - { - if(ref.getInstance().traceLevels().location >= 1) - { - StringBuffer s = new StringBuffer(); - s.append("object not found\n"); - s.append("object = " + ref.getInstance().identityToString(identity)); - ref.getInstance().initializationData().logger.trace( - ref.getInstance().traceLevels().locationCat, s.toString()); - } - Ice.NotRegisteredException e = new Ice.NotRegisteredException(); - e.kindOfObject = "object"; - e.id = ref.getInstance().identityToString(identity); - throw e; - } - catch(Ice.NotRegisteredException ex) + if(ref.getInstance().traceLevels().location >= 1) { - throw ex; + getEndpointsTrace(ref, endpoints, cached.value); } - catch(Ice.LocalException ex) + + return endpoints == null ? new EndpointI[0] : endpoints; + } + + public void + getEndpoints(final IndirectReference ref, final int ttl, final GetEndpointsCallback callback) + { + final String adapterId = ref.getAdapterId(); + final Ice.Identity identity = ref.getIdentity(); + final Instance instance = ref.getInstance(); + if(adapterId.length() > 0) { - if(ref.getInstance().traceLevels().location >= 1) + EndpointI[] endpoints = _table.getAdapterEndpoints(adapterId, ttl); + if(endpoints == null) { - StringBuffer s = new StringBuffer(); - s.append("couldn't contact the locator to retrieve adapter endpoints\n"); - if(adapterId.length() > 0) + if(instance.traceLevels().location >= 1) { - s.append("adapter = " + adapterId + "\n"); + StringBuffer s = new StringBuffer(); + s.append("searching for adapter by id\n"); + s.append("adapter = " + adapterId); + instance.initializationData().logger.trace(instance.traceLevels().locationCat, s.toString()); } - else + + // + // Search the adapter in the location service if we didn't + // find it in the cache. + // + _locator.findAdapterById_async(new Ice.AMI_Locator_findAdapterById() + { + public void + ice_response(Ice.ObjectPrx object) + { + EndpointI[] endpoints = null; + if(object != null) + { + endpoints = ((Ice.ObjectPrxHelperBase)object).__reference().getEndpoints(); + if(endpoints.length > 0) + { + _table.addAdapterEndpoints(adapterId, endpoints); + } + } + + if(instance.traceLevels().location >= 1) + { + getEndpointsTrace(ref, endpoints, false); + } + + if(endpoints == null) + { + callback.setEndpoints(new EndpointI[0], false); + } + else + { + callback.setEndpoints(endpoints, false); + } + } + + public void + ice_exception(Ice.UserException ex) + { + getEndpointsException(ref, ex, callback); + } + + public void + ice_exception(Ice.LocalException ex) + { + getEndpointsException(ref, ex, callback); + } + }, adapterId); + return; + } + else + { + if(instance.traceLevels().location >= 1) { - s.append("object = " + ref.getInstance().identityToString(identity) + "\n"); + getEndpointsTrace(ref, endpoints, true); } - s.append("reason = " + ex); - ref.getInstance().initializationData().logger.trace( - ref.getInstance().traceLevels().locationCat, s.toString()); + callback.setEndpoints(endpoints, true); + return; } - throw ex; } - - if(ref.getInstance().traceLevels().location >= 1) + else { - if(endpoints != null && endpoints.length > 0) + Ice.ObjectPrx object = _table.getProxy(identity, ttl); + if(object == null) { - if(cached.value) - { - trace("found endpoints in locator table", ref, endpoints); - } - else + if(instance.traceLevels().location >= 1) { - trace("retrieved endpoints from locator, adding to locator table", ref, endpoints); + StringBuffer s = new StringBuffer(); + s.append("searching for object by id\n"); + s.append("object = " + instance.identityToString(identity)); + instance.initializationData().logger.trace(instance.traceLevels().locationCat, s.toString()); } + + _locator.findObjectById_async(new Ice.AMI_Locator_findObjectById() + { + public void + ice_response(Ice.ObjectPrx object) + { + getWellKnownObjectEndpoints(ref, object, ttl, false, callback); + } + + public void + ice_exception(Ice.UserException ex) + { + getEndpointsException(ref, ex, callback); + } + + public void + ice_exception(Ice.LocalException ex) + { + getEndpointsException(ref, ex, callback); + } + }, identity); + return; } else { - StringBuffer s = new StringBuffer(); - s.append("no endpoints configured for "); - if(adapterId.length() > 0) - { - s.append("adapter\n"); - s.append("adapter = " + adapterId + "\n"); - } - else - { - s.append("object\n"); - s.append("object = " + ref.getInstance().identityToString(identity) + "\n"); - } + getWellKnownObjectEndpoints(ref, object, ttl, true, callback); + return; } } - - return endpoints == null ? new EndpointI[0] : endpoints; } public void @@ -333,14 +382,204 @@ public final class LocatorInfo { s.append(endpoints[i].toString()); if(i + 1 < sz) + { s.append(":"); + } } ref.getInstance().initializationData().logger.trace(ref.getInstance().traceLevels().locationCat, s.toString()); } + private void + getEndpointsException(IndirectReference ref, Exception exc) + { + try + { + throw exc; + } + catch(Ice.AdapterNotFoundException ex) + { + final Instance instance = ref.getInstance(); + if(instance.traceLevels().location >= 1) + { + StringBuffer s = new StringBuffer(); + s.append("adapter not found\n"); + s.append("adapter = " + ref.getAdapterId()); + instance.initializationData().logger.trace(instance.traceLevels().locationCat, s.toString()); + } + + Ice.NotRegisteredException e = new Ice.NotRegisteredException(); + e.kindOfObject = "object adapter"; + e.id = ref.getAdapterId(); + throw e; + } + catch(Ice.ObjectNotFoundException ex) + { + final Instance instance = ref.getInstance(); + if(instance.traceLevels().location >= 1) + { + StringBuffer s = new StringBuffer(); + s.append("object not found\n"); + s.append("object = " + instance.identityToString(ref.getIdentity())); + instance.initializationData().logger.trace(instance.traceLevels().locationCat, s.toString()); + } + + Ice.NotRegisteredException e = new Ice.NotRegisteredException(); + e.kindOfObject = "object"; + e.id = instance.identityToString(ref.getIdentity()); + throw e; + } + catch(Ice.NotRegisteredException ex) + { + throw ex; + } + catch(Ice.LocalException ex) + { + final Instance instance = ref.getInstance(); + if(instance.traceLevels().location >= 1) + { + StringBuffer s = new StringBuffer(); + s.append("couldn't contact the locator to retrieve adapter endpoints\n"); + if(ref.getAdapterId().length() > 0) + { + s.append("adapter = " + ref.getAdapterId() + "\n"); + } + else + { + s.append("object = " + instance.identityToString(ref.getIdentity()) + "\n"); + } + s.append("reason = " + ex); + instance.initializationData().logger.trace(instance.traceLevels().locationCat, s.toString()); + } + throw ex; + } + catch(Exception ex) + { + assert(false); + } + } + + private void + getEndpointsException(IndirectReference ref, Exception exc, GetEndpointsCallback callback) + { + try + { + getEndpointsException(ref, exc); + } + catch(Ice.LocalException ex) + { + callback.setException(ex); + } + catch(Exception ex) + { + assert(false); + } + } + + private void + getWellKnownObjectEndpoints(final IndirectReference ref, + final Ice.ObjectPrx object, + final int ttl, + final boolean objectCached, + final GetEndpointsCallback callback) + { + EndpointI[] endpoints = null; + if(object != null) + { + Reference r = ((Ice.ObjectPrxHelperBase)object).__reference(); + if(r instanceof DirectReference) + { + DirectReference odr = (DirectReference)r; + endpoints = odr.getEndpoints(); + } + else + { + IndirectReference oir = (IndirectReference)r; + if(oir.getAdapterId().length() > 0) + { + getEndpoints(oir, ttl, new GetEndpointsCallback() + { + public void + setEndpoints(EndpointI[] endpoints, boolean endpointsCached) + { + if(!objectCached && endpoints != null && endpoints.length > 0) + { + _table.addProxy(ref.getIdentity(), object); + } + + if(ref.getInstance().traceLevels().location >= 1) + { + getEndpointsTrace(ref, endpoints, objectCached || endpointsCached); + } + + callback.setEndpoints(endpoints, objectCached || endpointsCached); + } + + public void + setException(Ice.LocalException ex) + { + callback.setException(ex); + } + }); + return; + } + } + } + + if(!objectCached && endpoints != null && endpoints.length > 0) + { + _table.addProxy(ref.getIdentity(), object); + } + + if(ref.getInstance().traceLevels().location >= 1) + { + getEndpointsTrace(ref, endpoints, objectCached); + } + + if(endpoints == null) + { + callback.setEndpoints(new EndpointI[0], false); + } + else + { + callback.setEndpoints(endpoints, objectCached); + } + } + + private void + getEndpointsTrace(IndirectReference ref, EndpointI[] endpoints, boolean cached) + { + if(endpoints != null && endpoints.length > 0) + { + if(cached) + { + trace("found endpoints in locator table", ref, endpoints); + } + else + { + trace("retrieved endpoints from locator, adding to locator table", ref, endpoints); + } + } + else + { + final Instance instance = ref.getInstance(); + StringBuffer s = new StringBuffer(); + s.append("no endpoints configured for "); + if(ref.getAdapterId().length() > 0) + { + s.append("adapter\n"); + s.append("adapter = " + ref.getAdapterId() + "\n"); + } + else + { + s.append("object\n"); + s.append("object = " + instance.identityToString(ref.getIdentity()) + "\n"); + } + instance.initializationData().logger.trace(instance.traceLevels().locationCat, s.toString()); + } + } + private final Ice.LocatorPrx _locator; private Ice.LocatorRegistryPrx _locatorRegistry; private final LocatorTable _table; } - diff --git a/java/src/IceInternal/Network.java b/java/src/IceInternal/Network.java index 07770711f45..5971e1122d5 100644 --- a/java/src/IceInternal/Network.java +++ b/java/src/IceInternal/Network.java @@ -162,7 +162,7 @@ public final class Network } } - private static void + public static void closeSocketNoThrow(java.nio.channels.SelectableChannel fd) { try @@ -274,13 +274,74 @@ public final class Network } } - public static void + public static boolean doConnect(java.nio.channels.SocketChannel fd, java.net.InetSocketAddress addr, int timeout) { try { if(!fd.connect(addr)) { + if(timeout == 0) + { + return false; + } + + try + { + doFinishConnect(fd, timeout); + } + catch(Ice.LocalException ex) + { + closeSocketNoThrow(fd); + throw ex; + } + return true; + } + } + catch(java.net.ConnectException ex) + { + closeSocketNoThrow(fd); + + Ice.ConnectFailedException se; + if(connectionRefused(ex)) + { + se = new Ice.ConnectionRefusedException(); + } + else + { + se = new Ice.ConnectFailedException(); + } + se.initCause(ex); + throw se; + } + catch(java.io.IOException ex) + { + closeSocketNoThrow(fd); + Ice.SocketException se = new Ice.SocketException(); + se.initCause(ex); + throw se; + } + + if(addr.equals(fd.socket().getLocalSocketAddress())) + { + closeSocketNoThrow(fd); + throw new Ice.ConnectionRefusedException(); + } + return true; + } + + public static void + doFinishConnect(java.nio.channels.SocketChannel fd, int timeout) + { + // + // Note: we don't close the socket if there's an exception. It's the responsibility + // of the caller to do so. + // + + if(timeout != 0) + { + try + { java.nio.channels.Selector selector = java.nio.channels.Selector.open(); try { @@ -288,17 +349,13 @@ public final class Network { try { - java.nio.channels.SelectionKey key = + java.nio.channels.SelectionKey key = fd.register(selector, java.nio.channels.SelectionKey.OP_CONNECT); int n; if(timeout > 0) { n = selector.select(timeout); } - else if(timeout == 0) - { - n = selector.selectNow(); - } else { n = selector.select(); @@ -306,7 +363,6 @@ public final class Network if(n == 0) { - closeSocketNoThrow(fd); throw new Ice.ConnectTimeoutException(); } @@ -335,17 +391,35 @@ public final class Network // Ignore } } + } + catch(java.io.IOException ex) + { + Ice.SocketException se = new Ice.SocketException(); + se.initCause(ex); + throw se; + } + } - if(!fd.finishConnect()) - { - throw new Ice.ConnectFailedException(); - } + try + { + if(!fd.finishConnect()) + { + throw new Ice.ConnectFailedException(); + } + + // + // Prevent self connect (self connect happens on Linux when a client tries to connect to + // a server which was just deactivated if the client socket re-uses the same ephemeral + // port as the server). + // + java.net.SocketAddress addr = fd.socket().getRemoteSocketAddress(); + if(addr != null && addr.equals(fd.socket().getLocalSocketAddress())) + { + throw new Ice.ConnectionRefusedException(); } } catch(java.net.ConnectException ex) { - closeSocketNoThrow(fd); - Ice.ConnectFailedException se; if(connectionRefused(ex)) { @@ -360,7 +434,6 @@ public final class Network } catch(java.io.IOException ex) { - closeSocketNoThrow(fd); Ice.SocketException se = new Ice.SocketException(); se.initCause(ex); throw se; diff --git a/java/src/IceInternal/Outgoing.java b/java/src/IceInternal/Outgoing.java index 85cf0c1ce9b..fd22795c6cf 100644 --- a/java/src/IceInternal/Outgoing.java +++ b/java/src/IceInternal/Outgoing.java @@ -9,19 +9,19 @@ package IceInternal; -public final class Outgoing +public final class Outgoing implements OutgoingMessageCallback { public - Outgoing(Ice.ConnectionI connection, Reference ref, String operation, Ice.OperationMode mode, - java.util.Map context, boolean compress) + Outgoing(RequestHandler handler, String operation, Ice.OperationMode mode, java.util.Map context) throws LocalExceptionWrapper { - _connection = connection; - _reference = ref; _state = StateUnsent; - _is = new BasicStream(ref.getInstance()); - _os = new BasicStream(ref.getInstance()); - _compress = compress; + _sent = false; + _handler = handler; + + Instance instance = _handler.getReference().getInstance(); + _is = new BasicStream(instance); + _os = new BasicStream(instance); writeHeader(operation, mode, context); } @@ -30,13 +30,13 @@ public final class Outgoing // These functions allow this object to be reused, rather than reallocated. // public void - reset(Reference ref, String operation, Ice.OperationMode mode, java.util.Map context, boolean compress) + reset(RequestHandler handler, String operation, Ice.OperationMode mode, java.util.Map context) throws LocalExceptionWrapper { - _reference = ref; _state = StateUnsent; _exception = null; - _compress = compress; + _sent = false; + _handler = handler; writeHeader(operation, mode, context); } @@ -57,41 +57,39 @@ public final class Outgoing _os.endWriteEncaps(); - switch(_reference.getMode()) + switch(_handler.getReference().getMode()) { case Reference.ModeTwoway: { - // - // We let all exceptions raised by sending directly - // propagate to the caller, because they can be - // retried without violating "at-most-once". In case - // of such exceptions, the connection object does not - // call back on this object, so we don't need to lock - // the mutex, keep track of state, or save exceptions. - // - _connection.sendRequest(_os, this, _compress); + _state = StateInProgress; - // - // Wait until the request has completed, or until the - // request times out. - // + Ice.ConnectionI connection = _handler.sendRequest(this); boolean timedOut = false; synchronized(this) { + // - // It's possible that the request has already - // completed, due to a regular response, or because of - // an exception. So we only change the state to "in - // progress" if it is still "unsent". + // If the request is being sent in the background we first wait for the + // sent notification. // - if(_state == StateUnsent) + while(_state != StateFailed && !_sent) { - _state = StateInProgress; + try + { + wait(); + } + catch(java.lang.InterruptedException ex) + { + } } - - int timeout = _connection.timeout(); + + // + // Wait until the request has completed, or until the request + // times out. + // + int timeout = connection.timeout(); while(_state == StateInProgress && !timedOut) { try @@ -99,7 +97,7 @@ public final class Outgoing if(timeout >= 0) { wait(timeout); - + if(_state == StateInProgress) { timedOut = true; @@ -115,15 +113,15 @@ public final class Outgoing } } } - + if(timedOut) { // // Must be called outside the synchronization of // this object // - _connection.exception(new Ice.TimeoutException()); - + connection.exception(new Ice.TimeoutException()); + // // We must wait until the exception set above has // propagated to this Outgoing object. @@ -142,11 +140,11 @@ public final class Outgoing } } } - + if(_exception != null) { _exception.fillInStackTrace(); - + // // A CloseConnectionException indicates graceful // server shutdown, and is therefore always repeatable @@ -158,12 +156,13 @@ public final class Outgoing // An ObjectNotExistException can always be retried as // well without violating "at-most-once". // - if(_exception instanceof Ice.CloseConnectionException || + if(!_sent || + _exception instanceof Ice.CloseConnectionException || _exception instanceof Ice.ObjectNotExistException) { throw _exception; } - + // // Throw the exception wrapped in a LocalExceptionWrapper, to // indicate that the request cannot be resent without @@ -171,30 +170,68 @@ public final class Outgoing // throw new LocalExceptionWrapper(_exception, false); } - + if(_state == StateUserException) { return false; } - - assert(_state == StateOK); - break; + else + { + assert(_state == StateOK); + return true; + } + } case Reference.ModeOneway: case Reference.ModeDatagram: { - // - // For oneway and datagram requests, the connection object - // never calls back on this object. Therefore we don't - // need to lock the mutex or save exceptions. We simply - // let all exceptions from sending propagate to the - // caller, because such exceptions can be retried without - // violating "at-most-once". - // - _state = StateInProgress; - _connection.sendRequest(_os, null, _compress); - break; + try + { + _state = StateInProgress; + if(_handler.sendRequest(this) != null) + { + // + // If the handler returns the connection, we must wait for the sent callback. + // + synchronized(this) + { + while(_state != StateFailed && !_sent) + { + try + { + wait(); + } + catch(java.lang.InterruptedException ex) + { + } + } + + if(_exception != null) + { + assert(!_sent); + throw _exception; + } + } + } + return true; + } + catch(Ice.LocalException ex) // Java specfic work-around (see ConnectionI.sendRequest()) + { + if(!_sent) // The send might have failed but the request might still be sent... + { + throw ex; + } + else + { + // + // We wrap the exception into a LocalExceptionWrapper to indicate that + // the request cannot be resent without potentially violating the + // "at-most-once" principle. + // + throw new IceInternal.LocalExceptionWrapper(ex, false); + } + } } case Reference.ModeBatchOneway: @@ -206,12 +243,13 @@ public final class Outgoing // apply. // _state = StateInProgress; - _connection.finishBatchRequest(_os, _compress); - break; + _handler.finishBatchRequest(_os); + return true; } } - - return true; + + assert(false); + return false; } public void @@ -225,10 +263,10 @@ public final class Outgoing // must notify the connection about that we give up ownership // of the batch stream. // - int mode = _reference.getMode(); + int mode = _handler.getReference().getMode(); if(mode == Reference.ModeBatchOneway || mode == Reference.ModeBatchDatagram) { - _connection.abortBatchRequest(); + _handler.abortBatchRequest(); // // If we abort a batch requests, we cannot retry, because @@ -242,10 +280,31 @@ public final class Outgoing throw ex; } + public void + sent(boolean notify) + { + if(notify) + { + synchronized(this) + { + _sent = true; + notify(); + } + } + else + { + // + // No synchronization is necessary if called from sendRequest() because the connection + // send mutex is locked and no other threads can call on Outgoing until it's released. + // + _sent = true; + } + } + public synchronized void finished(BasicStream is) { - assert(_reference.getMode() == Reference.ModeTwoway); // Can only be called for twoways. + assert(_handler.getReference().getMode() == Reference.ModeTwoway); // Only for twoways. assert(_state <= StateInProgress); @@ -390,11 +449,8 @@ public final class Outgoing public synchronized void finished(Ice.LocalException ex) { - assert(_reference.getMode() == Reference.ModeTwoway); // Can only be called for twoways. - assert(_state <= StateInProgress); - - _state = StateLocalException; + _state = StateFailed; _exception = ex; notify(); } @@ -415,7 +471,7 @@ public final class Outgoing writeHeader(String operation, Ice.OperationMode mode, java.util.Map context) throws LocalExceptionWrapper { - switch(_reference.getMode()) + switch(_handler.getReference().getMode()) { case Reference.ModeTwoway: case Reference.ModeOneway: @@ -428,19 +484,19 @@ public final class Outgoing case Reference.ModeBatchOneway: case Reference.ModeBatchDatagram: { - _connection.prepareBatchRequest(_os); + _handler.prepareBatchRequest(_os); break; } } try { - _reference.getIdentity().__write(_os); + _handler.getReference().getIdentity().__write(_os); // // For compatibility with the old FacetPath. // - String facet = _reference.getFacet(); + String facet = _handler.getReference().getFacet(); if(facet == null || facet.length() == 0) { _os.writeStringSeq(null); @@ -467,10 +523,8 @@ public final class Outgoing // // Implicit context // - Ice.ImplicitContextI implicitContext = - _reference.getInstance().getImplicitContext(); - - java.util.Map prxContext = _reference.getContext(); + Ice.ImplicitContextI implicitContext = _handler.getReference().getInstance().getImplicitContext(); + java.util.Map prxContext = _handler.getReference().getContext(); if(implicitContext == null) { @@ -495,8 +549,11 @@ public final class Outgoing } } - private Ice.ConnectionI _connection; - private Reference _reference; + private RequestHandler _handler; + private BasicStream _is; + private BasicStream _os; + private boolean _sent; + private Ice.LocalException _exception; private static final int StateUnsent = 0; @@ -504,12 +561,8 @@ public final class Outgoing private static final int StateOK = 2; private static final int StateUserException = 3; private static final int StateLocalException = 4; + private static final int StateFailed = 5; private int _state; - private BasicStream _is; - private BasicStream _os; - - private boolean _compress; - public Outgoing next; // For use by Ice._ObjectDelM } diff --git a/java/src/IceInternal/OutgoingAsync.java b/java/src/IceInternal/OutgoingAsync.java index 8f49c69ac06..184355e8e06 100644 --- a/java/src/IceInternal/OutgoingAsync.java +++ b/java/src/IceInternal/OutgoingAsync.java @@ -9,7 +9,7 @@ package IceInternal; -public abstract class OutgoingAsync +public abstract class OutgoingAsync implements OutgoingAsyncMessageCallback { public OutgoingAsync() @@ -18,19 +18,75 @@ public abstract class OutgoingAsync public abstract void ice_exception(Ice.LocalException ex); + public final BasicStream + __os() + { + return __os; + } + public final void - __finished(BasicStream is) + __sent(final Ice.ConnectionI connection) { synchronized(_monitor) { - byte replyStatus; - - try + _sent = true; + + if(!_proxy.ice_isTwoway()) + { + cleanup(); // No response expected, we're done with the OutgoingAsync. + } + else if(_response) + { + _monitor.notifyAll(); // If the response was already received notify finished() which is waiting. + } + else if(connection.timeout() >= 0) + { + assert(_timerTask == null); + _timerTask = new TimerTask() + { + public void + runTimerTask() + { + __runTimerTask(connection); + } + }; + _proxy.__reference().getInstance().timer().schedule(_timerTask, connection.timeout()); + } + } + } + + public final void + __finished(BasicStream is) + { + assert(_proxy.ice_isTwoway()); // Can only be called for twoways. + + byte replyStatus; + try + { + synchronized(_monitor) { + assert(__os != null); + _response = true; + + if(_timerTask != null && _proxy.__reference().getInstance().timer().cancel(_timerTask)) + { + _timerTask = null; // Timer cancelled. + } + + while(!_sent || _timerTask != null) + { + try + { + _monitor.wait(); + } + catch(java.lang.InterruptedException ex) + { + } + } + __is.swap(is); - replyStatus = __is.readByte(); - + switch(replyStatus) { case ReplyStatus.replyOK: @@ -39,7 +95,7 @@ public abstract class OutgoingAsync __is.startReadEncaps(); break; } - + case ReplyStatus.replyObjectNotExist: case ReplyStatus.replyFacetNotExist: case ReplyStatus.replyOperationNotExist: @@ -145,36 +201,57 @@ public abstract class OutgoingAsync } } } - catch(Ice.LocalException ex) - { - __finished(ex); - return; - } - - assert(replyStatus == ReplyStatus.replyOK || replyStatus == ReplyStatus.replyUserException); + } + catch(Ice.LocalException ex) + { + __finished(ex); + return; + } + + assert(replyStatus == ReplyStatus.replyOK || replyStatus == ReplyStatus.replyUserException); - try - { - __response(replyStatus == ReplyStatus.replyOK); - } - catch(java.lang.Exception ex) - { - warning(ex); - } - finally + try + { + __response(replyStatus == ReplyStatus.replyOK); + } + catch(java.lang.Exception ex) + { + warning(ex); + } + finally + { + synchronized(_monitor) { cleanup(); } } } + public final void __finished(Ice.LocalException exc) { + boolean retry = false; synchronized(_monitor) { - if(__os != null) // Don't retry if cleanup() was already called. + if(__os != null) // Might be called from __prepare or before __prepare { + if(_timerTask != null && _proxy.__reference().getInstance().timer().cancel(_timerTask)) + { + _timerTask = null; // Timer cancelled. + } + + while(_timerTask != null) + { + try + { + _monitor.wait(); + } + catch(java.lang.InterruptedException ex) + { + } + } + // // A CloseConnectionException indicates graceful // server shutdown, and is therefore always repeatable @@ -187,53 +264,85 @@ public abstract class OutgoingAsync // An ObjectNotExistException can always be retried as // well without violating "at-most-once". // - if(_mode == Ice.OperationMode.Nonmutating || _mode == Ice.OperationMode.Idempotent || + if(!_sent || + _mode == Ice.OperationMode.Nonmutating || _mode == Ice.OperationMode.Idempotent || exc instanceof Ice.CloseConnectionException || exc instanceof Ice.ObjectNotExistException) { - try - { - _cnt = ((Ice.ObjectPrxHelperBase)_proxy).__handleException(_delegate, exc, _cnt); - __send(); - return; - } - catch(Ice.LocalException ex) - { - } + retry = true; } } - + } + + if(retry) + { try { - ice_exception(exc); + _cnt = _proxy.__handleException(_delegate, exc, _cnt); + __send(); + return; } - catch(java.lang.Exception ex) + catch(Ice.LocalException ex) { - warning(ex); } - finally + } + + try + { + ice_exception(exc); + } + catch(java.lang.Exception ex) + { + warning(ex); + } + finally + { + synchronized(_monitor) { cleanup(); } } } - public final boolean - __timedOut() + public final void + __finished(LocalExceptionWrapper ex) { // - // No synchronization necessary, because - // _absoluteTimeoutMillis is declared volatile. We cannot - // synchronize here because otherwise there might be deadlocks - // when Ice.ConnectionI calls back on this object with this - // function. + // NOTE: This is called if sendRequest/sendAsyncRequest fails with + // a LocalExceptionWrapper exception. It's not possible for the + // timer to be set at this point because the request couldn't be + // sent. // - if(_absoluteTimeoutMillis > 0) + assert(!_sent && _timerTask == null); + + try { - return IceInternal.Time.currentMonotonicTimeMillis() >= _absoluteTimeoutMillis; + if(_mode == Ice.OperationMode.Nonmutating || _mode == Ice.OperationMode.Idempotent) + { + _cnt = _proxy.__handleExceptionWrapperRelaxed(_delegate, ex, _cnt); + } + else + { + _proxy.__handleExceptionWrapper(_delegate, ex); + } + __send(); } - else + catch(Ice.LocalException exc) { - return false; + try + { + ice_exception(exc); + } + catch(java.lang.Exception exl) + { + warning(exl); + } + finally + { + synchronized(_monitor) + { + cleanup(); + } + } } } @@ -259,23 +368,28 @@ public abstract class OutgoingAsync } // - // Can't call async via a oneway proxy. + // Can't call async via a batch proxy. // - ((Ice.ObjectPrxHelperBase)prx).__checkTwowayOnly(operation); + _proxy = (Ice.ObjectPrxHelperBase)prx; + if(_proxy.ice_isBatchOneway() || _proxy.ice_isBatchDatagram()) + { + throw new Ice.FeatureNotSupportedException("can't send batch requests with AMI"); + } - _proxy = prx; _delegate = null; _cnt = 0; _mode = mode; + _sent = false; + _response = false; - Reference ref = ((Ice.ObjectPrxHelperBase)_proxy).__reference(); + Reference ref = _proxy.__reference(); assert(__is == null); __is = new BasicStream(ref.getInstance()); assert(__os == null); __os = new BasicStream(ref.getInstance()); __os.writeBlob(IceInternal.Protocol.requestHdr); - + ref.getIdentity().__write(__os); // @@ -308,11 +422,9 @@ public abstract class OutgoingAsync // // Implicit context // - Ice.ImplicitContextI implicitContext = - ref.getInstance().getImplicitContext(); - + Ice.ImplicitContextI implicitContext = ref.getInstance().getImplicitContext(); java.util.Map prxContext = ref.getContext(); - + if(implicitContext == null) { Ice.ContextHelper.write(__os, prxContext); @@ -322,7 +434,7 @@ public abstract class OutgoingAsync implicitContext.write(prxContext, __os); } } - + __os.startWriteEncaps(); } catch(Ice.LocalException ex) @@ -336,62 +448,56 @@ public abstract class OutgoingAsync protected final void __send() { + // + // NOTE: no synchronization needed. At this point, no other threads can be calling on this object. + // + + RequestHandler handler; + try + { + _delegate = _proxy.__getDelegate(true); + handler = _delegate.__getRequestHandler(); + } + catch(Ice.LocalException ex) + { + __finished(ex); + return; + } + + _sent = false; + _response = false; + handler.sendAsyncRequest(this); + } + + protected abstract void __response(boolean ok); + + private final void + __runTimerTask(Ice.ConnectionI connection) + { synchronized(_monitor) { - try - { - while(true) - { - Ice.BooleanHolder comp = new Ice.BooleanHolder(); - _delegate = ((Ice.ObjectPrxHelperBase)_proxy).__getDelegate(); - Ice.ConnectionI con = _delegate.__getConnection(comp); - if(con.timeout() >= 0) - { - _absoluteTimeoutMillis = IceInternal.Time.currentMonotonicTimeMillis() + con.timeout(); - } - else - { - _absoluteTimeoutMillis = 0; - } - - try - { - con.sendAsyncRequest(__os, this, comp.value); - - // - // Don't do anything after sendAsyncRequest() returned - // without an exception. I such case, there will be - // callbacks, i.e., calls to the __finished() - // functions. Since there is no mutex protection, we - // cannot modify state here and in such callbacks. - // - return; - } - catch(LocalExceptionWrapper ex) - { - ((Ice.ObjectPrxHelperBase)_proxy).__handleExceptionWrapper(_delegate, ex); - } - catch(Ice.LocalException ex) - { - _cnt = ((Ice.ObjectPrxHelperBase)_proxy).__handleException(_delegate, ex, _cnt); - } - } - } - catch(Ice.LocalException ex) + assert(_timerTask != null && _sent); // Can only be set once the request is sent. + + if(_response) // If the response was just received, don't close the connection. { - __finished(ex); + connection = null; } + _timerTask = null; + _monitor.notifyAll(); } - } - protected abstract void __response(boolean ok); + if(connection != null) + { + connection.exception(new Ice.TimeoutException()); + } + } private final void warning(java.lang.Exception ex) { if(__os != null) // Don't print anything if cleanup() was already called. { - Reference ref = ((Ice.ObjectPrxHelperBase)_proxy).__reference(); + Reference ref = _proxy.__reference(); if(ref.getInstance().initializationData().properties.getPropertyAsIntWithDefault( "Ice.Warn.AMICallback", 1) > 0) { @@ -410,6 +516,8 @@ public abstract class OutgoingAsync private final void cleanup() { + assert(_timerTask == null); + __is = null; __os = null; @@ -419,16 +527,14 @@ public abstract class OutgoingAsync protected BasicStream __is; protected BasicStream __os; - private Ice.ObjectPrx _proxy; + private boolean _sent; + private boolean _response; + private Ice.ObjectPrxHelperBase _proxy; private Ice._ObjectDel _delegate; private int _cnt; private Ice.OperationMode _mode; - // - // Must be volatile, because we don't want to lock the monitor - // below in __timedOut(), to avoid deadlocks. - // - private volatile long _absoluteTimeoutMillis; + private TimerTask _timerTask; private final java.lang.Object _monitor = new java.lang.Object(); } diff --git a/java/src/IceInternal/OutgoingAsyncMessageCallback.java b/java/src/IceInternal/OutgoingAsyncMessageCallback.java new file mode 100644 index 00000000000..2575dd33b38 --- /dev/null +++ b/java/src/IceInternal/OutgoingAsyncMessageCallback.java @@ -0,0 +1,16 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2007 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +package IceInternal; + +public interface OutgoingAsyncMessageCallback +{ + void __sent(Ice.ConnectionI connection); + void __finished(Ice.LocalException ex); +};
\ No newline at end of file diff --git a/java/src/IceInternal/OutgoingConnectionFactory.java b/java/src/IceInternal/OutgoingConnectionFactory.java index ffec338dd83..fb3108aa472 100644 --- a/java/src/IceInternal/OutgoingConnectionFactory.java +++ b/java/src/IceInternal/OutgoingConnectionFactory.java @@ -11,6 +11,11 @@ package IceInternal; public final class OutgoingConnectionFactory { + interface CreateConnectionCallback + { + void setConnection(Ice.ConnectionI connection, boolean compress); + void setException(Ice.LocalException ex); + } public synchronized void destroy() @@ -40,7 +45,7 @@ public final class OutgoingConnectionFactory public void waitUntilFinished() { - java.util.HashMap connections; + java.util.HashMap connections = null; synchronized(this) { @@ -50,7 +55,7 @@ public final class OutgoingConnectionFactory // anymore. Only then we can be sure the _connections // contains all connections. // - while(!_destroyed || !_pending.isEmpty()) + while(!_destroyed || !_pending.isEmpty() || !_pendingEndpoints.isEmpty()) { try { @@ -65,17 +70,14 @@ public final class OutgoingConnectionFactory // We want to wait until all connections are finished outside the // thread synchronization. // - // For consistency with C#, we set _connections to null rather than to a - // new empty list so that our finalizer does not try to invoke any - // methods on member objects. - // - connections = _connections; - _connections = null; + if(_connections != null) + { + connections = new java.util.HashMap(_connections); + } } // - // Now we wait for until the destruction of each connection is - // finished. + // Now we wait until the destruction of each connection is finished. // java.util.Iterator p = connections.values().iterator(); while(p.hasNext()) @@ -89,257 +91,119 @@ public final class OutgoingConnectionFactory connection.waitUntilFinished(); } } + + synchronized(this) + { + // + // For consistency with C#, we set _connections to null rather than to a + // new empty list so that our finalizer does not try to invoke any + // methods on member objects. + // + _connections = null; + } } public Ice.ConnectionI - create(EndpointI[] endpts, boolean hasMore, boolean threadPerConnection, Ice.EndpointSelectionType selType, + create(EndpointI[] endpts, boolean hasMore, boolean tpc, Ice.EndpointSelectionType selType, Ice.BooleanHolder compress) { - class ConnectorEndpointPair - { - public ConnectorEndpointPair(Connector c, EndpointI e) - { - connector = c; - endpoint = e; - } - - public Connector connector; - public EndpointI endpoint; - } - assert(endpts.length > 0); - java.util.ArrayList connectors = new java.util.ArrayList(); - - DefaultsAndOverrides defaultsAndOverrides = _instance.defaultsAndOverrides(); - synchronized(this) + // + // TODO: Remove when we no longer support SSL for JDK 1.4. We can also remove + // the threadPerConnection argument. + // + for(int i = 0; i < endpts.length; i++) { - if(_destroyed) + if(!tpc && endpts[i].requiresThreadPerConnection()) { - throw new Ice.CommunicatorDestroyedException(); + Ice.FeatureNotSupportedException ex = new Ice.FeatureNotSupportedException(); + ex.unsupportedFeature = "endpoint requires thread-per-connection:\n" + endpts[i].toString(); + throw ex; } + } - // - // TODO: Remove when we no longer support SSL for JDK 1.4. - // - for(int i = 0; i < endpts.length; i++) - { - if(!threadPerConnection && endpts[i].requiresThreadPerConnection()) - { - Ice.FeatureNotSupportedException ex = new Ice.FeatureNotSupportedException(); - ex.unsupportedFeature = "endpoint requires thread-per-connection:\n" + endpts[i].toString(); - throw ex; - } - } + // + // Apply the overrides. + // + java.util.List endpoints = applyOverrides(endpts); - // - // Reap connections for which destruction has completed. - // - java.util.Iterator p = _connections.values().iterator(); - while(p.hasNext()) - { - java.util.LinkedList connectionList = (java.util.LinkedList)p.next(); - - java.util.Iterator q = connectionList.iterator(); - while(q.hasNext()) - { - Ice.ConnectionI con = (Ice.ConnectionI)q.next(); - if(con.isFinished()) - { - q.remove(); - } - } + // + // Try to find a connection to one of the given endpoints. + // + Ice.ConnectionI connection = findConnection(endpoints, tpc, compress); + if(connection != null) + { + return connection; + } - if(connectionList.isEmpty()) - { - p.remove(); - } - } + Ice.LocalException exception = null; - EndpointI[] endpoints = new EndpointI[endpts.length]; - System.arraycopy(endpts, 0, endpoints, 0, endpts.length); - for(int i = 0; i < endpoints.length; i++) - { - // - // Modify endpoints with overrides. - // - if(defaultsAndOverrides.overrideTimeout) - { - endpoints[i] = endpoints[i].timeout(defaultsAndOverrides.overrideTimeoutValue); - } + // + // If we didn't find a connection with the endpoints, we create the connectors + // for the endpoints. + // + java.util.ArrayList connectors = new java.util.ArrayList(); + java.util.Iterator p = endpoints.iterator(); + while(p.hasNext()) + { + EndpointI endpoint = (EndpointI)p.next(); - // - // Create connectors for the endpoint. - // - java.util.ArrayList cons = endpoints[i].connectors(); + // + // Create connectors for the endpoint. + // + try + { + java.util.List cons = endpoint.connectors(); assert(cons.size() > 0); - + // - // Shuffle connectors is endpoint selection type is Random. + // Shuffle connectors if endpoint selection type is Random. // if(selType == Ice.EndpointSelectionType.Random) { java.util.Collections.shuffle(cons); } - - p = cons.iterator(); - while(p.hasNext()) - { - connectors.add(new ConnectorEndpointPair((Connector)p.next(), endpoints[i])); - } - - } - - // - // Search for existing connections. - // - p = connectors.iterator(); - while(p.hasNext()) - { - ConnectorEndpointPair cep = (ConnectorEndpointPair)p.next(); - - - java.util.LinkedList connectionList = (java.util.LinkedList)_connections.get(cep.connector); - if(connectionList != null) - { - java.util.Iterator q = connectionList.iterator(); - - while(q.hasNext()) - { - Ice.ConnectionI connection = (Ice.ConnectionI)q.next(); - - // - // Don't return connections for which destruction has - // been initiated. The connection must also match the - // requested thread-per-connection setting. - // - if(!connection.isDestroyed() && connection.threadPerConnection() == threadPerConnection) - { - if(defaultsAndOverrides.overrideCompress) - { - compress.value = defaultsAndOverrides.overrideCompressValue; - } - else - { - compress.value = cep.endpoint.compress(); - } - - return connection; - } - } - } - } - - // - // If some other thread is currently trying to establish a - // connection to any of our endpoints, we wait until this - // thread is finished. - // - boolean searchAgain = false; - while(!_destroyed) - { - boolean found = false; - p = connectors.iterator(); - while(p.hasNext()) - { - ConnectorEndpointPair cep = (ConnectorEndpointPair)p.next(); - if(_pending.contains(cep.connector)) - { - found = true; - break; - } - } - if(!found) - { - break; - } - - searchAgain = true; - - try - { - wait(); - } - catch(InterruptedException ex) - { - } - } - - if(_destroyed) - { - throw new Ice.CommunicatorDestroyedException(); - } - - // - // Search for existing connections again if we waited - // above, as new connections might have been added in the - // meantime. - // - if(searchAgain) - { - p = connectors.iterator(); - while(p.hasNext()) + java.util.Iterator q = cons.iterator(); + while(q.hasNext()) { - ConnectorEndpointPair cep = (ConnectorEndpointPair)p.next(); - - java.util.LinkedList connectionList = (java.util.LinkedList)_connections.get(cep.connector); - if(connectionList != null) - { - java.util.Iterator q = connectionList.iterator(); - - while(q.hasNext()) - { - Ice.ConnectionI connection = (Ice.ConnectionI)q.next(); - - // - // Don't return connections for which destruction has - // been initiated. The connection must also match the - // requested thread-per-connection setting. - // - if(!connection.isDestroyed() && connection.threadPerConnection() == threadPerConnection) - { - if(defaultsAndOverrides.overrideCompress) - { - compress.value = defaultsAndOverrides.overrideCompressValue; - } - else - { - compress.value = cep.endpoint.compress(); - } - - return connection; - } - } - } + connectors.add(new ConnectorInfo((Connector)q.next(), endpoint, tpc)); } } - - // - // No connection to any of our endpoints exists yet, so we - // will try to create one. To avoid that other threads try - // to create connections to the same endpoints, we add our - // endpoints to _pending. - // - p = connectors.iterator(); - while(p.hasNext()) + catch(Ice.LocalException ex) { - ConnectorEndpointPair cep = (ConnectorEndpointPair)p.next(); - _pending.add(cep.connector); + exception = ex; + handleException(exception, hasMore || p.hasNext()); } } + + if(connectors.isEmpty()) + { + assert(exception != null); + throw exception; + } + + // + // Try to get a connection to one of the connectors. A null result indicates that no + // connection was found and that we should try to establish the connection (and that + // the connectors were added to _pending to prevent other threads from establishing + // the connection). + // + connection = getConnection(connectors, null, compress); + if(connection != null) + { + return connection; + } - Connector connector = null; - Ice.ConnectionI connection = null; - Ice.LocalException exception = null; - - java.util.Iterator p = connectors.iterator(); + // + // Try to establish the connection to the connectors. + // + DefaultsAndOverrides defaultsAndOverrides = _instance.defaultsAndOverrides(); + p = connectors.iterator(); while(p.hasNext()) { - ConnectorEndpointPair cep = (ConnectorEndpointPair)p.next(); - connector = cep.connector; - EndpointI endpoint = cep.endpoint; - + ConnectorInfo ci = (ConnectorInfo)p.next(); try { int timeout; @@ -347,21 +211,17 @@ public final class OutgoingConnectionFactory { timeout = defaultsAndOverrides.overrideConnectTimeoutValue; } - // It is not necessary to check for overrideTimeout, - // the endpoint has already been modified with this - // override, if set. else { - timeout = endpoint.timeout(); + // + // It is not necessary to check for overrideTimeout, the endpoint has already + // been modified with this override, if set. + // + timeout = ci.endpoint.timeout(); } - Transceiver transceiver = connector.connect(timeout); - assert(transceiver != null); - - connection = - new Ice.ConnectionI(_instance, transceiver, endpoint.compress(false), null, threadPerConnection); - connection.start(); - connection.validate(); + connection = createConnection(ci.connector.connect(timeout), ci); + connection.start(null); if(defaultsAndOverrides.overrideCompress) { @@ -369,86 +229,79 @@ public final class OutgoingConnectionFactory } else { - compress.value = endpoint.compress(); + compress.value = ci.endpoint.compress(); } + break; } - catch(Ice.LocalException ex) + catch(Ice.CommunicatorDestroyedException ex) { exception = ex; - - // - // If a connection object was constructed, then validate() - // must have raised the exception. - // - if(connection != null) - { - connection.waitUntilFinished(); // We must call waitUntilFinished() for cleanup. - connection = null; - } + handleException(exception, ci, connection, hasMore || p.hasNext()); + connection = null; + break; // No need to continue } - - TraceLevels traceLevels = _instance.traceLevels(); - if(traceLevels.retry >= 2) + catch(Ice.LocalException ex) { - StringBuffer s = new StringBuffer(); - s.append("connection to endpoint failed"); - if(hasMore || p.hasNext()) - { - s.append(", trying next endpoint\n"); - } - else - { - s.append(" and no more endpoints to try\n"); - } - s.append(exception.toString()); - _instance.initializationData().logger.trace(traceLevels.retryCat, s.toString()); + exception = ex; + handleException(exception, ci, connection, hasMore || p.hasNext()); + connection = null; } } - - synchronized(this) + + // + // Finish creating the connection (this removes the connectors from the _pending + // list and notifies any waiting threads). + // + finishGetConnection(connectors, null, connection); + + if(connection == null) { - // - // Signal other threads that we are done with trying to - // establish connections to our endpoints. - // - p = connectors.iterator(); - while(p.hasNext()) - { - ConnectorEndpointPair cep = (ConnectorEndpointPair)p.next(); - _pending.remove(cep.connector); - } - notifyAll(); - - if(connection == null) + assert(exception != null); + throw exception; + } + + return connection; + } + + public void + create(EndpointI[] endpts, boolean hasMore, boolean tpc, Ice.EndpointSelectionType selType, + CreateConnectionCallback callback) + { + assert(endpts.length > 0); + + // + // TODO: Remove when we no longer support SSL for JDK 1.4. We can also remove + // the threadPerConnection argument. + // + for(int i = 0; i < endpts.length; i++) + { + if(!tpc && endpts[i].requiresThreadPerConnection()) { - assert(exception != null); - throw exception; + Ice.FeatureNotSupportedException ex = new Ice.FeatureNotSupportedException(); + ex.unsupportedFeature = "endpoint requires thread-per-connection:\n" + endpts[i].toString(); + throw ex; } - else - { - java.util.LinkedList connectionList = (java.util.LinkedList)_connections.get(connector); - if(connectionList == null) - { - connectionList = new java.util.LinkedList(); - _connections.put(connector, connectionList); - } - connectionList.add(connection); + } - if(_destroyed) - { - connection.destroy(Ice.ConnectionI.CommunicatorDestroyed); - throw new Ice.CommunicatorDestroyedException(); - } - else - { - connection.activate(); - } - } + // + // Apply the overrides. + // + java.util.List endpoints = applyOverrides(endpts); + + // + // Try to find a connection to one of the given endpoints. + // + Ice.BooleanHolder compress = new Ice.BooleanHolder(); + Ice.ConnectionI connection = findConnection(endpoints, tpc, compress); + if(connection != null) + { + callback.setConnection(connection, compress.value); + return; } - - assert(connection != null); - return connection; + + ConnectCallback cb = new ConnectCallback(this, endpoints, hasMore, callback, selType, tpc); + cb.getConnection(); } public synchronized void @@ -607,8 +460,740 @@ public final class OutgoingConnectionFactory super.finalize(); } + private java.util.List + applyOverrides(EndpointI[] endpts) + { + DefaultsAndOverrides defaultsAndOverrides = _instance.defaultsAndOverrides(); + java.util.ArrayList endpoints = new java.util.ArrayList(); + for(int i = 0; i < endpts.length; i++) + { + // + // Modify endpoints with overrides. + // + if(defaultsAndOverrides.overrideTimeout) + { + endpoints.add(endpts[i].timeout(defaultsAndOverrides.overrideTimeoutValue)); + } + else + { + endpoints.add(endpts[i]); + } + } + + return endpoints; + } + + synchronized private Ice.ConnectionI + findConnection(java.util.List endpoints, boolean tpc, Ice.BooleanHolder compress) + { + DefaultsAndOverrides defaultsAndOverrides = _instance.defaultsAndOverrides(); + assert(!endpoints.isEmpty()); + + java.util.Iterator p = endpoints.iterator(); + while(p.hasNext()) + { + EndpointI endpoint = (EndpointI)p.next(); + java.util.LinkedList connectionList = (java.util.LinkedList)_connectionsByEndpoint.get(endpoint); + if(connectionList == null) + { + continue; + } + + java.util.Iterator q = connectionList.iterator(); + while(q.hasNext()) + { + Ice.ConnectionI connection = (Ice.ConnectionI)q.next(); + if(connection.isActiveOrHolding() && + connection.threadPerConnection() == tpc) // Don't return destroyed or un-validated connections + { + if(defaultsAndOverrides.overrideCompress) + { + compress.value = defaultsAndOverrides.overrideCompressValue; + } + else + { + compress.value = endpoint.compress(); + } + return connection; + } + } + } + + return null; + } + + // + // Must be called while synchronized. + // + private Ice.ConnectionI + findConnection(java.util.List connectors, Ice.BooleanHolder compress) + { + DefaultsAndOverrides defaultsAndOverrides = _instance.defaultsAndOverrides(); + java.util.Iterator p = connectors.iterator(); + while(p.hasNext()) + { + ConnectorInfo ci = (ConnectorInfo)p.next(); + java.util.LinkedList connectionList = (java.util.LinkedList)_connections.get(ci); + if(connectionList == null) + { + continue; + } + + java.util.Iterator q = connectionList.iterator(); + while(q.hasNext()) + { + Ice.ConnectionI connection = (Ice.ConnectionI)q.next(); + if(connection.isActiveOrHolding()) // Don't return destroyed or un-validated connections + { + if(connection.endpoint().equals(ci.endpoint)) + { + java.util.List conList = (java.util.LinkedList)_connectionsByEndpoint.get(ci.endpoint); + if(conList == null) + { + conList = new java.util.LinkedList(); + _connectionsByEndpoint.put(ci.endpoint, conList); + } + conList.add(connection); + } + + if(defaultsAndOverrides.overrideCompress) + { + compress.value = defaultsAndOverrides.overrideCompressValue; + } + else + { + compress.value = ci.endpoint.compress(); + } + return connection; + } + } + } + + return null; + } + + synchronized private void + addPendingEndpoints(java.util.List endpoints) + { + if(_destroyed) + { + throw new Ice.CommunicatorDestroyedException(); + } + _pendingEndpoints.addAll(endpoints); + } + + synchronized private void + removePendingEndpoints(java.util.List endpoints) + { + java.util.Iterator p = endpoints.iterator(); + while(p.hasNext()) + { + _pendingEndpoints.remove(p.next()); + } + + if(_destroyed) + { + notifyAll(); + } + } + + private Ice.ConnectionI + getConnection(java.util.List connectors, ConnectCallback cb, Ice.BooleanHolder compress) + { + synchronized(this) + { + if(_destroyed) + { + throw new Ice.CommunicatorDestroyedException(); + } + + // + // Reap connections for which destruction has completed. + // + java.util.Iterator p = _connections.values().iterator(); + while(p.hasNext()) + { + java.util.LinkedList connectionList = (java.util.LinkedList)p.next(); + java.util.Iterator q = connectionList.iterator(); + while(q.hasNext()) + { + Ice.ConnectionI con = (Ice.ConnectionI)q.next(); + if(con.isFinished()) + { + q.remove(); + } + } + + if(connectionList.isEmpty()) + { + p.remove(); + } + } + + p = _connectionsByEndpoint.values().iterator(); + while(p.hasNext()) + { + java.util.LinkedList connectionList = (java.util.LinkedList)p.next(); + java.util.Iterator q = connectionList.iterator(); + while(q.hasNext()) + { + Ice.ConnectionI con = (Ice.ConnectionI)q.next(); + if(con.isFinished()) + { + q.remove(); + } + } + + if(connectionList.isEmpty()) + { + p.remove(); + } + } + + // + // Try to get the connection. We may need to wait for other threads to + // finish if one of them is currently establishing a connection to one + // of our connectors. + // + while(!_destroyed) + { + // + // Search for a matching connection. If we find one, we're done. + // + Ice.ConnectionI connection = findConnection(connectors, compress); + if(connection != null) + { + if(cb != null) + { + // + // This might not be the first getConnection call for the callback. We need + // to ensure that the callback isn't registered with any other pending + // connectors since we just found a connection and therefore don't need to + // wait anymore for other pending connectors. + // + p = connectors.iterator(); + while(p.hasNext()) + { + java.util.Set cbs = (java.util.Set)_pending.get(p.next()); + if(cbs != null) + { + cbs.remove(cb); + } + } + } + return connection; + } + + // + // Determine whether another thread is currently attempting to connect to one of our endpoints; + // if so we wait until it's done. + // + p = connectors.iterator(); + boolean found = false; + while(p.hasNext()) + { + java.util.Set cbs = (java.util.Set)_pending.get(p.next()); + if(cbs != null) + { + found = true; + if(cb != null) + { + cbs.add(cb); // Add the callback to each pending connector. + } + } + } + + if(!found) + { + // + // If no thread is currently establishing a connection to one of our connectors, + // we get out of this loop and start the connection establishment to one of the + // given connectors. + // + break; + } + else + { + // + // If a callback is not specified we wait until another thread notifies us about a + // change to the pending list. Otherwise, if a callback is provided we're done: + // when the pending list changes the callback will be notified and will try to + // get the connection again. + // + if(cb == null) + { + try + { + wait(); + } + catch(InterruptedException ex) + { + } + } + else + { + return null; + } + } + } + + if(_destroyed) + { + throw new Ice.CommunicatorDestroyedException(); + } + + // + // No connection to any of our endpoints exists yet; we add the given connectors to + // the _pending set to indicate that we're attempting connection establishment to + // these connectors. + // + p = connectors.iterator(); + while(p.hasNext()) + { + _pending.put(p.next(), new java.util.HashSet()); + } + } + + // + // At this point, we're responsible for establishing the connection to one of + // the given connectors. If it's a non-blocking connect, calling nextConnector + // will start the connection establishment. Otherwise, we return null to get + // the caller to establish the connection. + // + if(cb != null) + { + cb.nextConnector(); + } + + return null; + } + + private synchronized Ice.ConnectionI + createConnection(Transceiver transceiver, ConnectorInfo ci) + { + assert(_pending.containsKey(ci) && transceiver != null); + + // + // Create and add the connection to the connection map. Adding the connection to the map + // is necessary to support the interruption of the connection initialization and validation + // in case the communicator is destroyed. + // + try + { + if(_destroyed) + { + throw new Ice.CommunicatorDestroyedException(); + } + + Ice.ConnectionI connection = new Ice.ConnectionI(_instance, transceiver, ci.endpoint.compress(false), + null, ci.threadPerConnection); + + java.util.LinkedList connectionList = (java.util.LinkedList)_connections.get(ci); + if(connectionList == null) + { + connectionList = new java.util.LinkedList(); + _connections.put(ci, connectionList); + } + connectionList.add(connection); + return connection; + } + catch(Ice.LocalException ex) + { + try + { + transceiver.close(); + } + catch(Ice.LocalException exc) + { + // Ignore + } + throw ex; + } + } + + private void + finishGetConnection(java.util.List connectors, ConnectCallback cb, Ice.ConnectionI connection) + { + java.util.ArrayList callbacks = new java.util.ArrayList(); + + synchronized(this) + { + // + // We're done trying to connect to the given connectors so we remove the + // connectors from the pending list and notify waiting threads. We also + // notify the pending connect callbacks (outside the synchronization). + // + + java.util.Iterator p = connectors.iterator(); + while(p.hasNext()) + { + callbacks.addAll((java.util.Set)_pending.remove(p.next())); + } + notifyAll(); + + // + // If the connect attempt succeeded and the communicator is not destroyed, + // activate the connection! + // + if(connection != null && !_destroyed) + { + connection.activate(); + } + } + + // + // Notify any waiting callbacks. + // + java.util.Iterator p = callbacks.iterator(); + while(p.hasNext()) + { + ((ConnectCallback)p.next()).getConnection(); + } + } + + private void + handleException(Ice.LocalException ex, ConnectorInfo ci, Ice.ConnectionI connection, boolean hasMore) + { + TraceLevels traceLevels = _instance.traceLevels(); + if(traceLevels.retry >= 2) + { + StringBuffer s = new StringBuffer(); + s.append("connection to endpoint failed"); + if(ex instanceof Ice.CommunicatorDestroyedException) + { + s.append("\n"); + } + else + { + if(hasMore) + { + s.append(", trying next endpoint\n"); + } + else + { + s.append(" and no more endpoints to try\n"); + } + } + s.append(ex.toString()); + _instance.initializationData().logger.trace(traceLevels.retryCat, s.toString()); + } + + if(connection != null && connection.isFinished()) + { + // + // If the connection is finished, we remove it right away instead of + // waiting for the reaping. + // + // NOTE: it's possible for the connection to not be finished yet. That's + // for instance the case when using thread per connection and if it's the + // thread which is calling back the outgoing connection factory to notify + // it of the failure. + // + synchronized(this) + { + java.util.LinkedList connectionList = (java.util.LinkedList)_connections.get(ci); + if(connectionList != null) // It might have already been reaped! + { + connectionList.remove(connection); + if(connectionList.isEmpty()) + { + _connections.remove(ci); + } + } + } + } + } + + private void + handleException(Ice.LocalException ex, boolean hasMore) + { + TraceLevels traceLevels = _instance.traceLevels(); + if(traceLevels.retry >= 2) + { + StringBuffer s = new StringBuffer(); + s.append("couldn't resolve endpoint host"); + if(ex instanceof Ice.CommunicatorDestroyedException) + { + s.append("\n"); + } + else + { + if(hasMore) + { + s.append(", trying next endpoint\n"); + } + else + { + s.append(" and no more endpoints to try\n"); + } + } + s.append(ex.toString()); + _instance.initializationData().logger.trace(traceLevels.retryCat, s.toString()); + } + } + + private static class ConnectorInfo + { + public ConnectorInfo(Connector c, EndpointI e, boolean t) + { + connector = c; + endpoint = e; + threadPerConnection = t; + } + + public boolean + equals(Object obj) + { + ConnectorInfo r = (ConnectorInfo)obj; + if(threadPerConnection != r.threadPerConnection) + { + return false; + } + return connector.equals(r.connector); + } + + public int + hashCode() + { + return 2 * connector.hashCode() + (threadPerConnection ? 0 : 1); + } + + public Connector connector; + public EndpointI endpoint; + public boolean threadPerConnection; + } + + private static class ConnectCallback implements Ice.ConnectionI.StartCallback, EndpointI_connectors, + ThreadPoolWorkItem + { + ConnectCallback(OutgoingConnectionFactory f, java.util.List endpoints, boolean more, + CreateConnectionCallback cb, Ice.EndpointSelectionType selType, boolean threadPerConnection) + { + _factory = f; + _endpoints = endpoints; + _hasMore = more; + _callback = cb; + _selType = selType; + _threadPerConnection = threadPerConnection; + _endpointsIter = _endpoints.iterator(); + } + + // + // Methods from ConnectionI.StartCallback + // + public synchronized void + connectionStartCompleted(Ice.ConnectionI connection) + { + assert(_exception == null && connection == _connection); + + boolean compress; + DefaultsAndOverrides defaultsAndOverrides = _factory._instance.defaultsAndOverrides(); + if(defaultsAndOverrides.overrideCompress) + { + compress = defaultsAndOverrides.overrideCompressValue; + } + else + { + compress = _current.endpoint.compress(); + } + + _factory.finishGetConnection(_connectors, this, connection); + _factory.removePendingEndpoints(_endpoints); + _callback.setConnection(connection, compress); + } + + public synchronized void + connectionStartFailed(Ice.ConnectionI connection, Ice.LocalException ex) + { + assert(_exception == null && connection == _connection); + + _exception = ex; + handleException(); + } + + // + // Methods from EndpointI_connectors + // + public void + connectors(java.util.List cons) + { + // + // Shuffle connectors if endpoint selection type is Random. + // + if(_selType == Ice.EndpointSelectionType.Random) + { + java.util.Collections.shuffle(cons); + } + + java.util.Iterator q = cons.iterator(); + while(q.hasNext()) + { + _connectors.add(new ConnectorInfo((Connector)q.next(), _currentEndpoint, _threadPerConnection)); + } + + if(_endpointsIter.hasNext()) + { + _currentEndpoint = (EndpointI)_endpointsIter.next(); + _currentEndpoint.connectors_async(this); + } + else + { + assert(!_connectors.isEmpty()); + + // + // We now have all the connectors for the given endpoints. We can try to obtain the + // connection. + // + _iter = _connectors.iterator(); + getConnection(); + } + } + + public void + exception(Ice.LocalException ex) + { + _factory.handleException(ex, _hasMore || _endpointsIter.hasNext()); + if(_endpointsIter.hasNext()) + { + _currentEndpoint = (EndpointI)_endpointsIter.next(); + _currentEndpoint.connectors_async(this); + } + else if(!_connectors.isEmpty()) + { + // + // We now have all the connectors for the given endpoints. We can try to obtain the + // connection. + // + _iter = _connectors.iterator(); + getConnection(); + } + else + { + _exception = ex; + _factory._instance.clientThreadPool().execute(this); + } + } + + // + // Methods from ThreadPoolWorkItem + // + public void + execute(ThreadPool threadPool) + { + threadPool.promoteFollower(); + assert(_exception != null); + _factory.removePendingEndpoints(_endpoints); + _callback.setException(_exception); + } + + void + getConnection() + { + // + // First, get the connectors for all the endpoints. + // + if(_endpointsIter.hasNext()) + { + try + { + _factory.addPendingEndpoints(_endpoints); + _currentEndpoint = (EndpointI)_endpointsIter.next(); + _currentEndpoint.connectors_async(this); + } + catch(Ice.LocalException ex) + { + _callback.setException(ex); + } + return; + } + + try + { + Ice.BooleanHolder compress = new Ice.BooleanHolder(); + Ice.ConnectionI connection = _factory.getConnection(_connectors, this, compress); + if(connection == null) + { + // + // A null return value from getConnection indicates that the connection + // is being established and that everthing has been done to ensure that + // the callback will be notified when the connection establishment is + // done. + // + return; + } + + _factory.removePendingEndpoints(_endpoints); + _callback.setConnection(connection, compress.value); + } + catch(Ice.LocalException ex) + { + _exception = ex; + _factory._instance.clientThreadPool().execute(this); + } + } + + void + nextConnector() + { + _current = (ConnectorInfo)_iter.next(); + try + { + _exception = null; + _connection = _factory.createConnection(_current.connector.connect(0), _current); + _connection.start(this); + } + catch(Ice.LocalException ex) + { + _exception = ex; + handleException(); + } + } + + private void + handleException() + { + assert(_current != null && _exception != null); + + _factory.handleException(_exception, _current, _connection, _hasMore || _iter.hasNext()); + if(_exception instanceof Ice.CommunicatorDestroyedException) // No need to continue. + { + _factory.finishGetConnection(_connectors, this, null); + _factory.removePendingEndpoints(_endpoints); + _callback.setException(_exception); + } + else if(_iter.hasNext()) // Try the next connector. + { + nextConnector(); + } + else + { + _factory.finishGetConnection(_connectors, this, null); + _factory.removePendingEndpoints(_endpoints); + _callback.setException(_exception); + } + } + + private final OutgoingConnectionFactory _factory; + private final boolean _hasMore; + private final CreateConnectionCallback _callback; + private final java.util.List _endpoints; + private final Ice.EndpointSelectionType _selType; + private final boolean _threadPerConnection; + private java.util.Iterator _endpointsIter; + private EndpointI _currentEndpoint; + private java.util.List _connectors = new java.util.ArrayList(); + private java.util.Iterator _iter; + private ConnectorInfo _current; + private Ice.LocalException _exception; + private Ice.ConnectionI _connection; + } + private final Instance _instance; private boolean _destroyed; + private java.util.HashMap _connections = new java.util.HashMap(); - private java.util.HashSet _pending = new java.util.HashSet(); + private java.util.HashMap _pending = new java.util.HashMap(); + + private java.util.HashMap _connectionsByEndpoint = new java.util.HashMap(); + private java.util.LinkedList _pendingEndpoints = new java.util.LinkedList(); } diff --git a/java/src/IceInternal/OutgoingMessageCallback.java b/java/src/IceInternal/OutgoingMessageCallback.java new file mode 100644 index 00000000000..53c7a744c12 --- /dev/null +++ b/java/src/IceInternal/OutgoingMessageCallback.java @@ -0,0 +1,16 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2007 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +package IceInternal; + +public interface OutgoingMessageCallback +{ + void sent(boolean notify); + void finished(Ice.LocalException ex); +} diff --git a/java/src/IceInternal/PropertyNames.java b/java/src/IceInternal/PropertyNames.java index 1f9fc892260..49e5b9e5673 100644 --- a/java/src/IceInternal/PropertyNames.java +++ b/java/src/IceInternal/PropertyNames.java @@ -7,7 +7,7 @@ // // ********************************************************************** // -// Generated by makeprops.py from file ../config/PropertyNames.xml, Thu Nov 22 20:40:28 2007 +// Generated by makeprops.py from file ../config/PropertyNames.xml, Mon Nov 26 11:29:02 2007 // IMPORTANT: Do not edit this file -- any edits made here will be lost! diff --git a/java/src/IceInternal/ProtocolPluginFacade.java b/java/src/IceInternal/ProtocolPluginFacade.java index 52b92d5f57d..8423491d10f 100644 --- a/java/src/IceInternal/ProtocolPluginFacade.java +++ b/java/src/IceInternal/ProtocolPluginFacade.java @@ -18,6 +18,11 @@ public interface ProtocolPluginFacade Ice.Communicator getCommunicator(); // + // Get the endpoint host resolver. + // + IceInternal.EndpointHostResolver getEndpointHostResolver(); + + // // Get the default hostname to be used in endpoints. // String getDefaultHost(); @@ -32,4 +37,9 @@ public interface ProtocolPluginFacade // Register an EndpointFactory. // void addEndpointFactory(EndpointFactory factory); + + // + // Get an EndpointFactory. + // + EndpointFactory getEndpointFactory(short type); } diff --git a/java/src/IceInternal/ProtocolPluginFacadeI.java b/java/src/IceInternal/ProtocolPluginFacadeI.java index 53ae82a59d1..1dacd623499 100644 --- a/java/src/IceInternal/ProtocolPluginFacadeI.java +++ b/java/src/IceInternal/ProtocolPluginFacadeI.java @@ -29,6 +29,15 @@ public class ProtocolPluginFacadeI implements ProtocolPluginFacade } // + // Get the endpoint host resolver. + // + public EndpointHostResolver + getEndpointHostResolver() + { + return _instance.endpointHostResolver(); + } + + // // Get the default hostname to be used in endpoints. // public String @@ -61,6 +70,15 @@ public class ProtocolPluginFacadeI implements ProtocolPluginFacade _instance.endpointFactoryManager().add(factory); } + // + // Register an EndpointFactory. + // + public EndpointFactory + getEndpointFactory(short type) + { + return _instance.endpointFactoryManager().get(type); + } + private Instance _instance; private Ice.Communicator _communicator; } diff --git a/java/src/IceInternal/Reference.java b/java/src/IceInternal/Reference.java index 59ccbee7bec..5d486b41af3 100644 --- a/java/src/IceInternal/Reference.java +++ b/java/src/IceInternal/Reference.java @@ -18,6 +18,12 @@ public abstract class Reference implements Cloneable public final static int ModeBatchDatagram = 4; public final static int ModeLast = ModeBatchDatagram; + public interface GetConnectionCallback + { + void setConnection(Ice.ConnectionI connection, boolean compress); + void setException(Ice.LocalException ex); + } + public final int getMode() { @@ -325,6 +331,7 @@ public abstract class Reference implements Cloneable } public abstract Ice.ConnectionI getConnection(Ice.BooleanHolder comp); + public abstract void getConnection(GetConnectionCallback callback); public boolean equals(java.lang.Object obj) diff --git a/java/src/IceInternal/ReferenceFactory.java b/java/src/IceInternal/ReferenceFactory.java index 784c45c4aa3..37f24e64c56 100644 --- a/java/src/IceInternal/ReferenceFactory.java +++ b/java/src/IceInternal/ReferenceFactory.java @@ -100,7 +100,7 @@ public final class ReferenceFactory // // Create new reference // - FixedReference ref = new FixedReference(_instance, _communicator, ident, context, facet, mode, + FixedReference ref = new FixedReference(_instance, _communicator, ident, context, facet, mode, fixedConnections); return updateCache(ref); } diff --git a/java/src/IceInternal/RequestHandler.java b/java/src/IceInternal/RequestHandler.java new file mode 100644 index 00000000000..49df9e6bc8d --- /dev/null +++ b/java/src/IceInternal/RequestHandler.java @@ -0,0 +1,34 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2007 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +package IceInternal; + +public interface RequestHandler +{ + void prepareBatchRequest(BasicStream out); + void finishBatchRequest(BasicStream out); + void abortBatchRequest(); + + Ice.ConnectionI sendRequest(Outgoing out) + throws LocalExceptionWrapper; + + void sendAsyncRequest(OutgoingAsync out); + + boolean flushBatchRequests(BatchOutgoing out); + void flushAsyncBatchRequests(BatchOutgoingAsync out); + + Reference getReference(); + + Ice.ConnectionI getConnection(boolean wait); + + Outgoing getOutgoing(String operation, Ice.OperationMode mode, java.util.Map context) + throws LocalExceptionWrapper; + + void reclaimOutgoing(Outgoing out); +} diff --git a/java/src/IceInternal/RoutableReference.java b/java/src/IceInternal/RoutableReference.java index f04c30eb86c..213453b71cd 100644 --- a/java/src/IceInternal/RoutableReference.java +++ b/java/src/IceInternal/RoutableReference.java @@ -17,20 +17,6 @@ public abstract class RoutableReference extends Reference return _routerInfo; } - public final EndpointI[] - getRoutedEndpoints() - { - if(_routerInfo != null) - { - // - // If we route, we send everything to the router's client - // proxy endpoints. - // - return _routerInfo.getClientEndpoints(); - } - return new EndpointI[0]; - } - public final boolean getSecure() { @@ -252,6 +238,7 @@ public abstract class RoutableReference extends Reference { return false; } + return _routerInfo == null ? rhs._routerInfo == null : _routerInfo.equals(rhs._routerInfo); } @@ -304,8 +291,8 @@ public abstract class RoutableReference extends Reference } } - protected Ice.ConnectionI - createConnection(EndpointI[] allEndpoints, Ice.BooleanHolder compress) + private EndpointI[] + filterEndpoints(EndpointI[] allEndpoints) { java.util.ArrayList endpoints = new java.util.ArrayList(); @@ -413,25 +400,30 @@ public abstract class RoutableReference extends Reference java.util.Collections.sort(endpoints, _preferNonSecureEndpointComparator); } - if(endpoints.size() == 0) + return (EndpointI[])endpoints.toArray(new EndpointI[endpoints.size()]); + } + + protected Ice.ConnectionI + createConnection(EndpointI[] allEndpoints, Ice.BooleanHolder compress) + { + EndpointI[] endpoints = filterEndpoints(allEndpoints); + if(endpoints.length == 0) { - Ice.NoEndpointException ex = new Ice.NoEndpointException(); - ex.proxy = toString(); - throw ex; + throw new Ice.NoEndpointException(toString()); } // // Finally, create the connection. // OutgoingConnectionFactory factory = getInstance().outgoingConnectionFactory(); - if(getCacheConnection() || endpoints.size() == 1) + Ice.ConnectionI connection = null; + if(getCacheConnection() || endpoints.length == 1) { // // Get an existing connection or create one if there's no // existing connection to one of the given endpoints. // - return factory.create((EndpointI[])endpoints.toArray( - new EndpointI[endpoints.size()]), false, _threadPerConnection, getEndpointSelection(), compress); + connection = factory.create(endpoints, false, _threadPerConnection, getEndpointSelection(), compress); } else { @@ -444,16 +436,16 @@ public abstract class RoutableReference extends Reference // Ice.LocalException exception = null; - EndpointI[] endpoint = new EndpointI[1]; - - java.util.Iterator i = endpoints.iterator(); - while(i.hasNext()) + EndpointI[] endpoint = new EndpointI[1]; + for(int i = 0; i < endpoints.length; ++i) { try { - endpoint[0] = (EndpointI)i.next(); - return factory.create(endpoint, i.hasNext(), _threadPerConnection, getEndpointSelection(), - compress); + endpoint[0] = endpoints[i]; + final boolean more = i != endpoints.length - 1; + connection = factory.create(endpoint, more, _threadPerConnection, getEndpointSelection(), + compress); + break; } catch(Ice.LocalException ex) { @@ -461,8 +453,123 @@ public abstract class RoutableReference extends Reference } } - assert(exception != null); - throw exception; + if(connection == null) + { + assert(exception != null); + throw exception; + } + } + + assert(connection != null); + + // + // If we have a router, set the object adapter for this router + // (if any) to the new connection, so that callbacks from the + // router can be received over this new connection. + // + if(_routerInfo != null) + { + connection.setAdapter(_routerInfo.getAdapter()); + } + + return connection; + } + + protected void + createConnection(EndpointI[] allEndpoints, final GetConnectionCallback callback) + { + final EndpointI[] endpoints = filterEndpoints(allEndpoints); + if(endpoints.length == 0) + { + callback.setException(new Ice.NoEndpointException(toString())); + return; + } + + // + // Finally, create the connection. + // + final OutgoingConnectionFactory factory = getInstance().outgoingConnectionFactory(); + if(getCacheConnection() || endpoints.length == 1) + { + // + // Get an existing connection or create one if there's no + // existing connection to one of the given endpoints. + // + factory.create(endpoints, false, _threadPerConnection, getEndpointSelection(), + new OutgoingConnectionFactory.CreateConnectionCallback() + { + public void + setConnection(Ice.ConnectionI connection, boolean compress) + { + // + // If we have a router, set the object adapter for this router + // (if any) to the new connection, so that callbacks from the + // router can be received over this new connection. + // + if(_routerInfo != null) + { + connection.setAdapter(_routerInfo.getAdapter()); + } + callback.setConnection(connection, compress); + } + + public void + setException(Ice.LocalException ex) + { + callback.setException(ex); + } + }); + } + else + { + // + // Go through the list of endpoints and try to create the + // connection until it succeeds. This is different from just + // calling create() with the given endpoints since this might + // create a new connection even if there's an existing + // connection for one of the endpoints. + // + + factory.create(new EndpointI[]{ endpoints[0] }, true, _threadPerConnection, getEndpointSelection(), + new OutgoingConnectionFactory.CreateConnectionCallback() + { + public void + setConnection(Ice.ConnectionI connection, boolean compress) + { + // + // If we have a router, set the object adapter for this router + // (if any) to the new connection, so that callbacks from the + // router can be received over this new connection. + // + if(_routerInfo != null) + { + connection.setAdapter(_routerInfo.getAdapter()); + } + callback.setConnection(connection, compress); + } + + public void + setException(final Ice.LocalException ex) + { + if(_exception == null) + { + _exception = ex; + } + + if(++_i == endpoints.length) + { + callback.setException(_exception); + return; + } + + final boolean more = _i != endpoints.length - 1; + final EndpointI[] endpoint = new EndpointI[]{ endpoints[_i] }; + factory.create(endpoint, more, _threadPerConnection, getEndpointSelection(), this); + } + + private int _i = 0; + private Ice.LocalException _exception = null; + }); } } diff --git a/java/src/IceInternal/RouterInfo.java b/java/src/IceInternal/RouterInfo.java index d9e6c6f6839..cf4180a7a0f 100644 --- a/java/src/IceInternal/RouterInfo.java +++ b/java/src/IceInternal/RouterInfo.java @@ -11,6 +11,18 @@ package IceInternal; public final class RouterInfo { + interface GetClientEndpointsCallback + { + void setEndpoints(EndpointI[] endpoints); + void setException(Ice.LocalException ex); + } + + interface AddProxyCallback + { + void addedProxy(); + void setException(Ice.LocalException ex); + } + RouterInfo(Ice.RouterPrx router) { _router = router; @@ -52,12 +64,136 @@ public final class RouterInfo return _router; } - public synchronized EndpointI[] + public EndpointI[] getClientEndpoints() { - if(_clientEndpoints == null) // Lazy initialization. + synchronized(this) + { + if(_clientEndpoints != null) // Lazy initialization. + { + return _clientEndpoints; + } + } + + return setClientEndpoints(_router.getClientProxy()); + } + + public void + getClientEndpoints(final GetClientEndpointsCallback callback) + { + EndpointI[] clientEndpoints = null; + synchronized(this) + { + clientEndpoints = _clientEndpoints; + } + + if(clientEndpoints != null) + { + callback.setEndpoints(clientEndpoints); + return; + } + + final RouterInfo self = this; + _router.getClientProxy_async(new Ice.AMI_Router_getClientProxy() + { + public void + ice_response(Ice.ObjectPrx clientProxy) + { + callback.setEndpoints(setClientEndpoints(clientProxy)); + } + + public void + ice_exception(Ice.LocalException ex) + { + callback.setException(ex); + } + }); + } + + public EndpointI[] + getServerEndpoints() + { + synchronized(this) + { + if(_serverEndpoints != null) // Lazy initialization. + { + return _serverEndpoints; + } + } + + return setServerEndpoints(_router.getServerProxy()); + } + + public void + addProxy(Ice.ObjectPrx proxy) + { + assert(proxy != null); + synchronized(this) + { + if(_identities.contains(proxy.ice_getIdentity())) + { + // + // Only add the proxy to the router if it's not already in our local map. + // + return; + } + } + + addAndEvictProxies(proxy, _router.addProxies(new Ice.ObjectPrx[] { proxy })); + } + + public boolean + addProxy(final Ice.ObjectPrx proxy, final AddProxyCallback callback) + { + assert(proxy != null); + synchronized(this) + { + if(_identities.contains(proxy.ice_getIdentity())) + { + // + // Only add the proxy to the router if it's not already in our local map. + // + return true; + } + } + + _router.addProxies_async(new Ice.AMI_Router_addProxies() + { + public void + ice_response(Ice.ObjectPrx[] evictedProxies) + { + addAndEvictProxies(proxy, evictedProxies); + callback.addedProxy(); + } + + public void + ice_exception(Ice.LocalException ex) + { + callback.setException(ex); + } + }, + new Ice.ObjectPrx[] { proxy }); + + return false; + } + + public synchronized void + setAdapter(Ice.ObjectAdapter adapter) + { + _adapter = adapter; + } + + public synchronized Ice.ObjectAdapter + getAdapter() + { + return _adapter; + } + + private synchronized EndpointI[] + setClientEndpoints(Ice.ObjectPrx clientProxy) + { + if(_clientEndpoints == null) { - Ice.ObjectPrx clientProxy = _router.getClientProxy(); if(clientProxy == null) { // @@ -82,76 +218,69 @@ public final class RouterInfo { // Ignore - collocated router. } - + _clientEndpoints = ((Ice.ObjectPrxHelperBase)clientProxy).__reference().getEndpoints(); } } - return _clientEndpoints; } - public synchronized EndpointI[] - getServerEndpoints() + private synchronized EndpointI[] + setServerEndpoints(Ice.ObjectPrx serverProxy) { - if(_serverEndpoints == null) // Lazy initialization. + if(serverProxy == null) { - Ice.ObjectPrx serverProxy = _router.getServerProxy(); - if(serverProxy == null) - { - throw new Ice.NoEndpointException(); - } - - serverProxy = serverProxy.ice_router(null); // The server proxy cannot be routed. - _serverEndpoints = ((Ice.ObjectPrxHelperBase)serverProxy).__reference().getEndpoints(); + throw new Ice.NoEndpointException(); } - + + serverProxy = serverProxy.ice_router(null); // The server proxy cannot be routed. + _serverEndpoints = ((Ice.ObjectPrxHelperBase)serverProxy).__reference().getEndpoints(); return _serverEndpoints; } - public synchronized void - addProxy(Ice.ObjectPrx proxy) + private synchronized void + addAndEvictProxies(Ice.ObjectPrx proxy, Ice.ObjectPrx[] evictedProxies) { - assert(proxy != null); - - if(!_identities.contains(proxy.ice_getIdentity())) + // + // Check if the proxy hasn't already been evicted by a + // concurrent addProxies call. If it's the case, don't + // add it to our local map. + // + int index = _evictedIdentities.indexOf(proxy.ice_getIdentity()); + if(index >= 0) + { + _evictedIdentities.remove(index); + } + else { // - // Only add the proxy to the router if it's not already in our local map. - // - Ice.ObjectPrx[] proxies = new Ice.ObjectPrx[1]; - proxies[0] = proxy; - Ice.ObjectPrx[] evictedProxies = _router.addProxies(proxies); - - // - // If we successfully added the proxy to the router, we add it to our local map. + // If we successfully added the proxy to the router, + // we add it to our local map. // _identities.add(proxy.ice_getIdentity()); + } - // - // We also must remove whatever proxies the router evicted. - // - for(int i = 0; i < evictedProxies.length; ++i) + // + // We also must remove whatever proxies the router evicted. + // + for(int i = 0; i < evictedProxies.length; ++i) + { + if(!_identities.remove(evictedProxies[i].ice_getIdentity())) { - _identities.remove(evictedProxies[i].ice_getIdentity()); + // + // It's possible for the proxy to not have been + // added yet in the local map if two threads + // concurrently call addProxies. + // + _evictedIdentities.add(evictedProxies[i].ice_getIdentity()); } } } - public synchronized void - setAdapter(Ice.ObjectAdapter adapter) - { - _adapter = adapter; - } - - public synchronized Ice.ObjectAdapter - getAdapter() - { - return _adapter; - } - private final Ice.RouterPrx _router; private EndpointI[] _clientEndpoints; private EndpointI[] _serverEndpoints; private Ice.ObjectAdapter _adapter; private java.util.HashSet _identities = new java.util.HashSet(); + private java.util.List _evictedIdentities = new java.util.ArrayList(); } diff --git a/java/src/IceInternal/RouterManager.java b/java/src/IceInternal/RouterManager.java index a34091fd479..9aad2116754 100644 --- a/java/src/IceInternal/RouterManager.java +++ b/java/src/IceInternal/RouterManager.java @@ -39,7 +39,10 @@ public final class RouterManager return null; } - Ice.RouterPrx router = Ice.RouterPrxHelper.uncheckedCast(rtr.ice_router(null)); // The router cannot be routed. + // + // The router cannot be routed. + // + Ice.RouterPrx router = Ice.RouterPrxHelper.uncheckedCast(rtr.ice_router(null)); synchronized(this) { diff --git a/java/src/IceInternal/SelectorThread.java b/java/src/IceInternal/SelectorThread.java new file mode 100644 index 00000000000..6a291e36926 --- /dev/null +++ b/java/src/IceInternal/SelectorThread.java @@ -0,0 +1,511 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2007 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +package IceInternal; + +public class SelectorThread +{ + public interface SocketReadyCallback + { + // + // The selector thread unregisters the callback when socketReady returns SocketStatus.Finished. + // + SocketStatus socketReady(boolean finished); + + // + // The selector thread doesn't unregister the callback when sockectTimeout is called; socketTimeout + // must unregister the callback either explicitly with unregister() or by shutting down the socket + // (if necessary). + // + void socketTimeout(); + } + + SelectorThread(Instance instance) + { + _instance = instance; + _destroyed = false; + + Network.SocketPair pair = Network.createPipe(); + _fdIntrRead = (java.nio.channels.ReadableByteChannel)pair.source; + _fdIntrWrite = pair.sink; + + try + { + _selector = java.nio.channels.Selector.open(); + pair.source.configureBlocking(false); + _fdIntrReadKey = pair.source.register(_selector, java.nio.channels.SelectionKey.OP_READ); + } + catch(java.io.IOException ex) + { + Ice.SyscallException sys = new Ice.SyscallException(); + sys.initCause(ex); + throw sys; + } + + // + // The Selector holds a Set representing the selected keys. The + // Set reference doesn't change, so we obtain it once here. + // + _keys = _selector.selectedKeys(); + + _thread = new HelperThread(); + _thread.start(); + + _timer = _instance.timer(); + } + + protected synchronized void + finalize() + throws Throwable + { + IceUtil.Assert.FinalizerAssert(_destroyed); + } + + public synchronized void + destroy() + { + assert(!_destroyed); + _destroyed = true; + setInterrupt(); + } + + public synchronized void + _register(java.nio.channels.SelectableChannel fd, SocketReadyCallback cb, SocketStatus status, int timeout) + { + assert(!_destroyed); // The selector thread is destroyed after the incoming/outgoing connection factories. + assert(status != SocketStatus.Finished); + + SocketInfo info = new SocketInfo(fd, cb, status, timeout); + _changes.add(info); + if(info.timeout >= 0) + { + _timer.schedule(info, info.timeout); + } + setInterrupt(); + } + + // + // Unregister the given file descriptor. The registered callback will be notified with socketReady() + // upon registration to allow some cleanup to be done. + // + public synchronized void + unregister(java.nio.channels.SelectableChannel fd) + { + assert(!_destroyed); // The selector thread is destroyed after the incoming/outgoing connection factories. + _changes.add(new SocketInfo(fd, null, SocketStatus.Finished, 0)); + setInterrupt(); + } + + public void + joinWithThread() + { + if(_thread != null) + { + try + { + _thread.join(); + } + catch(InterruptedException ex) + { + } + } + } + + private void + clearInterrupt() + { + byte b = 0; + + java.nio.ByteBuffer buf = java.nio.ByteBuffer.allocate(1); + try + { + while(true) + { + buf.rewind(); + if(_fdIntrRead.read(buf) != 1) + { + break; + } + + b = buf.get(0); + break; + } + } + catch(java.io.IOException ex) + { + Ice.SocketException se = new Ice.SocketException(); + se.initCause(ex); + throw se; + } + } + + private void + setInterrupt() + { + java.nio.ByteBuffer buf = java.nio.ByteBuffer.allocate(1); + buf.put(0, (byte)0); + while(buf.hasRemaining()) + { + try + { + _fdIntrWrite.write(buf); + } + catch(java.io.IOException ex) + { + Ice.SocketException se = new Ice.SocketException(); + se.initCause(ex); + throw se; + } + } + } + + public void + run() + { + java.util.HashMap socketMap = new java.util.HashMap(); + java.util.LinkedList readyList = new java.util.LinkedList(); + java.util.LinkedList finishedList = new java.util.LinkedList(); + while(true) + { + int ret = 0; + + while(true) + { + try + { + ret = _selector.select(); + } + catch(java.io.IOException ex) + { + // + // Pressing Ctrl-C causes select() to raise an + // IOException, which seems like a JDK bug. We trap + // for that special case here and ignore it. + // Hopefully we're not masking something important! + // + if(Network.interrupted(ex)) + { + continue; + } + + Ice.SocketException se = new Ice.SocketException(); + se.initCause(ex); + //throw se; + java.io.StringWriter sw = new java.io.StringWriter(); + java.io.PrintWriter pw = new java.io.PrintWriter(sw); + se.printStackTrace(pw); + pw.flush(); + String s = "exception in selector thread:\n" + sw.toString(); + _instance.initializationData().logger.error(s); + continue; + } + + break; + } + + assert(readyList.isEmpty() && finishedList.isEmpty()); + + if(_keys.contains(_fdIntrReadKey) && _fdIntrReadKey.isReadable()) + { + synchronized(this) + { + // + // There are two possiblities for an interrupt: + // + // 1. The selector thread has been destroyed. + // 2. A socket was registered or unregistered. + // + + // + // Thread destroyed? + // + if(_destroyed) + { + break; + } + + // + // Remove the interrupt channel from the selected key set. + // + _keys.remove(_fdIntrReadKey); + + clearInterrupt(); + SocketInfo info = (SocketInfo)_changes.removeFirst(); + if(info.cb != null) // Registration + { + try + { + info.key = info.fd.register(_selector, convertStatus(info.status), info); + } + catch(java.nio.channels.ClosedChannelException ex) + { + assert(false); + } + assert(!socketMap.containsKey(info.fd)); + socketMap.put(info.fd, info); + } + else // Unregistration + { + info = (SocketInfo)socketMap.get(info.fd); + if(info != null && info.status != SocketStatus.Finished) + { + if(info.timeout >= 0) + { + _timer.cancel(info); + } + + try + { + info.key.cancel(); + } + catch(java.nio.channels.CancelledKeyException ex) + { + assert(false); + } + info.status = SocketStatus.Finished; + readyList.add(info); + } + } + } + } + else + { + // + // Examine the selection key set. + // + java.util.Iterator iter = _keys.iterator(); + while(iter.hasNext()) + { + // + // Ignore selection keys that have been cancelled or timed out. + // + java.nio.channels.SelectionKey key = (java.nio.channels.SelectionKey)iter.next(); + iter.remove(); + assert(key != _fdIntrReadKey); + SocketInfo info = (SocketInfo)key.attachment(); + if(info.timeout >= 0) + { + _timer.cancel(info); + } + assert(key.isValid()); + readyList.add(info); + } + } + + java.util.Iterator iter = readyList.iterator(); + while(iter.hasNext()) + { + SocketInfo info = (SocketInfo)iter.next(); + SocketStatus status; + try + { + status = info.cb.socketReady(info.status == SocketStatus.Finished); + } + catch(Ice.LocalException ex) + { + java.io.StringWriter sw = new java.io.StringWriter(); + java.io.PrintWriter pw = new java.io.PrintWriter(sw); + ex.printStackTrace(pw); + pw.flush(); + String s = "exception in selector thread " + _thread.getName() + + " while calling socketReady():\n" + sw.toString(); + _instance.initializationData().logger.error(s); + status = SocketStatus.Finished; + } + + if(status == SocketStatus.Finished) + { + finishedList.add(info); + } + else + { + assert(info.status != SocketStatus.Finished); + try + { + info.status = status; + info.key.interestOps(convertStatus(status)); + if(info.timeout >= 0) + { + _timer.schedule(info, info.timeout); + } + } + catch(java.nio.channels.CancelledKeyException ex) + { + assert(false); + } + } + } + readyList.clear(); + + if(finishedList.isEmpty()) + { + continue; + } + + iter = finishedList.iterator(); + while(iter.hasNext()) + { + SocketInfo info = (SocketInfo)iter.next(); + if(info.status != SocketStatus.Finished) + { + try + { + info.key.cancel(); + } + catch(java.nio.channels.CancelledKeyException ex) + { + //assert(false); // The channel might already be closed at this point so we can't assert. + } + } + socketMap.remove(info.fd); + } + finishedList.clear(); + } + + assert(_destroyed); + + try + { + _selector.close(); + } + catch(java.io.IOException ex) + { + // Ignore. + } + + try + { + _fdIntrWrite.close(); + } + catch(java.io.IOException ex) + { + // + // BUGFIX: + // + // Ignore this exception. This shouldn't happen + // but for some reasons the close() call raises + // "java.io.IOException: No such file or + // directory" under Linux with JDK 1.4.2. + // + } + _fdIntrWrite = null; + + try + { + _fdIntrRead.close(); + } + catch(java.io.IOException ex) + { + } + _fdIntrRead = null; + } + + private int + convertStatus(SocketStatus status) + { + if(status == SocketStatus.NeedConnect) + { + return java.nio.channels.SelectionKey.OP_CONNECT; + } + else if(status == SocketStatus.NeedRead) + { + return java.nio.channels.SelectionKey.OP_READ; + } + else + { + assert(status == SocketStatus.NeedWrite); + return java.nio.channels.SelectionKey.OP_WRITE; + } + } + + private Instance _instance; + private boolean _destroyed; + private java.nio.channels.ReadableByteChannel _fdIntrRead; + private java.nio.channels.SelectionKey _fdIntrReadKey; + private java.nio.channels.WritableByteChannel _fdIntrWrite; + private java.nio.channels.Selector _selector; + private java.util.Set _keys; + private java.util.LinkedList _changes = new java.util.LinkedList(); + + private final class SocketInfo implements TimerTask + { + java.nio.channels.SelectableChannel fd; + SocketReadyCallback cb; + SocketStatus status; + int timeout; + java.nio.channels.SelectionKey key; + + public void + runTimerTask() + { + this.cb.socketTimeout(); // Exceptions will be reported by the timer thread. + } + + SocketInfo(java.nio.channels.SelectableChannel fd, SocketReadyCallback cb, SocketStatus status, int timeout) + { + this.fd = fd; + this.cb = cb; + this.status = status; + this.timeout = timeout; + } + } + + private final class HelperThread extends Thread + { + HelperThread() + { + String threadName = _instance.initializationData().properties.getProperty("Ice.ProgramName"); + if(threadName.length() > 0) + { + threadName += "-"; + } + setName(threadName + "Ice.SelectorThread"); + } + + public void + run() + { + if(_instance.initializationData().threadHook != null) + { + _instance.initializationData().threadHook.start(); + } + + try + { + SelectorThread.this.run(); + } + catch(Ice.LocalException ex) + { + java.io.StringWriter sw = new java.io.StringWriter(); + java.io.PrintWriter pw = new java.io.PrintWriter(sw); + ex.printStackTrace(pw); + pw.flush(); + String s = "exception in selector thread " + getName() + ":\n" + sw.toString(); + _instance.initializationData().logger.error(s); + } + catch(java.lang.Exception ex) + { + java.io.StringWriter sw = new java.io.StringWriter(); + java.io.PrintWriter pw = new java.io.PrintWriter(sw); + ex.printStackTrace(pw); + pw.flush(); + String s = "unknown exception in selector thread " + getName() + ":\n" + sw.toString(); + _instance.initializationData().logger.error(s); + } + + if(_instance.initializationData().threadHook != null) + { + _instance.initializationData().threadHook.stop(); + } + } + } + + private HelperThread _thread; + private Timer _timer; +} diff --git a/java/src/IceInternal/SocketStatus.java b/java/src/IceInternal/SocketStatus.java new file mode 100644 index 00000000000..dc3bed4a735 --- /dev/null +++ b/java/src/IceInternal/SocketStatus.java @@ -0,0 +1,39 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2007 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +package IceInternal; + +public final class SocketStatus +{ + private static SocketStatus[] _values = new SocketStatus[4]; + + public static final int _Finished = 0; + public static final SocketStatus Finished = new SocketStatus(_Finished); + public static final int _NeedConnect = 1; + public static final SocketStatus NeedConnect = new SocketStatus(_NeedConnect); + public static final int _NeedRead = 2; + public static final SocketStatus NeedRead = new SocketStatus(_NeedRead); + public static final int _NeedWrite = 3; + public static final SocketStatus NeedWrite = new SocketStatus(_NeedWrite); + + public int + value() + { + return _value; + } + + private + SocketStatus(int val) + { + _value = val; + _values[val] = this; + } + + private int _value; +} diff --git a/java/src/IceInternal/TcpAcceptor.java b/java/src/IceInternal/TcpAcceptor.java index 21d56732fc6..72eaec50cf9 100644 --- a/java/src/IceInternal/TcpAcceptor.java +++ b/java/src/IceInternal/TcpAcceptor.java @@ -160,7 +160,7 @@ class TcpAcceptor implements Acceptor _logger.trace(_traceLevels.networkCat, s); } - return new TcpTransceiver(_instance, fd); + return new TcpTransceiver(_instance, fd, true); } public void diff --git a/java/src/IceInternal/TcpConnector.java b/java/src/IceInternal/TcpConnector.java index 2635d9ad881..ab2a2a877e3 100644 --- a/java/src/IceInternal/TcpConnector.java +++ b/java/src/IceInternal/TcpConnector.java @@ -11,8 +11,6 @@ package IceInternal; final class TcpConnector implements Connector, java.lang.Comparable { - final static short TYPE = 1; - public Transceiver connect(int timeout) { @@ -25,21 +23,22 @@ final class TcpConnector implements Connector, java.lang.Comparable java.nio.channels.SocketChannel fd = Network.createTcpSocket(); Network.setBlock(fd, false); Network.setTcpBufSize(fd, _instance.initializationData().properties, _logger); - Network.doConnect(fd, _addr, timeout); - - if(_traceLevels.network >= 1) + boolean connected = Network.doConnect(fd, _addr, timeout); + if(connected) { - String s = "tcp connection established\n" + Network.fdToString(fd); - _logger.trace(_traceLevels.networkCat, s); + if(_traceLevels.network >= 1) + { + String s = "tcp connection established\n" + Network.fdToString(fd); + _logger.trace(_traceLevels.networkCat, s); + } } - - return new TcpTransceiver(_instance, fd); + return new TcpTransceiver(_instance, fd, connected); } public short type() { - return TYPE; + return TcpEndpointI.TYPE; } public String @@ -54,21 +53,6 @@ final class TcpConnector implements Connector, java.lang.Comparable return _hashCode; } - public final boolean - equivalent(String host, int port) - { - java.net.InetSocketAddress addr; - try - { - addr = Network.getAddress(host, port); - } - catch(Ice.DNSException ex) - { - return false; - } - return addr.equals(_addr); - } - // // Only for use by TcpEndpoint // @@ -149,6 +133,13 @@ final class TcpConnector implements Connector, java.lang.Comparable return Network.compareAddress(_addr, p._addr); } + protected synchronized void + finalize() + throws Throwable + { + super.finalize(); + } + private Instance _instance; private TraceLevels _traceLevels; private Ice.Logger _logger; diff --git a/java/src/IceInternal/TcpEndpointI.java b/java/src/IceInternal/TcpEndpointI.java index 941b1ee0cc3..90b3073b3ce 100644 --- a/java/src/IceInternal/TcpEndpointI.java +++ b/java/src/IceInternal/TcpEndpointI.java @@ -332,17 +332,16 @@ final class TcpEndpointI extends EndpointI // Return connectors for this endpoint, or empty list if no connector // is available. // - public java.util.ArrayList + public java.util.List connectors() { - java.util.ArrayList connectors = new java.util.ArrayList(); - java.util.ArrayList addresses = Network.getAddresses(_host, _port); - java.util.Iterator p = addresses.iterator(); - while(p.hasNext()) - { - connectors.add(new TcpConnector(_instance, (java.net.InetSocketAddress)p.next(), _timeout, _connectionId)); - } - return connectors; + return connectors(Network.getAddresses(_host, _port)); + } + + public void + connectors_async(EndpointI_connectors callback) + { + _instance.endpointHostResolver().resolve(_host, _port, this, callback); } // @@ -364,7 +363,7 @@ final class TcpEndpointI extends EndpointI // Expand endpoint out in to separate endpoints for each local // host if listening on INADDR_ANY. // - public java.util.ArrayList + public java.util.List expand() { java.util.ArrayList endps = new java.util.ArrayList(); @@ -389,21 +388,21 @@ final class TcpEndpointI extends EndpointI } // - // Check whether the endpoint is equivalent to a specific Connector + // Check whether the endpoint is equivalent to another one. // public boolean - equivalent(Connector connector) + equivalent(EndpointI endpoint) { - TcpConnector tcpConnector = null; + TcpEndpointI tcpEndpointI = null; try { - tcpConnector = (TcpConnector)connector; + tcpEndpointI = (TcpEndpointI)endpoint; } catch(ClassCastException ex) { return false; } - return tcpConnector.equivalent(_host, _port); + return tcpEndpointI._host.equals(_host) && tcpEndpointI._port == _port; } public int @@ -489,6 +488,18 @@ final class TcpEndpointI extends EndpointI return false; } + public java.util.List + connectors(java.util.List addresses) + { + java.util.ArrayList connectors = new java.util.ArrayList(); + java.util.Iterator p = addresses.iterator(); + while(p.hasNext()) + { + connectors.add(new TcpConnector(_instance, (java.net.InetSocketAddress)p.next(), _timeout, _connectionId)); + } + return connectors; + } + private void calcHashValue() { diff --git a/java/src/IceInternal/TcpTransceiver.java b/java/src/IceInternal/TcpTransceiver.java index f07bff3a62c..533b91adfc7 100644 --- a/java/src/IceInternal/TcpTransceiver.java +++ b/java/src/IceInternal/TcpTransceiver.java @@ -18,6 +18,29 @@ final class TcpTransceiver implements Transceiver return _fd; } + public SocketStatus + initialize(int timeout) + { + if(_state == StateNeedConnect && timeout == 0) + { + _state = StateConnectPending; + return SocketStatus.NeedConnect; + } + else if(_state <= StateConnectPending) + { + Network.doFinishConnect(_fd, timeout); + _state = StateConnected; + _desc = Network.fdToString(_fd); + if(_traceLevels.network >= 1) + { + String s = "tcp connection established\n" + _desc; + _logger.trace(_traceLevels.networkCat, s); + } + } + assert(_state == StateConnected); + return SocketStatus.Finished; + } + public void close() { @@ -74,6 +97,11 @@ final class TcpTransceiver implements Transceiver public void shutdownWrite() { + if(_state < StateConnected) + { + return; + } + if(_traceLevels.network >= 2) { String s = "shutting down tcp connection for writing\n" + toString(); @@ -110,6 +138,11 @@ final class TcpTransceiver implements Transceiver public void shutdownReadWrite() { + if(_state < StateConnected) + { + return; + } + if(_traceLevels.network >= 2) { String s = "shutting down tcp connection for reading and writing\n" + toString(); @@ -144,125 +177,78 @@ final class TcpTransceiver implements Transceiver } } - public void - write(BasicStream stream, int timeout) + public boolean + write(Buffer buf, int timeout) throws LocalExceptionWrapper { - java.nio.ByteBuffer buf = stream.prepareWrite(); - int size = buf.limit(); - int packetSize = 0; - if(_maxPacketSize > 0 && size > _maxPacketSize) + while(writeBuffer(buf.b)) { - packetSize = _maxPacketSize; - buf.limit(buf.position() + packetSize); - } + // + // There is more data to write but the socket would block; now we + // must deal with timeouts. + // + assert(buf.b.hasRemaining()); - while(buf.hasRemaining()) - { + if(timeout == 0) + { + return false; + } + try { - assert(_fd != null); - int ret = _fd.write(buf); - - if(ret == -1) + if(_writeSelector == null) { - throw new Ice.ConnectionLostException(); + _writeSelector = java.nio.channels.Selector.open(); + _fd.register(_writeSelector, java.nio.channels.SelectionKey.OP_WRITE, null); } - if(ret == 0) + try { - if(timeout == 0) + if(timeout > 0) { - throw new Ice.TimeoutException(); - } - - if(_writeSelector == null) - { - _writeSelector = java.nio.channels.Selector.open(); - _fd.register(_writeSelector, java.nio.channels.SelectionKey.OP_WRITE, null); - } - - try - { - if(timeout > 0) - { - long start = IceInternal.Time.currentMonotonicTimeMillis(); - int n = _writeSelector.select(timeout); - if(n == 0 && IceInternal.Time.currentMonotonicTimeMillis() >= start + timeout) - { - throw new Ice.TimeoutException(); - } - } - else + long start = IceInternal.Time.currentMonotonicTimeMillis(); + int n = _writeSelector.select(timeout); + if(n == 0 && IceInternal.Time.currentMonotonicTimeMillis() >= start + timeout) { - _writeSelector.select(); + throw new Ice.TimeoutException(); } } - catch(java.io.InterruptedIOException ex) + else { - // Ignore. + _writeSelector.select(); } - - continue; } - - - if(_traceLevels.network >= 3) - { - String s = "sent " + ret + " of " + buf.limit() + " bytes via tcp\n" + toString(); - _logger.trace(_traceLevels.networkCat, s); - } - - if(_stats != null) - { - _stats.bytesSent(type(), ret); - } - - if(packetSize > 0) + catch(java.io.InterruptedIOException ex) { - assert(buf.position() == buf.limit()); - int position = buf.position(); - if(size - position > packetSize) - { - buf.limit(position + packetSize); - } - else - { - packetSize = 0; - buf.limit(size); - } + // Ignore. } } - catch(java.io.InterruptedIOException ex) - { - continue; - } catch(java.io.IOException ex) { Ice.SocketException se = new Ice.SocketException(); se.initCause(ex); throw se; } - } + } + return true; } public boolean - read(BasicStream stream, int timeout) + read(Buffer buf, int timeout, Ice.BooleanHolder moreData) { - java.nio.ByteBuffer buf = stream.prepareRead(); - int remaining = 0; if(_traceLevels.network >= 3) { - remaining = buf.remaining(); + remaining = buf.b.remaining(); } + moreData.value = false; - while(buf.hasRemaining()) + while(buf.b.hasRemaining()) { try { assert(_fd != null); - int ret = _fd.read(buf); + int ret = _fd.read(buf.b); if(ret == -1) { @@ -273,7 +259,7 @@ final class TcpTransceiver implements Transceiver { if(timeout == 0) { - throw new Ice.TimeoutException(); + return false; } if(_readSelector == null) @@ -339,7 +325,7 @@ final class TcpTransceiver implements Transceiver } } - return false; + return true; } public String @@ -355,9 +341,9 @@ final class TcpTransceiver implements Transceiver } public void - checkSendSize(BasicStream stream, int messageSizeMax) + checkSendSize(Buffer buf, int messageSizeMax) { - if(stream.size() > messageSizeMax) + if(buf.size() > messageSizeMax) { throw new Ice.MemoryLimitException(); } @@ -366,12 +352,13 @@ final class TcpTransceiver implements Transceiver // // Only for use by TcpConnector, TcpAcceptor // - TcpTransceiver(Instance instance, java.nio.channels.SocketChannel fd) + TcpTransceiver(Instance instance, java.nio.channels.SocketChannel fd, boolean connected) { _fd = fd; _traceLevels = instance.traceLevels(); _logger = instance.initializationData().logger; _stats = instance.initializationData().stats; + _state = connected ? StateConnected : StateNeedConnect; _desc = Network.fdToString(_fd); _maxPacketSize = 0; @@ -399,12 +386,89 @@ final class TcpTransceiver implements Transceiver super.finalize(); } + private boolean + writeBuffer(java.nio.ByteBuffer buf) + { + final int size = buf.limit(); + int packetSize = size - buf.position(); + if(_maxPacketSize > 0 && packetSize > _maxPacketSize) + { + packetSize = _maxPacketSize; + buf.limit(buf.position() + packetSize); + } + + while(buf.hasRemaining()) + { + try + { + assert(_fd != null); + int ret = _fd.write(buf); + + if(ret == -1) + { + throw new Ice.ConnectionLostException(); + } + else if(ret == 0) + { + // + // Writing would block, so we reset the limit (if necessary) and return true to indicate + // that more data must be sent. + // + if(packetSize == _maxPacketSize) + { + buf.limit(size); + } + return true; + } + + if(_traceLevels.network >= 3) + { + String s = "sent " + ret + " of " + size + " bytes via tcp\n" + toString(); + _logger.trace(_traceLevels.networkCat, s); + } + + if(_stats != null) + { + _stats.bytesSent(type(), ret); + } + + if(packetSize == _maxPacketSize) + { + assert(buf.position() == buf.limit()); + packetSize = size - buf.position(); + if(packetSize > _maxPacketSize) + { + packetSize = _maxPacketSize; + } + buf.limit(buf.position() + packetSize); + } + } + catch(java.io.InterruptedIOException ex) + { + continue; + } + catch(java.io.IOException ex) + { + Ice.SocketException se = new Ice.SocketException(); + se.initCause(ex); + throw se; + } + } + + return false; // No more data to send. + } + private java.nio.channels.SocketChannel _fd; private TraceLevels _traceLevels; private Ice.Logger _logger; private Ice.Stats _stats; private String _desc; + private int _state; private java.nio.channels.Selector _readSelector; private java.nio.channels.Selector _writeSelector; private int _maxPacketSize; + + private static final int StateNeedConnect = 0; + private static final int StateConnectPending = 1; + private static final int StateConnected = 2; } diff --git a/java/src/IceInternal/ThreadPool.java b/java/src/IceInternal/ThreadPool.java index 30ed86a3082..6306ca10475 100644 --- a/java/src/IceInternal/ThreadPool.java +++ b/java/src/IceInternal/ThreadPool.java @@ -146,6 +146,7 @@ public final class ThreadPool assert(!_destroyed); assert(_handlerMap.isEmpty()); assert(_changes.isEmpty()); + assert(_workItems.isEmpty()); _destroyed = true; setInterrupt(); } @@ -193,6 +194,14 @@ public final class ThreadPool setInterrupt(); } + public synchronized void + execute(ThreadPoolWorkItem workItem) + { + assert(!_destroyed); + _workItems.add(workItem); + setInterrupt(); + } + public void promoteFollower() { @@ -468,6 +477,7 @@ public final class ThreadPool } EventHandler handler = null; + ThreadPoolWorkItem workItem = null; // // Only call select() if there are no pending handlers with additional data @@ -510,14 +520,15 @@ public final class ThreadPool } // - // There are two possiblities for an interrupt: + // There are three possiblities for an interrupt: // // 1. The thread pool has been destroyed. // // 2. An event handler was registered or unregistered. // - + // 3. A work item has been scheduled. // + // Thread pool destroyed? // if(_destroyed) @@ -547,57 +558,79 @@ public final class ThreadPool // An event handler must have been registered // or unregistered. // - assert(!_changes.isEmpty()); - FdHandlerPair change = (FdHandlerPair)_changes.removeFirst(); - - if(change.handler != null) // Addition if handler is set. + if(!_changes.isEmpty()) { - int op; - if((change.fd.validOps() & java.nio.channels.SelectionKey.OP_READ) > 0) - { - op = java.nio.channels.SelectionKey.OP_READ; - } - else + FdHandlerPair change = (FdHandlerPair)_changes.removeFirst(); + + if(change.handler != null) // Addition if handler is set. { - op = java.nio.channels.SelectionKey.OP_ACCEPT; - } + int op; + if((change.fd.validOps() & java.nio.channels.SelectionKey.OP_READ) > 0) + { + op = java.nio.channels.SelectionKey.OP_READ; + } + else + { + op = java.nio.channels.SelectionKey.OP_ACCEPT; + } - java.nio.channels.SelectionKey key = null; - try - { - key = change.fd.register(_selector, op, change.handler); + java.nio.channels.SelectionKey key = null; + try + { + key = change.fd.register(_selector, op, change.handler); + } + catch(java.nio.channels.ClosedChannelException ex) + { + // + // This is expected if the transceiver finishConnect() call failed + // and the connection is a background connection. + // + } + _handlerMap.put(change.fd, new HandlerKeyPair(change.handler, key)); + + // + // If the handler is readable and already has some data to read add it + // to the _pendingHandlers list to ensure it will be processed. + // + if(change.handler.readable() && change.handler.hasMoreData()) + { + _pendingHandlers.add(change.handler); + } + + if(TRACE_REGISTRATION) + { + trace("added handler (" + change.handler.getClass().getName() + ") for fd " + + change.fd); + } + + continue; } - catch(java.nio.channels.ClosedChannelException ex) + else // Removal if handler is not set. { - assert(false); - } - _handlerMap.put(change.fd, new HandlerKeyPair(change.handler, key)); + HandlerKeyPair pair = (HandlerKeyPair)_handlerMap.remove(change.fd); + assert(pair != null); + handler = pair.handler; + finished = true; + if(pair.key != null) + { + pair.key.cancel(); + } - if(TRACE_REGISTRATION) - { - trace("added handler (" + change.handler.getClass().getName() + ") for fd " + - change.fd); - } + if(TRACE_REGISTRATION) + { + trace("removed handler (" + handler.getClass().getName() + ") for fd " + + change.fd); + } - continue; + // Don't continue; we have to call + // finished() on the event handler below, + // outside the thread synchronization. + } } - else // Removal if handler is not set. + else { - HandlerKeyPair pair = (HandlerKeyPair)_handlerMap.remove(change.fd); - assert(pair != null); - handler = pair.handler; - finished = true; - pair.key.cancel(); - - if(TRACE_REGISTRATION) - { - trace("removed handler (" + handler.getClass().getName() + ") for fd " + - change.fd); - } - - // Don't continue; we have to call - // finished() on the event handler below, - // outside the thread synchronization. + assert(!_workItems.isEmpty()); + workItem = (ThreadPoolWorkItem)_workItems.removeFirst(); } } else @@ -672,6 +705,29 @@ public final class ThreadPool // promoteFollower(). // } + else if(workItem != null) + { + try + { + workItem.execute(this); + } + catch(Ice.LocalException ex) + { + java.io.StringWriter sw = new java.io.StringWriter(); + java.io.PrintWriter pw = new java.io.PrintWriter(sw); + ex.printStackTrace(pw); + pw.flush(); + String s = "exception in `" + _prefix + "' while calling execute():\n" + sw.toString(); + _instance.initializationData().logger.error(s); + } + + // + // No "continue", because we want execute() to + // be called in its own thread from this + // pool. Note that this means that execute() + // must call promoteFollower(). + // + } else { assert(handler != null); @@ -716,17 +772,23 @@ public final class ThreadPool { try { + if(!read(handler)) + { + continue; // Can't read without blocking. + } + // - // If read returns true, the handler has more data for the thread pool - // to process. + // If the handler has more data to process add it to the _pendingHandlers list + // to ensure it will be processed. // - if(read(handler)) + if(handler.hasMoreData()) { _pendingHandlers.add(handler); } } - catch(Ice.TimeoutException ex) // Expected. + catch(Ice.TimeoutException ex) { + assert(false); // This shouldn't occur as we only perform non-blocking reads. continue; } catch(Ice.DatagramLimitException ex) // Expected. @@ -914,8 +976,6 @@ public final class ThreadPool private boolean read(EventHandler handler) { - boolean moreData = false; - BasicStream stream = handler._stream; if(stream.size() == 0) @@ -926,7 +986,10 @@ public final class ThreadPool if(stream.pos() != stream.size()) { - moreData = handler.read(stream); + if(!handler.read(stream)) + { + return false; + } assert(stream.pos() == stream.size()); } @@ -1008,12 +1071,15 @@ public final class ThreadPool } else { - moreData = handler.read(stream); + if(!handler.read(stream)) + { + return false; + } assert(stream.pos() == stream.size()); } } - return moreData; + return true; } /* @@ -1205,6 +1271,7 @@ public final class ThreadPool private java.util.Set _keys; private java.util.LinkedList _changes = new java.util.LinkedList(); + private java.util.LinkedList _workItems = new java.util.LinkedList(); private java.util.HashMap _handlerMap = new java.util.HashMap(); diff --git a/java/src/IceInternal/ThreadPoolWorkItem.java b/java/src/IceInternal/ThreadPoolWorkItem.java new file mode 100644 index 00000000000..ba917b67708 --- /dev/null +++ b/java/src/IceInternal/ThreadPoolWorkItem.java @@ -0,0 +1,15 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2007 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +package IceInternal; + +public interface ThreadPoolWorkItem +{ + void execute(ThreadPool threadPool); +} diff --git a/java/src/IceInternal/Time.java b/java/src/IceInternal/Time.java index d77879ef836..04059a31fff 100644 --- a/java/src/IceInternal/Time.java +++ b/java/src/IceInternal/Time.java @@ -16,4 +16,4 @@ final public class Time { return System.nanoTime() / 1000000; } -}; +} diff --git a/java/src/IceInternal/Timer.java b/java/src/IceInternal/Timer.java index 44e8ba0f6eb..7646409a16c 100644 --- a/java/src/IceInternal/Timer.java +++ b/java/src/IceInternal/Timer.java @@ -11,8 +11,8 @@ package IceInternal; interface TimerTask { - void run(); -}; + void runTimerTask(); +} public final class Timer extends Thread { @@ -224,7 +224,7 @@ public final class Timer extends Thread { try { - token.task.run(); + token.task.runTimerTask(); } catch(Exception ex) { diff --git a/java/src/IceInternal/TraceUtil.java b/java/src/IceInternal/TraceUtil.java index 7a81793ffdc..6a85917a7c4 100644 --- a/java/src/IceInternal/TraceUtil.java +++ b/java/src/IceInternal/TraceUtil.java @@ -12,77 +12,41 @@ package IceInternal; public final class TraceUtil { public static void - traceHeader(String heading, BasicStream str, Ice.Logger logger, TraceLevels tl) + traceSend(BasicStream str, Ice.Logger logger, TraceLevels tl) { if(tl.protocol >= 1) { int p = str.pos(); str.pos(0); - + java.io.StringWriter s = new java.io.StringWriter(); - s.write(heading); - printHeader(s, str); + byte type = printMessage(s, str); - logger.trace(tl.protocolCat, s.toString()); - str.pos(p); - } - } + logger.trace(tl.protocolCat, "sending " + getMessageTypeAsString(type) + " " + s.toString()); - public static void - traceRequest(String heading, BasicStream str, Ice.Logger logger, TraceLevels tl) - { - if(tl.protocol >= 1) - { - int p = str.pos(); - str.pos(0); - - java.io.StringWriter s = new java.io.StringWriter(); - s.write(heading); - printHeader(s, str); - - int requestId = str.readInt(); - s.write("\nrequest id = " + requestId); - if(requestId == 0) - { - s.write(" (oneway)"); - } - - printRequestHeader(s, str); - - logger.trace(tl.protocolCat, s.toString()); str.pos(p); } } public static void - traceBatchRequest(String heading, BasicStream str, Ice.Logger logger, TraceLevels tl) + traceRecv(BasicStream str, Ice.Logger logger, TraceLevels tl) { if(tl.protocol >= 1) { int p = str.pos(); str.pos(0); - + java.io.StringWriter s = new java.io.StringWriter(); - s.write(heading); - printHeader(s, str); + byte type = printMessage(s, str); - int batchRequestNum = str.readInt(); - s.write("\nnumber of requests = " + batchRequestNum); - - for(int i = 0; i < batchRequestNum; ++i) - { - s.write("\nrequest #" + i + ':'); - printRequestHeader(s, str); - str.skipEncaps(); - } + logger.trace(tl.protocolCat, "received " + getMessageTypeAsString(type) + " " + s.toString()); - logger.trace(tl.protocolCat, s.toString()); str.pos(p); } } public static void - traceReply(String heading, BasicStream str, Ice.Logger logger, TraceLevels tl) + trace(String heading, BasicStream str, Ice.Logger logger, TraceLevels tl) { if(tl.protocol >= 1) { @@ -91,105 +55,7 @@ public final class TraceUtil java.io.StringWriter s = new java.io.StringWriter(); s.write(heading); - printHeader(s, str); - - int requestId = str.readInt(); - s.write("\nrequest id = " + requestId); - - byte replyStatus = str.readByte(); - s.write("\nreply status = " + (int)replyStatus + ' '); - - switch(replyStatus) - { - case ReplyStatus.replyOK: - { - s.write("(ok)"); - break; - } - - case ReplyStatus.replyUserException: - { - s.write("(user exception)"); - break; - } - - case ReplyStatus.replyObjectNotExist: - case ReplyStatus.replyFacetNotExist: - case ReplyStatus.replyOperationNotExist: - { - switch(replyStatus) - { - case ReplyStatus.replyObjectNotExist: - { - s.write("(object not exist)"); - break; - } - - case ReplyStatus.replyFacetNotExist: - { - s.write("(facet not exist)"); - break; - } - - case ReplyStatus.replyOperationNotExist: - { - s.write("(operation not exist)"); - break; - } - - default: - { - assert(false); - break; - } - } - - printIdentityFacetOperation(s, str); - break; - } - - case ReplyStatus.replyUnknownException: - case ReplyStatus.replyUnknownLocalException: - case ReplyStatus.replyUnknownUserException: - { - switch(replyStatus) - { - case ReplyStatus.replyUnknownException: - { - s.write("(unknown exception)"); - break; - } - - case ReplyStatus.replyUnknownLocalException: - { - s.write("(unknown local exception)"); - break; - } - - case ReplyStatus.replyUnknownUserException: - { - s.write("(unknown user exception)"); - break; - } - - default: - { - assert(false); - break; - } - } - - String unknown = str.readString(); - s.write("\nunknown = " + unknown); - break; - } - - default: - { - s.write("(unknown)"); - break; - } - } + printMessage(s, str); logger.trace(tl.protocolCat, s.toString()); str.pos(p); @@ -304,6 +170,135 @@ public final class TraceUtil } private static void + printRequest(java.io.StringWriter s, BasicStream str) + { + int requestId = str.readInt(); + s.write("\nrequest id = " + requestId); + if(requestId == 0) + { + s.write(" (oneway)"); + } + + printRequestHeader(s, str); + } + + private static void + printBatchRequest(java.io.StringWriter s, BasicStream str) + { + int batchRequestNum = str.readInt(); + s.write("\nnumber of requests = " + batchRequestNum); + + for(int i = 0; i < batchRequestNum; ++i) + { + s.write("\nrequest #" + i + ':'); + printRequestHeader(s, str); + str.skipEncaps(); + } + } + + private static void + printReply(java.io.StringWriter s, BasicStream str) + { + int requestId = str.readInt(); + s.write("\nrequest id = " + requestId); + + byte replyStatus = str.readByte(); + s.write("\nreply status = " + (int)replyStatus + ' '); + + switch(replyStatus) + { + case ReplyStatus.replyOK: + { + s.write("(ok)"); + break; + } + + case ReplyStatus.replyUserException: + { + s.write("(user exception)"); + break; + } + + case ReplyStatus.replyObjectNotExist: + case ReplyStatus.replyFacetNotExist: + case ReplyStatus.replyOperationNotExist: + { + switch(replyStatus) + { + case ReplyStatus.replyObjectNotExist: + { + s.write("(object not exist)"); + break; + } + + case ReplyStatus.replyFacetNotExist: + { + s.write("(facet not exist)"); + break; + } + + case ReplyStatus.replyOperationNotExist: + { + s.write("(operation not exist)"); + break; + } + + default: + { + assert(false); + break; + } + } + + printIdentityFacetOperation(s, str); + break; + } + + case ReplyStatus.replyUnknownException: + case ReplyStatus.replyUnknownLocalException: + case ReplyStatus.replyUnknownUserException: + { + switch(replyStatus) + { + case ReplyStatus.replyUnknownException: + { + s.write("(unknown exception)"); + break; + } + + case ReplyStatus.replyUnknownLocalException: + { + s.write("(unknown local exception)"); + break; + } + + case ReplyStatus.replyUnknownUserException: + { + s.write("(unknown user exception)"); + break; + } + + default: + { + assert(false); + break; + } + } + + String unknown = str.readString(); + s.write("\nunknown = " + unknown); + break; + } + + default: + { + s.write("(unknown)"); + break; + } + } + } + + private static void printRequestHeader(java.io.Writer out, BasicStream stream) { printIdentityFacetOperation(out, stream); @@ -358,66 +353,28 @@ public final class TraceUtil } } - private static void + private static byte printHeader(java.io.Writer out, BasicStream stream) { - try - { - byte magic; - magic = stream.readByte(); // Don't bother printing the magic number - magic = stream.readByte(); - magic = stream.readByte(); - magic = stream.readByte(); - - byte pMajor = stream.readByte(); - byte pMinor = stream.readByte(); + byte magic; + magic = stream.readByte(); // Don't bother printing the magic number + magic = stream.readByte(); + magic = stream.readByte(); + magic = stream.readByte(); + + byte pMajor = stream.readByte(); + byte pMinor = stream.readByte(); // out.write("\nprotocol version = " + (int)pMajor + "." + (int)pMinor); - - byte eMajor = stream.readByte(); - byte eMinor = stream.readByte(); + + byte eMajor = stream.readByte(); + byte eMinor = stream.readByte(); // out.write("\nencoding version = " + (int)eMajor + "." + (int)eMinor); + + byte type = stream.readByte(); - byte type = stream.readByte(); - out.write("\nmessage type = " + (int)type + ' '); - switch(type) - { - case Protocol.requestMsg: - { - out.write("(request)"); - break; - } - - case Protocol.requestBatchMsg: - { - out.write("(batch request)"); - break; - } - - case Protocol.replyMsg: - { - out.write("(reply)"); - break; - } - - case Protocol.closeConnectionMsg: - { - out.write("(close connection)"); - break; - } - - case Protocol.validateConnectionMsg: - { - out.write("(validate connection)"); - break; - } - - default: - { - out.write("(unknown)"); - break; - } - } - + try + { + out.write("\nmessage type = " + (int)type + " (" + getMessageTypeAsString(type) + ')'); byte compress = stream.readByte(); out.write("\ncompression status = " + (int)compress + ' '); switch(compress) @@ -449,10 +406,73 @@ public final class TraceUtil int size = stream.readInt(); out.write("\nmessage size = " + size); + return type; } catch(java.io.IOException ex) { assert(false); + return 0; + } + } + + static private byte + printMessage(java.io.StringWriter s, BasicStream str) + { + byte type = printHeader(s, str); + + switch(type) + { + case Protocol.closeConnectionMsg: + case Protocol.validateConnectionMsg: + { + // We're done. + break; + } + + case Protocol.requestMsg: + { + printRequest(s, str); + break; + } + + case Protocol.requestBatchMsg: + { + printBatchRequest(s, str); + break; + } + + case Protocol.replyMsg: + { + printReply(s, str); + break; + } + + default: + { + break; + } + } + + return type; + } + + static private String + getMessageTypeAsString(byte type) + { + switch(type) + { + case Protocol.requestMsg: + return "request"; + case Protocol.requestBatchMsg: + return "batch request"; + case Protocol.replyMsg: + return "reply"; + case Protocol.closeConnectionMsg: + return "close connection"; + case Protocol.validateConnectionMsg: + return "validate connection"; + default: + return "unknown"; } } } diff --git a/java/src/IceInternal/Transceiver.java b/java/src/IceInternal/Transceiver.java index 260d59bde77..7dce61206b8 100644 --- a/java/src/IceInternal/Transceiver.java +++ b/java/src/IceInternal/Transceiver.java @@ -12,23 +12,60 @@ package IceInternal; public interface Transceiver { java.nio.channels.SelectableChannel fd(); + + // + // Initialize the transceiver. + // + // Returns the status if the initialize operation. If timeout is != 0, + // the status will always be SocketStatus.Finished. If timeout is 0, + // the operation won't block and will return SocketStatus.NeedRead or + // SocketStatus.NeedWrite if the initialization couldn't be completed + // without blocking. This operation should be called again once the + // socket is ready for reading or writing and until it returns + // SocketStatus.Finished. + // + SocketStatus initialize(int timeout); + void close(); void shutdownWrite(); void shutdownReadWrite(); + + // + // Write data. + // + // Returns true if all the data was written, false otherwise. If + // timeout is -1, this operation will block until all the data is + // written. If timeout is 0, it will return when the write can't + // be completed without blocking. If the timeout is > 0, it will + // block until all the data is written or the specified timeout + // expires. // // NOTE: In Java, write() can raise LocalExceptionWrapper to indicate that // retrying may not be safe, which is necessary to address an issue // in the IceSSL implementation for JDK 1.4. We can remove this if // we ever drop support for JDK 1.4 (also see Ice.ConnectionI). // - void write(BasicStream stream, int timeout) + boolean write(Buffer buf, int timeout) throws LocalExceptionWrapper; + // - // NOTE: In Java, read() returns a boolean to indicate whether the transceiver - // has read more data than requested. + // Read data. // - boolean read(BasicStream stream, int timeout); + // Returns true if all the requested data was read, false otherwise. + // If timeout is -1, this operation will block until all the data + // is read. If timeout is 0, it will return when the read can't be + // completed without blocking. If the timeout is > 0, it will + // block until all the data is read or the specified timeout + // expires. + // + // NOTE: In Java, read() returns a boolean in moreData to indicate + // whether the transceiver has read more data than requested. + // If moreData is true, read should be called again without + // calling select on the FD. + // + boolean read(Buffer buf, int timeout, Ice.BooleanHolder moreData); + String type(); String toString(); - void checkSendSize(BasicStream stream, int messageSizeMax); + void checkSendSize(Buffer buf, int messageSizeMax); } diff --git a/java/src/IceInternal/UdpConnector.java b/java/src/IceInternal/UdpConnector.java index e3c49b6955b..37964024236 100644 --- a/java/src/IceInternal/UdpConnector.java +++ b/java/src/IceInternal/UdpConnector.java @@ -11,18 +11,23 @@ package IceInternal; final class UdpConnector implements Connector, java.lang.Comparable { - final static short TYPE = 3; - public Transceiver connect(int timeout) { return new UdpTransceiver(_instance, _addr, _mcastInterface, _mcastTtl); } + public java.nio.channels.SelectableChannel + fd() + { + assert(false); // Shouldn't be called, startConnect always completes immediately. + return null; + } + public short type() { - return TYPE; + return UdpEndpointI.TYPE; } public String @@ -37,21 +42,6 @@ final class UdpConnector implements Connector, java.lang.Comparable return _hashCode; } - final public boolean - equivalent(String host, int port) - { - java.net.InetSocketAddress addr; - try - { - addr = Network.getAddress(host, port); - } - catch(Ice.DNSException ex) - { - return false; - } - return addr.equals(_addr); - } - // // Only for use by TcpEndpoint // diff --git a/java/src/IceInternal/UdpEndpointI.java b/java/src/IceInternal/UdpEndpointI.java index a87c6d9bed1..1d635a7dff2 100644 --- a/java/src/IceInternal/UdpEndpointI.java +++ b/java/src/IceInternal/UdpEndpointI.java @@ -501,19 +501,16 @@ final class UdpEndpointI extends EndpointI // Return connectors for this endpoint, or empty list if no connector // is available. // - public java.util.ArrayList + public java.util.List connectors() { - java.util.ArrayList connectors = new java.util.ArrayList(); - java.util.ArrayList addresses = Network.getAddresses(_host, _port); - java.util.Iterator p = addresses.iterator(); - while(p.hasNext()) - { - connectors.add( - new UdpConnector(_instance, (java.net.InetSocketAddress)p.next(), _mcastInterface, _mcastTtl, - _protocolMajor, _protocolMinor, _encodingMajor, _encodingMinor, _connectionId)); - } - return connectors; + return connectors(Network.getAddresses(_host, _port)); + } + + public void + connectors_async(EndpointI_connectors callback) + { + _instance.endpointHostResolver().resolve(_host, _port, this, callback); } // @@ -534,7 +531,7 @@ final class UdpEndpointI extends EndpointI // Expand endpoint out in to separate endpoints for each local // host if listening on INADDR_ANY. // - public java.util.ArrayList + public java.util.List expand() { java.util.ArrayList endps = new java.util.ArrayList(); @@ -561,21 +558,21 @@ final class UdpEndpointI extends EndpointI } // - // Check whether the endpoint is equivalent to a specific Conenctor. + // Check whether the endpoint is equivalent to another one. // public boolean - equivalent(Connector connector) + equivalent(EndpointI endpoint) { - UdpConnector udpConnector = null; + UdpEndpointI udpEndpointI = null; try { - udpConnector = (UdpConnector)connector; + udpEndpointI = (UdpEndpointI)endpoint; } catch(ClassCastException ex) { return false; } - return udpConnector.equivalent(_host, _port); + return udpEndpointI._host.equals(_host) && udpEndpointI._port == _port; } public int @@ -712,6 +709,20 @@ final class UdpEndpointI extends EndpointI return false; } + public java.util.List + connectors(java.util.List addresses) + { + java.util.ArrayList connectors = new java.util.ArrayList(); + java.util.Iterator p = addresses.iterator(); + while(p.hasNext()) + { + connectors.add( + new UdpConnector(_instance, (java.net.InetSocketAddress)p.next(), _mcastInterface, _mcastTtl, + _protocolMajor, _protocolMinor, _encodingMajor, _encodingMinor, _connectionId)); + } + return connectors; + } + private void calcHashValue() { diff --git a/java/src/IceInternal/UdpTransceiver.java b/java/src/IceInternal/UdpTransceiver.java index 408379547f6..e04d4f0b84b 100644 --- a/java/src/IceInternal/UdpTransceiver.java +++ b/java/src/IceInternal/UdpTransceiver.java @@ -18,6 +18,15 @@ final class UdpTransceiver implements Transceiver return _fd; } + public SocketStatus + initialize(int timeout) + { + // + // Nothing to do. + // + return SocketStatus.Finished; + } + public synchronized void close() { @@ -38,6 +47,19 @@ final class UdpTransceiver implements Transceiver } _readSelector = null; } + + if(_writeSelector != null) + { + try + { + _writeSelector.close(); + } + catch(java.io.IOException ex) + { + // Ignore. + } + _writeSelector = null; + } } public void @@ -64,17 +86,19 @@ final class UdpTransceiver implements Transceiver { _readSelector.wakeup(); } + if(_writeSelector != null) + { + _writeSelector.wakeup(); + } } - public void - write(BasicStream stream, int timeout) // NOTE: timeout is not used + public boolean + write(Buffer buf, int timeout) throws LocalExceptionWrapper { - java.nio.ByteBuffer buf = stream.prepareWrite(); - - assert(buf.position() == 0); + assert(buf.b.position() == 0); final int packetSize = java.lang.Math.min(_maxPacketSize, _sndSize - _udpOverhead); - if(packetSize < buf.limit()) + if(packetSize < buf.size()) { // // We don't log a warning here because the client gets an exception anyway. @@ -82,12 +106,52 @@ final class UdpTransceiver implements Transceiver throw new Ice.DatagramLimitException(); } - while(buf.hasRemaining()) + while(buf.b.hasRemaining()) { try { assert(_fd != null); - int ret = _fd.write(buf); + + int ret = _fd.write(buf.b); + + if(ret == 0) + { + if(timeout == 0) + { + return false; + } + + synchronized(this) + { + if(_writeSelector == null) + { + _writeSelector = java.nio.channels.Selector.open(); + _fd.register(_writeSelector, java.nio.channels.SelectionKey.OP_WRITE, null); + } + } + + try + { + if(timeout > 0) + { + long start = IceInternal.Time.currentMonotonicTimeMillis(); + int n = _writeSelector.select(timeout); + if(n == 0 && IceInternal.Time.currentMonotonicTimeMillis() >= start + timeout) + { + throw new Ice.TimeoutException(); + } + } + else + { + _writeSelector.select(); + } + } + catch(java.io.InterruptedIOException ex) + { + // Ignore. + } + continue; + } if(_traceLevels.network >= 3) { @@ -100,7 +164,7 @@ final class UdpTransceiver implements Transceiver _stats.bytesSent(type(), ret); } - assert(ret == buf.limit()); + assert(ret == buf.b.limit()); break; } catch(java.nio.channels.AsynchronousCloseException ex) @@ -126,15 +190,18 @@ final class UdpTransceiver implements Transceiver throw se; } } + + return true; } public boolean - read(BasicStream stream, int timeout) // NOTE: timeout is not used + read(Buffer buf, int timeout, Ice.BooleanHolder moreData) { - assert(stream.pos() == 0); + assert(buf.b.position() == 0); + moreData.value = false; final int packetSize = java.lang.Math.min(_maxPacketSize, _rcvSize - _udpOverhead); - if(packetSize < stream.size()) + if(packetSize < buf.size()) { // // We log a warning here because this is the server side -- without the @@ -146,41 +213,8 @@ final class UdpTransceiver implements Transceiver } throw new Ice.DatagramLimitException(); } - stream.resize(packetSize, true); - java.nio.ByteBuffer buf = stream.prepareRead(); - buf.position(0); - - synchronized(this) - { - // - // Check for shutdown. - // - if(_fd == null) - { - throw new Ice.ConnectionLostException(); - } - if(_readSelector == null) - { - try - { - _readSelector = java.nio.channels.Selector.open(); - _fd.register(_readSelector, java.nio.channels.SelectionKey.OP_READ, null); - } - catch(java.io.IOException ex) - { - if(Network.connectionLost(ex)) - { - Ice.ConnectionLostException se = new Ice.ConnectionLostException(); - se.initCause(ex); - throw se; - } - - Ice.SocketException se = new Ice.SocketException(); - se.initCause(ex); - throw se; - } - } - } + buf.resize(packetSize, true); + buf.b.position(0); int ret = 0; while(true) @@ -200,17 +234,48 @@ final class UdpTransceiver implements Transceiver try { - java.net.InetSocketAddress sender = (java.net.InetSocketAddress)fd.receive(buf); - if(sender == null || buf.position() == 0) + java.net.InetSocketAddress sender = (java.net.InetSocketAddress)fd.receive(buf.b); + + if(sender == null || buf.b.position() == 0) { - // - // Wait until packet arrives or socket is closed. - // - _readSelector.select(); + if(timeout == 0) + { + return false; + } + + synchronized(this) + { + if(_readSelector == null) + { + _readSelector = java.nio.channels.Selector.open(); + _fd.register(_readSelector, java.nio.channels.SelectionKey.OP_READ, null); + } + } + + try + { + if(timeout > 0) + { + long start = IceInternal.Time.currentMonotonicTimeMillis(); + int n = _readSelector.select(timeout); + if(n == 0 && IceInternal.Time.currentMonotonicTimeMillis() >= start + timeout) + { + throw new Ice.TimeoutException(); + } + } + else + { + _readSelector.select(); + } + } + catch(java.io.InterruptedIOException ex) + { + // Ignore. + } continue; } - ret = buf.position(); + ret = buf.b.position(); if(_connect) { @@ -272,10 +337,10 @@ final class UdpTransceiver implements Transceiver _stats.bytesReceived(type(), ret); } - stream.resize(ret, true); - stream.pos(ret); + buf.resize(ret, true); + buf.b.position(ret); - return false; + return true; } public String @@ -299,14 +364,14 @@ final class UdpTransceiver implements Transceiver } public void - checkSendSize(BasicStream stream, int messageSizeMax) + checkSendSize(Buffer buf, int messageSizeMax) { - if(stream.size() > messageSizeMax) + if(buf.size() > messageSizeMax) { throw new Ice.MemoryLimitException(); } final int packetSize = java.lang.Math.min(_maxPacketSize, _sndSize - _udpOverhead); - if(packetSize < stream.size()) + if(packetSize < buf.size()) { throw new Ice.DatagramLimitException(); } @@ -608,6 +673,7 @@ final class UdpTransceiver implements Transceiver private java.nio.channels.DatagramChannel _fd; private java.net.InetSocketAddress _addr; private java.nio.channels.Selector _readSelector; + private java.nio.channels.Selector _writeSelector; private boolean mcastServer = false; // diff --git a/java/src/IceInternal/UnknownEndpointI.java b/java/src/IceInternal/UnknownEndpointI.java index 226fe8f4bd0..638a81b52e9 100644 --- a/java/src/IceInternal/UnknownEndpointI.java +++ b/java/src/IceInternal/UnknownEndpointI.java @@ -238,12 +238,18 @@ final class UnknownEndpointI extends EndpointI // Return connectors for this endpoint, or empty list if no connector // is available. // - public java.util.ArrayList + public java.util.List connectors() { return new java.util.ArrayList(); } + public void + connectors_async(EndpointI_connectors callback) + { + callback.connectors(new java.util.ArrayList()); + } + // // Return an acceptor for this endpoint, or null if no acceptors // is available. In case an acceptor is created, this operation @@ -262,7 +268,7 @@ final class UnknownEndpointI extends EndpointI // Expand endpoint out in to separate endpoints for each local // host if listening on INADDR_ANY. // - public java.util.ArrayList + public java.util.List expand() { java.util.ArrayList endps = new java.util.ArrayList(); @@ -271,10 +277,10 @@ final class UnknownEndpointI extends EndpointI } // - // Check whether the endpoint is equivalent to a specific Conenctor. + // Check whether the endpoint is equivalent to another one. // public boolean - equivalent(Connector connector) + equivalent(EndpointI endpoint) { return false; } diff --git a/java/src/IceSSL/AcceptorI.java b/java/src/IceSSL/AcceptorI.java index 04f2b3331ab..3d1ea382f35 100644 --- a/java/src/IceSSL/AcceptorI.java +++ b/java/src/IceSSL/AcceptorI.java @@ -37,14 +37,7 @@ final class AcceptorI implements IceInternal.Acceptor } if(fd != null) { - try - { - fd.close(); - } - catch(java.io.IOException ex) - { - // Ignore. - } + IceInternal.Network.closeSocketNoThrow(fd); } if(selector != null) { @@ -149,33 +142,6 @@ final class AcceptorI implements IceInternal.Acceptor } } - // - // Check whether this socket is the result of a call to connectToSelf. - // Despite the fact that connectToSelf immediately closes the socket, - // the server-side handshake process does not raise an exception. - // Furthermore, we can't simply proceed with the regular handshake - // process because we don't want to pass such a socket to the - // certificate verifier (if any). - // - // In order to detect a call to connectToSelf, we compare the remote - // address of the newly-accepted socket to that in _connectToSelfAddr. - // - java.net.SocketAddress remoteAddr = fd.socket().getRemoteSocketAddress(); - synchronized(this) - { - if(remoteAddr.equals(_connectToSelfAddr)) - { - try - { - fd.close(); - } - catch(java.io.IOException e) - { - } - return null; - } - } - javax.net.ssl.SSLEngine engine = null; try { @@ -199,24 +165,17 @@ final class AcceptorI implements IceInternal.Acceptor } catch(RuntimeException ex) { - try - { - fd.close(); - } - catch(java.io.IOException e) - { - // Ignore. - } + IceInternal.Network.closeSocketNoThrow(fd); throw ex; } if(_instance.networkTraceLevel() >= 1) { - _logger.trace(_instance.networkTraceCategory(), "attempting to accept ssl connection\n" + + _logger.trace(_instance.networkTraceCategory(), "accepting ssl connection\n" + IceInternal.Network.fdToString(fd)); } - return new TransceiverI(_instance, engine, fd, "", true, _adapterName); + return new TransceiverI(_instance, engine, fd, "", true, true, _adapterName); } public void @@ -224,17 +183,8 @@ final class AcceptorI implements IceInternal.Acceptor { java.nio.channels.SocketChannel fd = IceInternal.Network.createTcpSocket(); IceInternal.Network.setBlock(fd, false); - synchronized(this) - { - // - // connectToSelf is called to wake up the thread blocked in - // accept. We remember the originating address for use in - // accept. See accept for details. - // - IceInternal.Network.doConnect(fd, _addr, -1); - _connectToSelfAddr = (java.net.InetSocketAddress)fd.socket().getLocalSocketAddress(); - } - IceInternal.Network.closeSocket(fd); + IceInternal.Network.doConnect(fd, _addr, -1); + IceInternal.Network.closeSocketNoThrow(fd); } public String @@ -314,5 +264,4 @@ final class AcceptorI implements IceInternal.Acceptor private int _backlog; private java.net.InetSocketAddress _addr; private java.nio.channels.Selector _selector; - private java.net.InetSocketAddress _connectToSelfAddr; } diff --git a/java/src/IceSSL/ConnectorI.java b/java/src/IceSSL/ConnectorI.java index 9836a78753a..e2d25283302 100644 --- a/java/src/IceSSL/ConnectorI.java +++ b/java/src/IceSSL/ConnectorI.java @@ -11,8 +11,6 @@ package IceSSL; final class ConnectorI implements IceInternal.Connector, java.lang.Comparable { - final static short TYPE = 2; - public IceInternal.Transceiver connect(int timeout) { @@ -35,73 +33,24 @@ final class ConnectorI implements IceInternal.Connector, java.lang.Comparable java.nio.channels.SocketChannel fd = IceInternal.Network.createTcpSocket(); IceInternal.Network.setBlock(fd, false); IceInternal.Network.setTcpBufSize(fd, _instance.communicator().getProperties(), _logger); - IceInternal.Network.doConnect(fd, _addr, timeout); + boolean connected = IceInternal.Network.doConnect(fd, _addr, timeout); - TransceiverI transceiver = null; try { javax.net.ssl.SSLEngine engine = _instance.createSSLEngine(false); - - transceiver = new TransceiverI(_instance, engine, fd, _host, false, ""); -/* - transceiver.waitForHandshake(timeout); - - // - // Check IceSSL.VerifyPeer. - // - int verifyPeer = - _instance.communicator().getProperties().getPropertyAsIntWithDefault("IceSSL.VerifyPeer", 2); - if(verifyPeer > 0) - { - try - { - engine.getSession().getPeerCertificates(); - } - catch(javax.net.ssl.SSLPeerUnverifiedException ex) - { - Ice.SecurityException e = new Ice.SecurityException(); - e.reason = "IceSSL: server did not supply a certificate"; - e.initCause(ex); - throw e; - } - } -*/ - -/* - if(!ctx.verifyPeer(fd, _host, false)) - { - Ice.SecurityException ex = new Ice.SecurityException(); - ex.reason = "IceSSL: outgoing connection rejected by certificate verifier"; - throw ex; - } -*/ + return new TransceiverI(_instance, engine, fd, _host, connected, false, ""); } catch(RuntimeException ex) { - try - { - fd.close(); - } - catch(java.io.IOException e) - { - // Ignore. - } + IceInternal.Network.closeSocketNoThrow(fd); throw ex; } - - if(_instance.networkTraceLevel() >= 1) - { - String s = "ssl connection established\n" + IceInternal.Network.fdToString(fd); - _logger.trace(_instance.networkTraceCategory(), s); - } - - return transceiver; } public short type() { - return TYPE; + return EndpointI.TYPE; } public String @@ -116,21 +65,6 @@ final class ConnectorI implements IceInternal.Connector, java.lang.Comparable return _hashCode; } - final boolean - equivalent(String host, int port) - { - java.net.InetSocketAddress addr; - try - { - addr = IceInternal.Network.getAddress(host, port); - } - catch(Ice.DNSException ex) - { - return false; - } - return addr.equals(_addr); - } - // // Only for use by EndpointI. // @@ -211,6 +145,13 @@ final class ConnectorI implements IceInternal.Connector, java.lang.Comparable return IceInternal.Network.compareAddress(_addr, p._addr); } + protected synchronized void + finalize() + throws Throwable + { + super.finalize(); + } + private Instance _instance; private Ice.Logger _logger; private String _host; diff --git a/java/src/IceSSL/EndpointI.java b/java/src/IceSSL/EndpointI.java index 8cf0f559fa4..f2a66c95f13 100644 --- a/java/src/IceSSL/EndpointI.java +++ b/java/src/IceSSL/EndpointI.java @@ -332,17 +332,16 @@ final class EndpointI extends IceInternal.EndpointI // Return connectors for this endpoint, or empty list if no connector // is available. // - public java.util.ArrayList + public java.util.List connectors() { - java.util.ArrayList connectors = new java.util.ArrayList(); - java.util.ArrayList addresses = IceInternal.Network.getAddresses(_host, _port); - java.util.Iterator p = addresses.iterator(); - while(p.hasNext()) - { - connectors.add(new ConnectorI(_instance, (java.net.InetSocketAddress)p.next(), _timeout, _connectionId)); - } - return connectors; + return connectors(IceInternal.Network.getAddresses(_host, _port)); + } + + public void + connectors_async(IceInternal.EndpointI_connectors callback) + { + _instance.endpointHostResolver().resolve(_host, _port, this, callback); } // @@ -364,7 +363,7 @@ final class EndpointI extends IceInternal.EndpointI // Expand endpoint out in to separate endpoints for each local // host if listening on INADDR_ANY. // - public java.util.ArrayList + public java.util.List expand() { java.util.ArrayList endps = new java.util.ArrayList(); @@ -393,18 +392,18 @@ final class EndpointI extends IceInternal.EndpointI // Check whether the endpoint is equivalent to a specific Connector. // public boolean - equivalent(IceInternal.Connector connector) + equivalent(IceInternal.EndpointI endpoint) { - ConnectorI sslConnector = null; + EndpointI sslEndpointI = null; try { - sslConnector = (ConnectorI)connector; + sslEndpointI = (EndpointI)endpoint; } catch(ClassCastException ex) { return false; } - return sslConnector.equivalent(_host, _port); + return sslEndpointI._host.equals(_host) && sslEndpointI._port == _port; } public int @@ -490,6 +489,18 @@ final class EndpointI extends IceInternal.EndpointI return false; } + public java.util.List + connectors(java.util.List addresses) + { + java.util.ArrayList connectors = new java.util.ArrayList(); + java.util.Iterator p = addresses.iterator(); + while(p.hasNext()) + { + connectors.add(new ConnectorI(_instance, (java.net.InetSocketAddress)p.next(), _timeout, _connectionId)); + } + return connectors; + } + private void calcHashValue() { diff --git a/java/src/IceSSL/Instance.java b/java/src/IceSSL/Instance.java index 0e6bb03a571..9d0251c5c13 100644 --- a/java/src/IceSSL/Instance.java +++ b/java/src/IceSSL/Instance.java @@ -507,6 +507,12 @@ class Instance return _facade.getCommunicator(); } + IceInternal.EndpointHostResolver + endpointHostResolver() + { + return _facade.getEndpointHostResolver(); + } + String defaultHost() { @@ -702,6 +708,7 @@ class Instance return _protocols; } + // TODO: Remove void traceConnection(java.nio.channels.SocketChannel fd, javax.net.ssl.SSLEngine engine, boolean incoming) { diff --git a/java/src/IceSSL/TransceiverI.java b/java/src/IceSSL/TransceiverI.java index b43a340c703..94d32b9f881 100644 --- a/java/src/IceSSL/TransceiverI.java +++ b/java/src/IceSSL/TransceiverI.java @@ -22,6 +22,48 @@ final class TransceiverI implements IceInternal.Transceiver return _fd; } + // + // All methods that can write to the socket are synchronized. + // + public synchronized IceInternal.SocketStatus + initialize(int timeout) + { + if(_state == StateNeedConnect && timeout == 0) + { + _state = StateConnectPending; + return IceInternal.SocketStatus.NeedConnect; + } + else if(_state <= StateConnectPending) + { + IceInternal.Network.doFinishConnect(_fd, timeout); + _state = StateConnected; + _desc = IceInternal.Network.fdToString(_fd); + } + assert(_state == StateConnected); + + IceInternal.SocketStatus status; + do + { + status = handshakeNonBlocking(); + if(timeout == 0) + { + return status; + } + + if(status != IceInternal.SocketStatus.Finished) + { + handleSocketStatus(status, timeout); + } + } + while(status != IceInternal.SocketStatus.Finished); + + return status; + } + + // + // All methods that can write to the socket are synchronized. + // + public void close() { @@ -78,13 +120,7 @@ final class TransceiverI implements IceInternal.Transceiver try { - _fd.close(); - } - catch(java.io.IOException ex) - { - Ice.SocketException se = new Ice.SocketException(); - se.initCause(ex); - throw se; + IceInternal.Network.closeSocket(_fd); } finally { @@ -98,6 +134,11 @@ final class TransceiverI implements IceInternal.Transceiver public synchronized void shutdownWrite() { + if(_state < StateConnected) + { + return; + } + if(_instance.networkTraceLevel() >= 2) { String s = "shutting down ssl connection for writing\n" + toString(); @@ -139,6 +180,11 @@ final class TransceiverI implements IceInternal.Transceiver public synchronized void shutdownReadWrite() { + if(_state < StateConnected) + { + return; + } + if(_instance.networkTraceLevel() >= 2) { String s = "shutting down ssl connection for reading and writing\n" + toString(); @@ -178,112 +224,58 @@ final class TransceiverI implements IceInternal.Transceiver // // All methods that can write to the socket are synchronized. // - public synchronized void - write(IceInternal.BasicStream stream, int timeout) + public synchronized boolean + write(IceInternal.Buffer buf, int timeout) throws IceInternal.LocalExceptionWrapper { - // - // Complete handshaking first if necessary. - // - if(!_handshakeComplete) - { - handshake(timeout); - } - - ByteBuffer buf = stream.prepareWrite(); + assert(_state == StateHandshakeComplete); - // - // Write any pending data to the socket. - // - flush(timeout); - - try + IceInternal.SocketStatus status; + do { - while(buf.hasRemaining()) + status = writeNonBlocking(buf.b); + if(status != IceInternal.SocketStatus.Finished) { - final int rem = buf.remaining(); - - // - // Encrypt the buffer. - // - SSLEngineResult result = _engine.wrap(buf, _netOutput); - switch(result.getStatus()) + if(timeout == 0) { - case BUFFER_OVERFLOW: - assert(false); - break; - case BUFFER_UNDERFLOW: - assert(false); - break; - case CLOSED: - throw new Ice.ConnectionLostException(); - case OK: - break; + assert(status == IceInternal.SocketStatus.NeedWrite); + return false; } - // - // Write the encrypted data to the socket. - // - if(result.bytesProduced() > 0) - { - flush(timeout); - - if(_instance.networkTraceLevel() >= 3) - { - String s = "sent " + result.bytesConsumed() + " of " + rem + " bytes via ssl\n" + toString(); - _logger.trace(_instance.networkTraceCategory(), s); - } - - if(_stats != null) - { - _stats.bytesSent(type(), result.bytesConsumed()); - } - } + handleSocketStatus(status, timeout); } - } - catch(SSLException ex) - { - Ice.SecurityException e = new Ice.SecurityException(); - e.reason = "IceSSL: error while encoding message"; - e.initCause(ex); - throw e; - } + } + while(status != IceInternal.SocketStatus.Finished); + + return true; } public boolean - read(IceInternal.BasicStream stream, int timeout) + read(IceInternal.Buffer buf, int timeout, Ice.BooleanHolder moreData) { - ByteBuffer buf = stream.prepareRead(); + assert(_state == StateHandshakeComplete); int rem = 0; if(_instance.networkTraceLevel() >= 3) { - rem = buf.remaining(); - } - - // - // Complete handshaking first if necessary. - // - if(!_handshakeComplete) - { - handshake(timeout); + rem = buf.b.remaining(); } // // Try to satisfy the request from data we've already decrypted. // - int pos = buf.position(); - fill(buf); + int pos = buf.b.position(); + fill(buf.b); - if(_instance.networkTraceLevel() >= 3 && buf.position() > pos) + if(_instance.networkTraceLevel() >= 3 && buf.b.position() > pos) { - String s = "received " + (buf.position() - pos) + " of " + rem + " bytes via ssl\n" + toString(); + String s = "received " + (buf.b.position() - pos) + " of " + rem + " bytes via ssl\n" + toString(); _logger.trace(_instance.networkTraceCategory(), s); } - if(_stats != null && buf.position() > pos) + if(_stats != null && buf.b.position() > pos) { - _stats.bytesReceived(type(), buf.position() - pos); + _stats.bytesReceived(type(), buf.b.position() - pos); } // @@ -293,7 +285,7 @@ final class TransceiverI implements IceInternal.Transceiver // try { - while(buf.hasRemaining()) + while(buf.b.hasRemaining()) { _netInput.flip(); SSLEngineResult result = _engine.unwrap(_netInput, _appInput); @@ -301,29 +293,53 @@ final class TransceiverI implements IceInternal.Transceiver switch(result.getStatus()) { case BUFFER_OVERFLOW: + { assert(false); break; + } case BUFFER_UNDERFLOW: - read(timeout); + { + IceInternal.SocketStatus status; + do + { + status = readNonBlocking(); + if(status != IceInternal.SocketStatus.Finished) + { + if(timeout == 0) + { + assert(status == IceInternal.SocketStatus.NeedRead); + moreData.value = false; + return false; + } + + handleSocketStatus(status, timeout); + } + } + while(status != IceInternal.SocketStatus.Finished); continue; + } case CLOSED: + { throw new Ice.ConnectionLostException(); + } case OK: + { break; } + } - pos = buf.position(); - fill(buf); + pos = buf.b.position(); + fill(buf.b); - if(_instance.networkTraceLevel() >= 3 && buf.position() > pos) + if(_instance.networkTraceLevel() >= 3 && buf.b.position() > pos) { - String s = "received " + (buf.position() - pos) + " of " + rem + " bytes via ssl\n" + toString(); + String s = "received " + (buf.b.position() - pos) + " of " + rem + " bytes via ssl\n" + toString(); _logger.trace(_instance.networkTraceCategory(), s); } - if(_stats != null && buf.position() > pos) + if(_stats != null && buf.b.position() > pos) { - _stats.bytesReceived(type(), buf.position() - pos); + _stats.bytesReceived(type(), buf.b.position() - pos); } } } @@ -338,7 +354,8 @@ final class TransceiverI implements IceInternal.Transceiver // // Return a boolean to indicate whether more data is available. // - return _netInput.position() > 0; + moreData.value = _netInput.position() > 0; + return true; } public String @@ -354,9 +371,9 @@ final class TransceiverI implements IceInternal.Transceiver } public void - checkSendSize(IceInternal.BasicStream stream, int messageSizeMax) + checkSendSize(IceInternal.Buffer buf, int messageSizeMax) { - if(stream.size() > messageSizeMax) + if(buf.size() > messageSizeMax) { throw new Ice.MemoryLimitException(); } @@ -376,7 +393,7 @@ final class TransceiverI implements IceInternal.Transceiver // Only for use by ConnectorI, AcceptorI. // TransceiverI(Instance instance, javax.net.ssl.SSLEngine engine, java.nio.channels.SocketChannel fd, - String host, boolean incoming, String adapterName) + String host, boolean connected, boolean incoming, String adapterName) { _instance = instance; _engine = engine; @@ -384,6 +401,7 @@ final class TransceiverI implements IceInternal.Transceiver _host = host; _adapterName = adapterName; _incoming = incoming; + _state = connected ? StateConnected : StateNeedConnect; _logger = instance.communicator().getLogger(); try { @@ -409,11 +427,9 @@ final class TransceiverI implements IceInternal.Transceiver } } - // TODO: Buffer cache? _appInput = ByteBuffer.allocateDirect(engine.getSession().getApplicationBufferSize() * 2); _netInput = ByteBuffer.allocateDirect(engine.getSession().getPacketBufferSize() * 2); _netOutput = ByteBuffer.allocateDirect(engine.getSession().getPacketBufferSize() * 2); - _handshakeComplete = false; } protected void @@ -425,185 +441,13 @@ final class TransceiverI implements IceInternal.Transceiver super.finalize(); } - private void - flush(int timeout) - { - _netOutput.flip(); - - int size = _netOutput.limit(); - int packetSize = 0; - if(_maxPacketSize > 0 && size > _maxPacketSize) - { - packetSize = _maxPacketSize; - _netOutput.limit(_netOutput.position() + packetSize); - } - - while(_netOutput.hasRemaining()) - { - try - { - assert(_fd != null); - int ret = _fd.write(_netOutput); - - if(ret == -1) - { - throw new Ice.ConnectionLostException(); - } - - if(ret == 0) - { - if(timeout == 0) - { - throw new Ice.TimeoutException(); - } - - if(_writeSelector == null) - { - _writeSelector = java.nio.channels.Selector.open(); - _fd.register(_writeSelector, java.nio.channels.SelectionKey.OP_WRITE, null); - } - - try - { - if(timeout > 0) - { - long start = System.currentTimeMillis(); - int n = _writeSelector.select(timeout); - if(n == 0 && System.currentTimeMillis() >= start + timeout) - { - throw new Ice.TimeoutException(); - } - } - else - { - _writeSelector.select(); - } - } - catch(java.io.InterruptedIOException ex) - { - // Ignore. - } - - continue; - } - } - catch(java.io.InterruptedIOException ex) - { - continue; - } - catch(java.io.IOException ex) - { - if(IceInternal.Network.connectionLost(ex)) - { - Ice.ConnectionLostException se = new Ice.ConnectionLostException(); - se.initCause(ex); - throw se; - } - - Ice.SocketException se = new Ice.SocketException(); - se.initCause(ex); - throw se; - } - - if(packetSize > 0) - { - assert(_netOutput.position() == _netOutput.limit()); - int position = _netOutput.position(); - if(size - position > packetSize) - { - _netOutput.limit(position + packetSize); - } - else - { - packetSize = 0; - _netOutput.limit(size); - } - } - } - _netOutput.clear(); - } - - private void - read(int timeout) - { - while(true) - { - try - { - assert(_fd != null); - int ret = _fd.read(_netInput); - - if(ret == -1) - { - throw new Ice.ConnectionLostException(); - } - - if(ret == 0) - { - if(timeout == 0) - { - throw new Ice.TimeoutException(); - } - - if(_readSelector == null) - { - _readSelector = java.nio.channels.Selector.open(); - _fd.register(_readSelector, java.nio.channels.SelectionKey.OP_READ, null); - } - - try - { - if(timeout > 0) - { - long start = System.currentTimeMillis(); - int n = _readSelector.select(timeout); - if(n == 0 && System.currentTimeMillis() >= start + timeout) - { - throw new Ice.TimeoutException(); - } - } - else - { - _readSelector.select(); - } - } - catch(java.io.InterruptedIOException ex) - { - // Ignore. - } - - continue; - } - - break; - } - catch(java.io.InterruptedIOException ex) - { - continue; - } - catch(java.io.IOException ex) - { - if(IceInternal.Network.connectionLost(ex)) - { - Ice.ConnectionLostException se = new Ice.ConnectionLostException(); - se.initCause(ex); - throw se; - } - - Ice.SocketException se = new Ice.SocketException(); - se.initCause(ex); - throw se; - } - } - } - - private void - handshake(int timeout) + private IceInternal.SocketStatus + handshakeNonBlocking() { try { HandshakeStatus status = _engine.getHandshakeStatus(); - while(!_handshakeComplete) + while(_state != StateHandshakeComplete) { SSLEngineResult result = null; switch(status) @@ -613,7 +457,6 @@ final class TransceiverI implements IceInternal.Transceiver break; case NEED_TASK: { - // TODO: Use separate threads & join with timeout? Runnable task; while((task = _engine.getDelegatedTask()) != null) { @@ -642,9 +485,15 @@ final class TransceiverI implements IceInternal.Transceiver assert(false); break; case BUFFER_UNDERFLOW: + { assert(status == javax.net.ssl.SSLEngineResult.HandshakeStatus.NEED_UNWRAP); - read(timeout); + IceInternal.SocketStatus ss = readNonBlocking(); + if(ss != IceInternal.SocketStatus.Finished) + { + return ss; + } break; + } case CLOSED: throw new Ice.ConnectionLostException(); case OK: @@ -660,7 +509,11 @@ final class TransceiverI implements IceInternal.Transceiver result = _engine.wrap(_emptyBuffer, _netOutput); if(result.bytesProduced() > 0) { - flush(timeout); + IceInternal.SocketStatus ss = flushNonBlocking(); + if(ss != IceInternal.SocketStatus.Finished) + { + return ss; + } } // // FINISHED is only returned from wrap or unwrap, not from engine.getHandshakeResult(). @@ -699,6 +552,314 @@ final class TransceiverI implements IceInternal.Transceiver e.initCause(ex); throw e; } + + return IceInternal.SocketStatus.Finished; + } + + private void + handshakeCompleted() + { + _state = StateHandshakeComplete; + + // + // IceSSL.VerifyPeer is translated into the proper SSLEngine configuration + // for a server, but we have to do it ourselves for a client. + // + if(!_incoming) + { + int verifyPeer = + _instance.communicator().getProperties().getPropertyAsIntWithDefault("IceSSL.VerifyPeer", 2); + if(verifyPeer > 0) + { + try + { + _engine.getSession().getPeerCertificates(); + } + catch(javax.net.ssl.SSLPeerUnverifiedException ex) + { + Ice.SecurityException e = new Ice.SecurityException(); + e.reason = "IceSSL: server did not supply a certificate"; + e.initCause(ex); + throw e; + } + } + } + + // + // Additional verification. + // + _info = Util.populateConnectionInfo(_engine.getSession(), _fd.socket(), _adapterName, _incoming); + _instance.verifyPeer(_info, _fd, _host, _incoming); + + if(_instance.networkTraceLevel() >= 1) + { + String s; + if(_incoming) + { + s = "accepted ssl connection\n" + _desc; + } + else + { + s = "ssl connection established\n" + _desc; + } + _logger.trace(_instance.networkTraceCategory(), s); + } + + if(_instance.securityTraceLevel() >= 1) + { + _instance.traceConnection(_fd, _engine, _incoming); + } + } + + private void + shutdown() + { + // + // Send the close_notify message. + // + _engine.closeOutbound(); + try + { + _netOutput.clear(); + while(!_engine.isOutboundDone()) + { + _engine.wrap(_emptyBuffer, _netOutput); + try + { + // + // We can't block to send the close_notify message as this is called from + // shutdownWrite and shutdownReadWrite which aren't suppose to block. In + // some cases, the close_notify message might therefore not be receieved + // by the peer. This is not a big issue since the Ice protocol isn't + // subject to truncation attacks. + // +// IceInternal.SocketStatus status; +// do +// { +// status = flushNonBlocking(); +// if(status != IceInternal.SocketStatus.Finished) +// { +// handleSocketStatus(status, -1); // TODO: Is waiting indefinitely really correct? +// } +// } +// while(status != IceInternal.SocketStatus.Finished); + flushNonBlocking(); + } + catch(Ice.ConnectionLostException ex) + { + // Ignore. + } + } + } + catch(SSLException ex) + { + Ice.SecurityException se = new Ice.SecurityException(); + se.reason = "IceSSL: SSL failure while shutting down socket"; + se.initCause(ex); + throw se; + } + } + + private IceInternal.SocketStatus + writeNonBlocking(ByteBuffer buf) + { + // + // This method has two purposes: encrypt the application's message buffer into our + // _netOutput buffer, and write the contents of _netOutput to the socket without + // blocking. + // + try + { + while(buf.hasRemaining() || _netOutput.position() > 0) + { + final int rem = buf.remaining(); + + if(rem > 0) + { + // + // Encrypt the buffer. + // + SSLEngineResult result = _engine.wrap(buf, _netOutput); + switch(result.getStatus()) + { + case BUFFER_OVERFLOW: + // + // Need to make room in _netOutput. + // + break; + case BUFFER_UNDERFLOW: + assert(false); + break; + case CLOSED: + throw new Ice.ConnectionLostException(); + case OK: + break; + } + + // + // If the SSL engine consumed any of the application's message buffer, + // then log it. + // + if(result.bytesConsumed() > 0) + { + if(_instance.networkTraceLevel() >= 3) + { + String s = "sent " + result.bytesConsumed() + " of " + rem + " bytes via ssl\n" + + toString(); + _logger.trace(_instance.networkTraceCategory(), s); + } + + if(_stats != null) + { + _stats.bytesSent(type(), result.bytesConsumed()); + } + } + } + + // + // Write the encrypted data to the socket. We continue writing until we've written + // all of _netOutput, or until flushNonBlocking indicates that it cannot write + // (i.e., by returning NeedWrite). + // + if(_netOutput.position() > 0) + { + IceInternal.SocketStatus ss = flushNonBlocking(); + if(ss != IceInternal.SocketStatus.Finished) + { + assert(ss == IceInternal.SocketStatus.NeedWrite); + return ss; + } + } + } + } + catch(SSLException ex) + { + Ice.SecurityException e = new Ice.SecurityException(); + e.reason = "IceSSL: error while encoding message"; + e.initCause(ex); + throw e; + } + + assert(_netOutput.position() == 0); + return IceInternal.SocketStatus.Finished; + } + + private IceInternal.SocketStatus + flushNonBlocking() + { + _netOutput.flip(); + + final int size = _netOutput.limit(); + int packetSize = size - _netOutput.position(); + if(_maxPacketSize > 0 && packetSize > _maxPacketSize) + { + packetSize = _maxPacketSize; + _netOutput.limit(_netOutput.position() + packetSize); + } + + IceInternal.SocketStatus status = IceInternal.SocketStatus.Finished; + while(_netOutput.hasRemaining()) + { + try + { + assert(_fd != null); + int ret = _fd.write(_netOutput); + + if(ret == -1) + { + throw new Ice.ConnectionLostException(); + } + else if(ret == 0) + { + status = IceInternal.SocketStatus.NeedWrite; + break; + } + + if(packetSize == _maxPacketSize) + { + assert(_netOutput.position() == _netOutput.limit()); + packetSize = size - _netOutput.position(); + if(packetSize > _maxPacketSize) + { + packetSize = _maxPacketSize; + } + _netOutput.limit(_netOutput.position() + packetSize); + } + } + catch(java.io.InterruptedIOException ex) + { + continue; + } + catch(java.io.IOException ex) + { + if(IceInternal.Network.connectionLost(ex)) + { + Ice.ConnectionLostException se = new Ice.ConnectionLostException(); + se.initCause(ex); + throw se; + } + + Ice.SocketException se = new Ice.SocketException(); + se.initCause(ex); + throw se; + } + } + + if(status == IceInternal.SocketStatus.Finished) + { + _netOutput.clear(); + } + else + { + _netOutput.limit(size); + _netOutput.compact(); + } + + return status; + } + + + private IceInternal.SocketStatus + readNonBlocking() + { + while(true) + { + try + { + assert(_fd != null); + int ret = _fd.read(_netInput); + + if(ret == -1) + { + throw new Ice.ConnectionLostException(); + } + else if(ret == 0) + { + return IceInternal.SocketStatus.NeedRead; + } + + break; + } + catch(java.io.InterruptedIOException ex) + { + continue; + } + catch(java.io.IOException ex) + { + if(IceInternal.Network.connectionLost(ex)) + { + Ice.ConnectionLostException se = new Ice.ConnectionLostException(); + se.initCause(ex); + throw se; + } + + Ice.SocketException se = new Ice.SocketException(); + se.initCause(ex); + throw se; + } + } + + return IceInternal.SocketStatus.Finished; } private void @@ -747,89 +908,70 @@ final class TransceiverI implements IceInternal.Transceiver } private void - shutdown() + handleSocketStatus(IceInternal.SocketStatus status, int timeout) { - // - // Send the close_notify message. - // - _engine.closeOutbound(); + assert(timeout != 0); try { - _netOutput.clear(); - while(!_engine.isOutboundDone()) + java.nio.channels.Selector selector; + if(status == IceInternal.SocketStatus.NeedRead) { - _engine.wrap(_emptyBuffer, _netOutput); - try + if(_readSelector == null) { - flush(-1); + _readSelector = java.nio.channels.Selector.open(); + _fd.register(_readSelector, java.nio.channels.SelectionKey.OP_READ, null); } - catch(Ice.ConnectionLostException ex) + selector = _readSelector; + } + else + { + assert(status == IceInternal.SocketStatus.NeedWrite); + if(_writeSelector == null) { - // Ignore. + _writeSelector = java.nio.channels.Selector.open(); + _fd.register(_writeSelector, java.nio.channels.SelectionKey.OP_WRITE, null); } + selector = _writeSelector; } - } - catch(SSLException ex) - { - Ice.SecurityException se = new Ice.SecurityException(); - se.reason = "IceSSL: SSL failure while shutting down socket"; - se.initCause(ex); - throw se; - } - } - private void - handshakeCompleted() - { - _handshakeComplete = true; - - // - // IceSSL.VerifyPeer is translated into the proper SSLEngine configuration - // for a server, but we have to do it ourselves for a client. - // - if(!_incoming) - { - int verifyPeer = - _instance.communicator().getProperties().getPropertyAsIntWithDefault("IceSSL.VerifyPeer", 2); - if(verifyPeer > 0) + while(true) { try { - _engine.getSession().getPeerCertificates(); + if(timeout > 0) + { + long start = System.currentTimeMillis(); + int n = selector.select(timeout); + if(n == 0 && System.currentTimeMillis() >= start + timeout) + { + throw new Ice.TimeoutException(); + } + } + else + { + selector.select(); + } + + break; } - catch(javax.net.ssl.SSLPeerUnverifiedException ex) + catch(java.io.InterruptedIOException ex) { - Ice.SecurityException e = new Ice.SecurityException(); - e.reason = "IceSSL: server did not supply a certificate"; - e.initCause(ex); - throw e; + // Ignore. } } } - - // - // Additional verification. - // - _info = Util.populateConnectionInfo(_engine.getSession(), _fd.socket(), _adapterName, _incoming); - _instance.verifyPeer(_info, _fd, _host, _incoming); - - if(_instance.networkTraceLevel() >= 1) + catch(java.io.IOException ex) { - String s; - if(_incoming) - { - s = "accepted ssl connection\n" + IceInternal.Network.fdToString(_fd); - } - else + if(IceInternal.Network.connectionLost(ex)) { - s = "ssl connection established\n" + IceInternal.Network.fdToString(_fd); + Ice.ConnectionLostException se = new Ice.ConnectionLostException(); + se.initCause(ex); + throw se; } - _logger.trace(_instance.networkTraceCategory(), s); - } - if(_instance.securityTraceLevel() >= 1) - { - _instance.traceConnection(_fd, _engine, _incoming); + Ice.SocketException se = new Ice.SocketException(); + se.initCause(ex); + throw se; } } @@ -839,6 +981,7 @@ final class TransceiverI implements IceInternal.Transceiver private String _host; private String _adapterName; private boolean _incoming; + private int _state; private Ice.Logger _logger; private Ice.Stats _stats; private String _desc; @@ -847,8 +990,12 @@ final class TransceiverI implements IceInternal.Transceiver private ByteBuffer _netInput; // Holds encrypted data read from the socket. private ByteBuffer _netOutput; // Holds encrypted data to be written to the socket. private static ByteBuffer _emptyBuffer = ByteBuffer.allocate(0); // Used during handshaking. - private boolean _handshakeComplete; private java.nio.channels.Selector _readSelector; private java.nio.channels.Selector _writeSelector; private ConnectionInfo _info; + + private static final int StateNeedConnect = 0; + private static final int StateConnectPending = 1; + private static final int StateConnected = 2; + private static final int StateHandshakeComplete = 3; } |