// ********************************************************************** // // 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 final class Outgoing implements OutgoingMessageCallback { public Outgoing(RequestHandler handler, String operation, Ice.OperationMode mode, java.util.Map<String, String> context) throws LocalExceptionWrapper { _state = StateUnsent; _sent = false; _handler = handler; Instance instance = _handler.getReference().getInstance(); _is = new BasicStream(instance); _os = new BasicStream(instance); writeHeader(operation, mode, context); } // // These functions allow this object to be reused, rather than reallocated. // public void reset(RequestHandler handler, String operation, Ice.OperationMode mode, java.util.Map<String, String> context) throws LocalExceptionWrapper { _state = StateUnsent; _exception = null; _sent = false; _handler = handler; writeHeader(operation, mode, context); } public void reclaim() { _is.reset(); _os.reset(); } // Returns true if ok, false if user exception. public boolean invoke() throws LocalExceptionWrapper { assert(_state == StateUnsent); _os.endWriteEncaps(); switch(_handler.getReference().getMode()) { case Reference.ModeTwoway: { _state = StateInProgress; Ice.ConnectionI connection = _handler.sendRequest(this); assert(connection != null); boolean timedOut = false; synchronized(this) { // // If the request is being sent in the background we first wait for the // sent notification. // while(_state != StateFailed && !_sent) { try { wait(); } catch(java.lang.InterruptedException ex) { } } // // Wait until the request has completed, or until the request // times out. // int timeout = connection.timeout(); while(_state == StateInProgress && !timedOut) { try { if(timeout >= 0) { wait(timeout); if(_state == StateInProgress) { timedOut = true; } } else { wait(); } } catch(InterruptedException ex) { } } } if(timedOut) { // // Must be called outside the synchronization of // this object // connection.exception(new Ice.TimeoutException()); // // We must wait until the exception set above has // propagated to this Outgoing object. // synchronized(this) { while(_state == StateInProgress) { try { wait(); } catch(InterruptedException ex) { } } } } if(_exception != null) { _exception.fillInStackTrace(); // // A CloseConnectionException indicates graceful // server shutdown, and is therefore always repeatable // without violating "at-most-once". That's because by // sending a close connection message, the server // guarantees that all outstanding requests can safely // be repeated. // // An ObjectNotExistException can always be retried as // well without violating "at-most-once" (see the // implementation of the checkRetryAfterException // method of the ProxyFactory class for the reasons // why it can be useful). // 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 // potentially violating the "at-most-once" principle. // throw new LocalExceptionWrapper(_exception, false); } if(_state == StateUserException) { return false; } else { assert(_state == StateOK); return true; } } case Reference.ModeOneway: case Reference.ModeDatagram: { _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; } case Reference.ModeBatchOneway: case Reference.ModeBatchDatagram: { // // For batch oneways and datagrams, the same rules as for // regular oneways and datagrams (see comment above) // apply. // _state = StateInProgress; _handler.finishBatchRequest(_os); return true; } } assert(false); return false; } public void abort(Ice.LocalException ex) throws LocalExceptionWrapper { assert(_state == StateUnsent); // // If we didn't finish a batch oneway or datagram request, we // must notify the connection about that we give up ownership // of the batch stream. // int mode = _handler.getReference().getMode(); if(mode == Reference.ModeBatchOneway || mode == Reference.ModeBatchDatagram) { _handler.abortBatchRequest(); } throw ex; } public void sent(boolean async) { if(async) { 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(_handler.getReference().getMode() == Reference.ModeTwoway); // Only for twoways. assert(_state <= StateInProgress); _is.swap(is); byte replyStatus = _is.readByte(); switch(replyStatus) { case ReplyStatus.replyOK: { _state = StateOK; // The state must be set last, in case there is an exception. break; } case ReplyStatus.replyUserException: { _state = StateUserException; // The state must be set last, in case there is an exception. break; } case ReplyStatus.replyObjectNotExist: case ReplyStatus.replyFacetNotExist: case ReplyStatus.replyOperationNotExist: { Ice.RequestFailedException ex = null; switch(replyStatus) { case ReplyStatus.replyObjectNotExist: { ex = new Ice.ObjectNotExistException(); break; } case ReplyStatus.replyFacetNotExist: { ex = new Ice.FacetNotExistException(); break; } case ReplyStatus.replyOperationNotExist: { ex = new Ice.OperationNotExistException(); break; } default: { assert(false); break; } } ex.id = new Ice.Identity(); ex.id.__read(_is); // // For compatibility with the old FacetPath. // String[] facetPath = _is.readStringSeq(); if(facetPath.length > 0) { if(facetPath.length > 1) { throw new Ice.MarshalException(); } ex.facet = facetPath[0]; } else { ex.facet = ""; } ex.operation = _is.readString(); _exception = ex; _state = StateLocalException; // The state must be set last, in case there is an exception. break; } case ReplyStatus.replyUnknownException: case ReplyStatus.replyUnknownLocalException: case ReplyStatus.replyUnknownUserException: { Ice.UnknownException ex = null; switch(replyStatus) { case ReplyStatus.replyUnknownException: { ex = new Ice.UnknownException(); break; } case ReplyStatus.replyUnknownLocalException: { ex = new Ice.UnknownLocalException(); break; } case ReplyStatus.replyUnknownUserException: { ex = new Ice.UnknownUserException(); break; } default: { assert(false); break; } } ex.unknown = _is.readString(); _exception = ex; _state = StateLocalException; // The state must be set last, in case there is an exception. break; } default: { _exception = new Ice.UnknownReplyStatusException(); _state = StateLocalException; break; } } notify(); } public synchronized void finished(Ice.LocalException ex, boolean sent) { assert(_state <= StateInProgress); _state = StateFailed; _exception = ex; _sent = sent; notify(); } public BasicStream is() { return _is; } public BasicStream os() { return _os; } public void throwUserException() throws Ice.UserException { try { _is.startReadEncaps(); _is.throwException(); } catch(Ice.UserException ex) { _is.endReadEncaps(); throw ex; } } private void writeHeader(String operation, Ice.OperationMode mode, java.util.Map<String, String> context) throws LocalExceptionWrapper { switch(_handler.getReference().getMode()) { case Reference.ModeTwoway: case Reference.ModeOneway: case Reference.ModeDatagram: { _os.writeBlob(IceInternal.Protocol.requestHdr); break; } case Reference.ModeBatchOneway: case Reference.ModeBatchDatagram: { _handler.prepareBatchRequest(_os); break; } } try { _handler.getReference().getIdentity().__write(_os); // // For compatibility with the old FacetPath. // String facet = _handler.getReference().getFacet(); if(facet == null || facet.length() == 0) { _os.writeStringSeq(null); } else { String[] facetPath = { facet }; _os.writeStringSeq(facetPath); } _os.writeString(operation); _os.writeByte((byte)mode.ordinal()); if(context != null) { // // Explicit context // Ice.ContextHelper.write(_os, context); } else { // // Implicit context // Ice.ImplicitContextI implicitContext = _handler.getReference().getInstance().getImplicitContext(); java.util.Map<String, String> prxContext = _handler.getReference().getContext(); if(implicitContext == null) { Ice.ContextHelper.write(_os, prxContext); } else { implicitContext.write(prxContext, _os); } } // // Input and output parameters are always sent in an // encapsulation, which makes it possible to forward requests as // blobs. // _os.startWriteEncaps(); } catch(Ice.LocalException ex) { abort(ex); } } private RequestHandler _handler; private BasicStream _is; private BasicStream _os; private boolean _sent; private Ice.LocalException _exception; private static final int StateUnsent = 0; private static final int StateInProgress = 1; 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; public Outgoing next; // For use by Ice._ObjectDelM }