/* * JacORB - a free Java ORB * * Copyright (C) 1997-2014 Gerald Brose / The JacORB Team. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the Free * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ package org.jacorb.orb.giop; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.ReentrantLock; import org.jacorb.config.Configuration; import org.jacorb.config.ConfigurationException; import org.jacorb.orb.CodeSet; import org.jacorb.orb.IBufferManager; import org.jacorb.orb.ORB; import org.jacorb.orb.SystemExceptionHelper; import org.jacorb.orb.etf.StreamConnectionBase; import org.jacorb.util.ObjectUtil; import org.jacorb.util.Time; import org.jacorb.util.TimerQueue; import org.jacorb.util.TimerQueueAction; import org.omg.CORBA.CompletionStatus; import org.omg.CORBA.NO_IMPLEMENT; import org.omg.CORBA.NO_MEMORY; import org.omg.CORBA.TIMEOUT; import org.omg.ETF.BufferHolder; import org.omg.GIOP.MsgType_1_1; import org.omg.GIOP.ReplyStatusType_1_2; import org.slf4j.Logger; /** * GIOPConnection.java * * Created: Sun Aug 12 21:30:48 2002 * * Configuration parameters:<br> * * jacorb.debug.dump_incoming_messages=[on|off], default=off<br> * jacorb.connection.client.connect_timeout=N, default=0<br> * jacorb.connection.statistics_providers={classnames}, default=(empty)<br> * * @author Nicolas Noffke */ public abstract class GIOPConnection extends java.io.OutputStream { /** * Profile describing the remote endpoint of this connection. */ protected final org.omg.ETF.Profile profile; protected org.omg.ETF.Connection transport = null; private Long write_monitor_timeout = null; private TimerQueue timer_queue = null; private RequestListener request_listener = null; private ReplyListener reply_listener = null; protected ConnectionListener connection_listener = null; protected Object connect_sync = new Object(); private ReentrantLock writeLock = new ReentrantLock (); // private boolean writer_active = false; // private final Object write_sync = new Object(); protected Logger logger; /* * Connection OSF character formats. */ private CodeSet tcs; private CodeSet tcsw; private boolean tcs_negotiated = false; /** * Fragmented message support. At any time, fragmentsSize contains total size of processed fragments. */ protected final Map<Integer, ByteArrayOutputStream> fragments = new HashMap<Integer, ByteArrayOutputStream>(); protected final Map<Integer, Integer> fragmentsSize = new HashMap<Integer, Integer>(); private IBufferManager buf_mg; private boolean dump_incoming = false; private long connectTimeout = 0; private final BufferHolder msg_header = new BufferHolder (new byte[Messages.MSG_HEADER_SIZE]); private final BufferHolder inbuf = new BufferHolder(); //// support for SAS Stateful contexts //private Hashtable sasContexts = null; // provide cubbyholes for other layers to store connection persistent data private static int cubby_count = 0; private Object[] cubbyholes = null; // the no. of outstanding messages (requests/replies) // pending_messages refers only to expected replies, to be sent // in response to two-way requests. pending_write refers to messages // that are outbound but have not yet been sent. These could be one-way // or two-way requests, or they could be replies being sent out of a // server. There will typicially be only one pending write. private int pending_messages = 0; private int pending_write = 0; protected boolean discard_messages = false; //used to lock the section where we got a message, but it isn't //yet decided, if this might need a reply, i.e. set the transport //busy protected Object pendingUndecidedSync = new Object(); //stop listening for messages protected boolean do_close = false; protected StatisticsProvider statistics_provider = null; protected StatisticsProviderAdapter statistics_provider_adapter = null; protected ORB orb; // deadline for current send operation private org.omg.TimeBase.UtcT sendDeadline = null; public class ConnectionReset extends TimerQueueAction { long relative; public ConnectionReset (long ms) { super(ms); relative = ms; } @Override public void expire () { if (logger.isErrorEnabled()) { logger.error("Write to connection exceeded time limit. " + "Transport forced closed."); } do_close = true; transport.close(); } } public GIOPConnection( org.omg.ETF.Profile profile, org.omg.ETF.Connection transport, RequestListener request_listener, ReplyListener reply_listener, StatisticsProvider statistics_provider ) { super(); this.profile = profile; this.transport = transport; this.request_listener = request_listener; this.reply_listener = reply_listener; this.statistics_provider = statistics_provider; if (statistics_provider != null) this.statistics_provider_adapter = new StatisticsProviderAdapter (statistics_provider); this.cubbyholes = new Object[cubby_count]; } public void configure(Configuration configuration) throws ConfigurationException { this.orb = configuration.getORB(); tcs = orb.getTCSDefault(); tcsw = orb.getTCSWDefault(); buf_mg = orb.getBufferManager(); logger = configuration.getLogger("org.jacorb.giop.conn"); dump_incoming = configuration.getAttributeAsBoolean("jacorb.debug.dump_incoming_messages", false); connectTimeout = configuration.getAttributeAsInteger("jacorb.connection.client.connect_timeout", 90000); List<String> statsProviderClassNames = configuration.getAttributeList( "jacorb.connection.statistics_providers"); for (Iterator<String> iter = statsProviderClassNames.iterator (); iter.hasNext ();) { String className = iter.next (); try { Class<?> iclass = ObjectUtil.classForName (className); this.statistics_provider_adapter = new StatisticsProviderAdapter ((StatisticsProvider)iclass.newInstance(), statistics_provider_adapter); } catch ( Exception e ) { if (logger.isErrorEnabled()) { logger.error ( "Unable to create class from property " + ">jacorb.connection.statistics_provider_class<: " + e.toString() ); } } } } protected void init_write_monitor(int timeout) { if (timeout <= 0) return; timer_queue = orb.getTimerQueue(); write_monitor_timeout = new Long (timeout); } public final void setCodeSets( CodeSet TCS, CodeSet TCSW ) { tcs = TCS; tcsw = TCSW; } public final void setCodeSets( int TCS, int TCSW ) { tcs = CodeSet.getCodeSet( TCS ); tcsw = CodeSet.getCodeSet( TCSW ); } public final CodeSet getTCS() { return tcs; } public final CodeSet getTCSW() { return tcsw; } public final void markTCSNegotiated() { tcs_negotiated = true; } public final boolean isTCSNegotiated() { return tcs_negotiated; } /** * Get the value of request_listener. * @return value of request_listener. */ protected final synchronized RequestListener getRequestListener() { return request_listener; } /** * Set the value of request_listener. * @param listener Value to assign to request_listener. */ public final synchronized void setRequestListener( RequestListener listener) { this.request_listener = listener; } /** * Get the value of reply_listener. * @return value of reply_listener. */ private final synchronized ReplyListener getReplyListener() { return reply_listener; } /** * Set the value of reply_listener. * @param listener Value to assign to reply_listener. */ public final synchronized void setReplyListener( ReplyListener listener) { this.reply_listener = listener; } public final void setConnectionListener( ConnectionListener connection_listener ) { this.connection_listener = connection_listener; } public final org.omg.ETF.Connection getTransport() { synchronized (connect_sync) { return transport; } } private boolean waitUntilConnected() { synchronized (connect_sync) { while (!transport.is_connected() && !do_close) { if (logger.isDebugEnabled()) { logger.debug (this.toString() + ": will wait until connected"); } try { connect_sync.wait(); } catch ( InterruptedException ie ) { } } return !do_close; } } /** * Called by this.getMessage() to signal that the attempt to * read a message resulted in a timeout. This method is implemented * differently on the client and server side. */ protected abstract void readTimedOut(); /** * Called by this.getMessage() to signal that the underlying transport * was closed while attempting to read a message. This method is * implemented differently on the client and server side. */ protected abstract void streamClosed(); /** * Read a GIOP message from the stream. This will first try to * read in the fixed-length GIOP message header to determine the * message size, and the read the rest. It also checks the leading * four magic bytes of the message header. This method <b>is not * thread safe<b> and only expected to be called by a single * thread. * * @return a GIOP message or null. */ private byte[] getMessage() { //Wait until the actual socket connection is established. This //is necessary for the client side, so opening up a new //connection can be delayed until the first message is to be //sent. if ( ! waitUntilConnected() ) { return null; } try { transport.read (msg_header, 0, Messages.MSG_HEADER_SIZE, Messages.MSG_HEADER_SIZE, 0); } catch (org.omg.CORBA.TRANSIENT ex) { return null; } catch (org.omg.CORBA.COMM_FAILURE ex) { if (logger.isDebugEnabled()) { logger.debug(this.toString() + ": getMessage() -- COMM_FAILURE"); } this.streamClosed(); return null; } catch (org.omg.CORBA.TIMEOUT ex) { if (logger.isDebugEnabled()) { logger.debug(this.toString() + ": getMessage() -- TIMEOUT"); } this.readTimedOut(); return null; } final byte[] header = msg_header.value; //(minimally) decode GIOP message header. Main checks should //be done one layer above. if (Messages.matchGIOPMagic(header)) { //determine message size int msg_size = Messages.getMsgSize( header ); if ( msg_size < 0 ) { if (logger.isErrorEnabled()) { logger.error ( "Negative GIOP message size (" + msg_size + ") in " + this.toString() ); } if (logger.isDebugEnabled()) { logger.debug ( "GIOPConnection.getMessage() with header: \n" + new String(header) + "\nsize : " + Messages.MSG_HEADER_SIZE + ", in " + this.toString() ); } return null; } //get a large enough buffer from the pool inbuf.value = buf_mg.getBuffer( msg_size + Messages.MSG_HEADER_SIZE ); //copy header System.arraycopy( header, 0, inbuf.value, 0, Messages.MSG_HEADER_SIZE ); try { transport.read (inbuf, Messages.MSG_HEADER_SIZE, msg_size, msg_size, 0); } catch (org.omg.CORBA.COMM_FAILURE ex) { if (logger.isErrorEnabled()) { logger.error ( "Failed to read GIOP message in " + this.toString(), ex ); } this.streamClosed(); return null; } if ( dump_incoming ) { if (logger.isInfoEnabled()) { logger.info ( this.toString() + " BufferDump:\n" + ObjectUtil.bufToString ( inbuf.value, 0, msg_size + Messages.MSG_HEADER_SIZE ) ); } } if ( getStatisticsProviderAdapter() != null ) { getStatisticsProviderAdapter().messageReceived( msg_size + Messages.MSG_HEADER_SIZE ); } if (logger.isDebugEnabled()) { logger.debug ("read GIOP message of size {} from {}", msg_size + Messages.MSG_HEADER_SIZE, this.toString()); } //this is the "good" exit point. return inbuf.value; } if (logger.isDebugEnabled()) { logger.debug(this.toString() + " getMessage(), invalid header read: " + ObjectUtil.bufToString(msg_header.value, 0, 4)); } if (logger.isErrorEnabled()) { logger.error( "Failed to read GIOP message in " + this.toString() + ", incorrect magic number --> connection closed" ); } //close transport connection, there is nearly no chance to sync with //peer on this connection again this.streamClosed(); return null; } public final void receiveMessages() throws IOException { while (!do_close) { try { receiveMessagesLoop(); } catch (Exception e) { logger.error("Unexpected error during receiveMessages. Lost a message!", e); } } } private void receiveMessagesLoop() throws IOException { try { byte[] message = getMessage(); if ( message == null ) { return; } synchronized ( pendingUndecidedSync ) { if ( discard_messages ) { buf_mg.returnBuffer( message ); return; } //check major version if ( Messages.getGIOPMajor( message ) != 1 ) { if (logger.isErrorEnabled()) { logger.error("Invalid GIOP major version encountered: " + Messages.getGIOPMajor( message ) + ", in " + this.toString() ); } buf_mg.returnBuffer( message ); return; } int msg_type = Messages.getMsgType( message ); if ( msg_type == MsgType_1_1._Fragment ) { //GIOP 1.0 messages aren't allowed to be fragmented if ( Messages.getGIOPMinor( message ) == 0 ) { if (logger.isWarnEnabled()) { logger.warn ("Received a GIOP 1.0 message of type Fragment" + " in " + this.toString()); } final MessageOutputStream out = new MessageOutputStream(orb); try { out.writeGIOPMsgHeader(MsgType_1_1._MessageError, 0); out.insertMsgSize(); sendMessage( out ); buf_mg.returnBuffer( message ); } finally { out.close(); } return; } //GIOP 1.1 Fragmented messages currently not supported if ( Messages.getGIOPMinor( message ) == 1 ) { if (logger.isWarnEnabled()) { logger.warn( "Received a GIOP 1.1 Fragment message" + " in " + this.toString()); } //Can't return a message in this case, because //GIOP 1.1 fragments don't have request //ids. Therefore, just discard. buf_mg.returnBuffer( message ); return; } //for now, only GIOP 1.2 from here on int request_id = Messages.getRequestId( message ); //sanity check if ( ! fragments.containsKey( request_id )) { if (logger.isErrorEnabled()) { logger.error( "No previous Fragment to this one in " + this.toString()); } //Drop this one and continue buf_mg.returnBuffer( message ); return; } ByteArrayOutputStream b_out = fragments.get( request_id ); //add the message contents to stream (discarding the //GIOP message header and the request id ulong of the //Fragment header) b_out.write( message, Messages.MSG_HEADER_SIZE + 4 , Messages.getMsgSize(message) - 4 ); fragmentsSize.put(request_id, fragmentsSize.get(request_id) + Messages.getMsgSize(message) - 4); if ( Messages.moreFragmentsFollow( message )) { //more to follow, so don't hand over to processing buf_mg.returnBuffer( message ); return; } buf_mg.returnBuffer( message ); //silently replace the original message buffer and type message = b_out.toByteArray(); Messages.setMsgSize(message, fragmentsSize.remove(request_id)); msg_type = Messages.getMsgType( message ); fragments.remove( request_id ); } else if ( Messages.moreFragmentsFollow( message ) ) { //GIOP 1.0 messages aren't allowed to be fragmented if ( Messages.getGIOPMinor( message ) == 0 ) { if (logger.isWarnEnabled()) { logger.warn ("Received a GIOP 1.0 message " + "with the \"more fragments follow\"" + "bits set in " + this.toString() ); } MessageOutputStream out = new MessageOutputStream( orb ); out.writeGIOPMsgHeader( MsgType_1_1._MessageError, 0 ); out.insertMsgSize(); sendMessage( out ); buf_mg.returnBuffer( message ); return; } //If GIOP 1.1, only Request and Reply messages may be fragmented if ( Messages.getGIOPMinor( message ) == 1 ) { if ( msg_type != MsgType_1_1._Request && msg_type != MsgType_1_1._Reply ) { if (logger.isWarnEnabled()) { logger.warn ("Received a GIOP 1.1 message of type " + msg_type + " with the " + "" + "\"more fragments follow\" bits set" + " in " + this.toString() ); } MessageOutputStream out = new MessageOutputStream( orb ); out.writeGIOPMsgHeader( MsgType_1_1._MessageError, 1 ); out.insertMsgSize(); sendMessage( out ); buf_mg.returnBuffer( message ); return; } //GIOP 1.1 Fragmented messages currently not supported if (logger.isWarnEnabled()) { logger.warn( "Received a fragmented GIOP 1.1 message" + " in " + this.toString() ); } int giop_minor = Messages.getGIOPMinor( message ); final ReplyOutputStream out = new ReplyOutputStream( orb, Messages.getRequestId( message ), ReplyStatusType_1_2.SYSTEM_EXCEPTION, giop_minor, false, logger);//no locate reply try { SystemExceptionHelper.write( out, new NO_IMPLEMENT( 0, CompletionStatus.COMPLETED_NO )); sendMessage( out ); buf_mg.returnBuffer( message ); return; } finally { out.close(); } } //check, that only the correct message types are fragmented if ( msg_type == MsgType_1_1._CancelRequest || msg_type == MsgType_1_1._CloseConnection || msg_type == MsgType_1_1._CancelRequest ) { if (logger.isWarnEnabled()) { logger.warn ("Received a GIOP message of type " + msg_type + " with the \"more fragments follow\" bits set, " + "but this message type isn't allowed to be " + "fragmented, in " + this.toString() ); } MessageOutputStream out = new MessageOutputStream( orb ); out.writeGIOPMsgHeader( MsgType_1_1._MessageError, 1 ); out.insertMsgSize(); sendMessage( out ); buf_mg.returnBuffer( message ); return; } //if we're here, it's the first part of a fragmented message Integer request_id = new Integer( Messages.getRequestId( message )); // NOPMD //sanity check if ( fragments.containsKey( request_id )) { if (logger.isErrorEnabled()) { logger.error ("Received a message of type " + msg_type + " with the more fragments follow bit set," + " but there is already an fragmented," + " incomplete message with the same request id (" + request_id + ", in " + this.toString() ); } //Drop this one and continue buf_mg.returnBuffer( message ); return; } //create new stream and add to table ByteArrayOutputStream b_out = new ByteArrayOutputStream(); fragments.put( request_id, b_out ); fragmentsSize.put(request_id, Messages.getMsgSize(message)); //add the message contents to stream b_out.write( message, 0, Messages.MSG_HEADER_SIZE + Messages.getMsgSize(message) ); buf_mg.returnBuffer( message ); //This message isn't yet complete return; } switch ( msg_type ) { case MsgType_1_1._Request: { getRequestListener().requestReceived( message, this ); break; } case MsgType_1_1._Reply: { getReplyListener().replyReceived( message, this ); break; } case MsgType_1_1._CancelRequest: { getRequestListener().cancelRequestReceived( message, this ); break; } case MsgType_1_1._LocateRequest: { getRequestListener().locateRequestReceived( message, this ); break; } case MsgType_1_1._LocateReply: { getReplyListener().locateReplyReceived( message, this ); break; } case MsgType_1_1._CloseConnection: { getReplyListener().closeConnectionReceived( message, this ); break; } case MsgType_1_1._MessageError: { break; } case MsgType_1_1._Fragment: { //currently not reached break; } default: { if (logger.isErrorEnabled()) { logger.error ("Received message with unknown message type " + msg_type + ", in " + this.toString() ); } buf_mg.returnBuffer( message ); } } }//synchronized( pendingUndecidedSync ) } // this should be catch out of memory catch (NO_MEMORY e) { logger.error ("Caught NO_MEMORY error", e); streamClosed(); } catch (OutOfMemoryError e) { logger.error ("Caught OutOfMemory error", e); streamClosed(); } } // timeout is in milliseconds and is an interval protected final boolean getWriteLock (long timeout) { long endTime = (timeout > 0 ? System.currentTimeMillis() + timeout : Long.MAX_VALUE); while (endTime > System.currentTimeMillis()) { long remainingTime = endTime - System.currentTimeMillis(); try { return writeLock.tryLock (remainingTime, TimeUnit.MILLISECONDS); } catch( InterruptedException e ) { // disregard } } return false; } protected final void releaseWriteLock() { try { writeLock.unlock(); } catch (IllegalMonitorStateException ex) { // This allows threads that have failed to acquire this lock to safely // attempt an unlock. } } private final synchronized void incPendingWrite() { ++pending_write; } private final synchronized void decPendingWrite() { --pending_write; } public final synchronized void incPendingMessages() { ++pending_messages; } public final synchronized void decPendingMessages() { --pending_messages; } public final synchronized boolean hasPendingMessages() { return (pending_messages != 0) || (pending_write != 0); } /** * write (a fragment of) the message (passes it on to the wire) */ @Override public final void write( byte[] fragment, int start, int size ) { ConnectionReset write_monitor = null; if (write_monitor_timeout != null) write_monitor = new ConnectionReset (write_monitor_timeout.longValue()); if (timer_queue != null) timer_queue.add(write_monitor); if (sendDeadline != null) { long time = Time.millisTo(sendDeadline); time = (time == 0 ? -1 : time); transport.write( false, false, fragment, start, size, time); } else { transport.write( false, false, fragment, start, size, 0 ); } if (timer_queue != null) timer_queue.remove(write_monitor); if (getStatisticsProviderAdapter() != null) { getStatisticsProviderAdapter().messageChunkSent (size); } } /* pro forma implementations of io.OutputStream methods */ @Override public final void write(int value) throws java.io.IOException { throw new org.omg.CORBA.NO_IMPLEMENT(); } @Override public final void write(byte[] value) throws java.io.IOException { throw new org.omg.CORBA.NO_IMPLEMENT(); } @Override public final void flush() throws java.io.IOException { throw new org.omg.CORBA.NO_IMPLEMENT(); } public final void sendRequest( MessageOutputStream out, boolean expect_reply ) throws IOException { if ( expect_reply ) { incPendingMessages(); } sendMessage( out, (out instanceof RequestOutputStream ? ((RequestOutputStream)out).getReplyEndTime() : null )); } public final void sendReply( MessageOutputStream out ) throws IOException { decPendingMessages(); sendMessage( out ); } private final void sendMessage( MessageOutputStream out ) throws IOException { sendMessage (out, null); } private final void sendMessage( MessageOutputStream out, org.omg.TimeBase.UtcT sendDeadline) throws IOException { try { try { incPendingWrite (); long timeout = (sendDeadline == null ? 0 : Time.millisTo(sendDeadline)); if (logger.isDebugEnabled()) { logger.debug ("GIOPConnection.sendMessage timeout (millis): " + timeout); } if (!getWriteLock(timeout)) { throw new TIMEOUT("Failed to acquire transport lock in " + timeout + " ms"); } // save send deadline for use later in the stack this.sendDeadline = sendDeadline; if (!transport.is_connected()) { tcs_negotiated = false; if (logger.isDebugEnabled()) { logger.debug ( this.toString() + ": sendMessage() - opening transport " + transport ); } synchronized (connect_sync) { try { long myConnectTimeout = (timeout != 0 && timeout < connectTimeout ? timeout : connectTimeout); transport.connect (profile, myConnectTimeout); connect_sync.notifyAll(); } catch (RuntimeException ex) { if (logger.isDebugEnabled()) { logger.debug ( this.toString() + ": sendMessage() -- failed to open transport"); } throw ex; } } } out.write_to( this ); transport.flush(); if (logger.isDebugEnabled()) { logger.debug ("wrote GIOP message of size {} to {}", out.size(), this.toString()); } if (getStatisticsProviderAdapter() != null) { getStatisticsProviderAdapter().flushed(); } } finally { sendDeadline = null; decPendingWrite(); // If a COMM_FAILURE occurs this release write lock prevents // dead locks to reader thread which might try to close this // socket concurrently too (unfortunately write lock is // requested during streamClosed()) releaseWriteLock(); } } catch (org.omg.CORBA.COMM_FAILURE e) { if (logger.isErrorEnabled()) { logger.error ( "Failed to write GIOP message due to COMM_FAILURE, in " + this.toString(), e ); } if ( !do_close ) { if (logger.isErrorEnabled()) { logger.error ("Underlying transport connection closed due to " + "errors during sendMessage(), in " + this.toString() ); } // It makes no sense to use this transport any longer // examples: firewall dropped connection silently, // socket system buffers full (peer didn't read // data in time) // signal GIOPConnectionManager to throw this connection away this.streamClosed(); } throw e; } } public final boolean isSSL() { if (transport instanceof StreamConnectionBase) { return ((StreamConnectionBase)transport).isSSL(); } return false; } @Override public void close() { if (logger.isDebugEnabled()) { logger.debug(this.toString() + ": close()" ); } synchronized (connect_sync) { if ( connection_listener != null ) { connection_listener.connectionClosed(); } do_close = true; transport.close(); connect_sync.notifyAll(); } } /** * Get an instance of StatisticsProvider derivative, for * updating the transport usage statistics. */ protected final StatisticsProviderAdapter getStatisticsProviderAdapter() { return statistics_provider_adapter; } /** * Get the statistics provider for transport usage statistics * that can be used in conjunction with the SelectionStrategy. * This is a special-case provider, usually supplied by, and * known to, the concrete SelectionStrategy. To actually update * the usage stats use getStatisticsProviderAdapter() */ public final StatisticsProvider getStatisticsProvider() { return statistics_provider; } /** * Return the StatissticsProvider, given the cardinality number * @param no * @return */ public StatisticsProvider getStatisticsProvider(int no) { if (statistics_provider_adapter == null) return null; return statistics_provider_adapter.find(no); } /* class CachedContext { public byte[] client_authentication_token; public EstablishContext msg; CachedContext(byte[] client_authentication_token, EstablishContext msg) { this.client_authentication_token = client_authentication_token; this.msg = msg; } } public void cacheSASContext(long client_context_id, byte[] client_authentication_token, EstablishContext msg) { synchronized ( sasContexts ) { sasContexts.put(new Long(client_context_id), new CachedContext(client_authentication_token, msg)); } } public void purgeSASContext(long client_context_id) { synchronized ( sasContexts ) { sasContexts.remove(new Long(client_context_id)); } } public byte[] getSASContext(long client_context_id) { Long key = new Long(client_context_id); synchronized (sasContexts) { if (!sasContexts.containsKey(key)) return null; return ((CachedContext)sasContexts.get(key)).client_authentication_token; } } public EstablishContext getSASContextMsg(long client_context_id) { Long key = new Long(client_context_id); synchronized (sasContexts) { if (!sasContexts.containsKey(key)) return null; return ((CachedContext)sasContexts.get(key)).msg; } } */ // provide cubbyholes for data public static int allocate_cubby_id() { return cubby_count++; } public Object get_cubby(int id) { if (id < 0 || id >= cubby_count) { if (logger.isErrorEnabled()) { logger.error ("Get bad cubby id "+id+" (max="+cubby_count+"), in " + this.toString() ); } return null; } return cubbyholes[id]; } public void set_cubby(int id, Object obj) { if (id < 0 || id >= cubby_count) { if (logger.isErrorEnabled()) { logger.error ("Set bad cubby id "+id+" (max="+cubby_count+"), in " + this.toString() ); } return; } cubbyholes[id] = obj; } /*default (or, package-level) access*/ org.omg.ETF.Profile getProfile() { return profile; } }// GIOPConnection