// **********************************************************************
//
// Copyright (c) 2003-2010 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 RequestHandler
connect()
{
_reference.getConnection(this);
synchronized(this)
{
if(initialized())
{
assert(_connection != null);
return new ConnectionRequestHandler(_reference, _connection, _compress);
}
else
{
_updateRequestHandler = true; // The proxy request handler will be updated when the connection is set.
return this;
}
}
}
public void
prepareBatchRequest(BasicStream os)
throws LocalExceptionWrapper
{
synchronized(this)
{
while(_batchRequestInProgress)
{
try
{
wait();
}
catch(java.lang.InterruptedException ex)
{
}
}
if(!initialized())
{
_batchRequestInProgress = true;
_batchStream.swap(os);
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())
{
Ex.throwMemoryLimitException(_batchStream.size() + _batchRequestsSize,
_reference.getInstance().messageSizeMax());
}
_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
{
if(!getConnection(true).sendRequest(out, _compress, _response) || _response)
{
return _connection; // The request has been sent or we're expecting a response.
}
else
{
return null; // The request hasn't been sent yet.
}
}
public int
sendAsyncRequest(OutgoingAsync out)
throws LocalExceptionWrapper
{
synchronized(this)
{
if(!initialized())
{
_requests.add(new Request(out));
return AsyncStatus.Queued;
}
}
return _connection.sendAsyncRequest(out, _compress, _response);
}
public boolean
flushBatchRequests(BatchOutgoing out)
{
return getConnection(true).flushBatchRequests(out);
}
public int
flushAsyncBatchRequests(BatchOutgoingAsync out)
{
synchronized(this)
{
if(!initialized())
{
_requests.add(new Request(out));
return AsyncStatus.Queued;
}
}
return _connection.flushAsyncBatchRequests(out);
}
public Outgoing
getOutgoing(String operation, Ice.OperationMode mode, java.util.Map<String, String> 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 waitInit)
{
if(waitInit)
{
//
// Wait for the connection establishment to complete or fail.
//
while(!_initialized && _exception == null)
{
try
{
wait();
}
catch(java.lang.InterruptedException ex)
{
}
}
}
if(_exception != null)
{
throw (Ice.LocalException)_exception.fillInStackTrace();
}
else
{
assert(!waitInit || _initialized);
return _connection;
}
}
//
// Implementation of Reference.GetConnectionCallback
//
public void
setConnection(Ice.ConnectionI connection, boolean compress)
{
synchronized(this)
{
assert(_exception == null && _connection == null);
assert(_updateRequestHandler || _requests.isEmpty());
_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 && !ri.addProxy(_proxy, this))
{
return; // The request handler will be initialized once addProxy returns.
}
//
// We can now send the queued requests.
//
flushRequests();
}
public synchronized void
setException(final Ice.LocalException ex)
{
assert(!_initialized && _exception == null);
assert(_updateRequestHandler || _requests.isEmpty());
_exception = ex;
_proxy = null; // Break cyclic reference count.
_delegate = null; // Break cyclic reference count.
//
// If some requests were queued, we notify them of the failure. This is done from a thread
// from the client thread pool since this will result in ice_exception callbacks to be
// called.
//
if(!_requests.isEmpty())
{
_reference.getInstance().clientThreadPool().execute(new DispatchWorkItem(_reference.getInstance())
{
public void
run()
{
flushRequestsWithException(ex);
};
});
}
notifyAll();
}
//
// Implementation of RouterInfo.AddProxyCallback
//
public void
addedProxy()
{
//
// The proxy was added to the router info, we're now ready to send the
// queued requests.
//
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;
_initialized = false;
_flushing = false;
_batchRequestInProgress = false;
_batchRequestsSize = Protocol.requestBatchHdr.length;
_batchStream = new BasicStream(ref.getInstance(), _batchAutoFlush);
_updateRequestHandler = false;
}
private boolean
initialized()
{
// Must be called with the mutex locked.
if(_initialized)
{
assert(_connection != null);
return true;
}
else
{
while(_flushing && _exception == null)
{
try
{
wait();
}
catch(java.lang.InterruptedException ex)
{
}
}
if(_exception != null)
{
throw (Ice.LocalException)_exception.fillInStackTrace();
}
else
{
return _initialized;
}
}
}
private void
flushRequests()
{
synchronized(this)
{
assert(_connection != null && !_initialized);
while(_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;
}
final java.util.List<OutgoingAsyncMessageCallback> sentCallbacks =
new java.util.ArrayList<OutgoingAsyncMessageCallback>();
try
{
java.util.Iterator<Request> p = _requests.iterator(); // _requests is immutable when _flushing = true
while(p.hasNext())
{
Request request = p.next();
if(request.out != null)
{
if((_connection.sendAsyncRequest(request.out, _compress, _response) &
AsyncStatus.InvokeSentCallback) > 0)
{
sentCallbacks.add(request.out);
}
}
else if(request.batchOut != null)
{
if((_connection.flushAsyncBatchRequests(request.batchOut) &
AsyncStatus.InvokeSentCallback) > 0)
{
sentCallbacks.add(request.batchOut);
}
}
else
{
BasicStream os = new BasicStream(request.os.instance());
_connection.prepareBatchRequest(os);
try
{
request.os.pos(0);
os.writeBlob(request.os.readBlob(request.os.size()));
}
catch(Ice.LocalException ex)
{
_connection.abortBatchRequest();
throw ex;
}
_connection.finishBatchRequest(os, _compress);
}
p.remove();
}
}
catch(final LocalExceptionWrapper ex)
{
synchronized(this)
{
assert(_exception == null && !_requests.isEmpty());
_exception = ex.get();
_reference.getInstance().clientThreadPool().execute(new DispatchWorkItem(_reference.getInstance())
{
public void
run()
{
flushRequestsWithException(ex);
};
});
}
}
catch(final Ice.LocalException ex)
{
synchronized(this)
{
assert(_exception == null && !_requests.isEmpty());
_exception = ex;
_reference.getInstance().clientThreadPool().execute(new DispatchWorkItem(_reference.getInstance())
{
public void
run()
{
flushRequestsWithException(ex);
};
});
}
}
if(!sentCallbacks.isEmpty())
{
final Instance instance = _reference.getInstance();
instance.clientThreadPool().execute(new DispatchWorkItem(instance)
{
public void
run()
{
for(OutgoingAsyncMessageCallback callback : sentCallbacks)
{
callback.__sent();
}
};
});
}
//
// We've finished sending the queued requests and the request handler now send
// the requests over the connection directly. It's time to substitute the
// request handler of the proxy with the more efficient connection request
// handler which does not have any synchronization. This also breaks the cyclic
// reference count with the proxy.
//
// NOTE: _updateRequestHandler is immutable once _flushing = true
//
if(_updateRequestHandler && _exception == null)
{
_proxy.__setRequestHandler(_delegate, new ConnectionRequestHandler(_reference, _connection, _compress));
}
synchronized(this)
{
assert(!_initialized);
if(_exception == null)
{
_initialized = true;
_flushing = false;
}
_proxy = null; // Break cyclic reference count.
_delegate = null; // Break cyclic reference count.
notifyAll();
}
}
void
flushRequestsWithException(Ice.LocalException ex)
{
for(Request request : _requests)
{
if(request.out != null)
{
request.out.__finished(ex, false);
}
else if(request.batchOut != null)
{
request.batchOut.__finished(ex, false);
}
}
_requests.clear();
}
void
flushRequestsWithException(LocalExceptionWrapper ex)
{
for(Request request : _requests)
{
if(request.out != null)
{
request.out.__finished(ex);
}
else if(request.batchOut != null)
{
request.batchOut.__finished(ex.get(), false);
}
}
_requests.clear();
}
private final Reference _reference;
private boolean _response;
private Ice.ObjectPrxHelperBase _proxy;
private Ice._ObjectDelM _delegate;
private final boolean _batchAutoFlush;
private Ice.ConnectionI _connection;
private boolean _compress;
private Ice.LocalException _exception;
private boolean _initialized;
private boolean _flushing;
private java.util.List<Request> _requests = new java.util.LinkedList<Request>();
private boolean _batchRequestInProgress;
private int _batchRequestsSize;
private BasicStream _batchStream;
private boolean _updateRequestHandler;
}