/// <summary> Creates an LdapResponse using an LdapException. /// Used to wake up the user following an abandon. /// Note: The abandon doesn't have to be user initiated /// but may be the result of error conditions. /// /// Referral information is available if this connection created solely /// to follow a referral. /// /// </summary> /// <param name="ex"> The exception /// /// </param> /// <param name="activeReferral"> The referral actually used to create the /// connection /// </param> public LdapResponse(InterThreadException ex, ReferralInfo activeReferral) { exception = ex; this.activeReferral = activeReferral; return; }
/// <summary> Creates an LdapResponse using an LdapException. /// Used to wake up the user following an abandon. /// Note: The abandon doesn't have to be user initiated /// but may be the result of error conditions. /// /// Referral information is available if this connection created solely /// to follow a referral. /// /// </summary> /// <param name="ex"> The exception /// /// </param> /// <param name="activeReferral"> The referral actually used to create the /// connection /// </param> public LdapResponse(InterThreadException ex, ReferralInfo activeReferral) { exception = ex; this.activeReferral = activeReferral; return ; }
/// <summary> /// Creates an LdapResponse using an LdapException. /// Used to wake up the user following an abandon. /// Note: The abandon doesn't have to be user initiated /// but may be the result of error conditions. /// Referral information is available if this connection created solely /// to follow a referral. /// </summary> /// <param name="ex"> /// The exception. /// </param> /// <param name="activeReferral"> /// The referral actually used to create the /// connection. /// </param> public LdapResponse(InterThreadException ex, ReferralInfo activeReferral) { _exception = ex; ActiveReferral = activeReferral; }
internal virtual void Abandon(LdapConstraints cons, InterThreadException informUserEx) { if (!waitForReply_Renamed_Field) { return ; } acceptReplies = false; // don't listen to anyone waitForReply_Renamed_Field = false; // don't let sleeping threads lie if (!complete) { try { // If a bind, release bind semaphore & wake up waiting threads // Must do before writing abandon message, otherwise deadlock if (bindprops != null) { int id; if (conn.BindSemIdClear) { // Semaphore id for normal operations id = msgId; } else { // Semaphore id for sasl bind id = conn.BindSemId; conn.clearBindSemId(); } conn.freeWriteSemaphore(id); } // Create the abandon message, but don't track it. LdapControl[] cont = null; if (cons != null) { cont = cons.getControls(); } LdapMessage msg = new LdapAbandonRequest(msgId, cont); // Send abandon message to server conn.writeMessage(msg); } catch (LdapException ex) { ; // do nothing } // If not informing user, remove message from agent if (informUserEx == null) { agent.Abandon(msgId, null); } conn.removeMessage(this); } // Get rid of all replies queued if (informUserEx != null) { replies.Add(new LdapResponse(informUserEx, conn.ActiveReferral)); stopTimer(); // wake up waiting threads to receive exception sleepersAwake(); // Message will get cleaned up when last response removed from queue } else { // Wake up any waiting threads, so they can terminate. // If informing the user, we wake sleepers after // caller queues dummy response with error status sleepersAwake(); cleanup(); } return ; }
internal virtual void Abandon(LdapConstraints cons, InterThreadException informUserEx) { if (!waitForReply_Renamed_Field) { return; } acceptReplies = false; // don't listen to anyone waitForReply_Renamed_Field = false; // don't let sleeping threads lie if (!complete) { try { // If a bind, release bind semaphore & wake up waiting threads // Must do before writing abandon message, otherwise deadlock if (bindprops != null) { int id; if (conn.BindSemIdClear) { // Semaphore id for normal operations id = msgId; } else { // Semaphore id for sasl bind id = conn.BindSemId; conn.clearBindSemId(); } conn.freeWriteSemaphore(id); } // Create the abandon message, but don't track it. LdapControl[] cont = null; if (cons != null) { cont = cons.getControls(); } LdapMessage msg = new LdapAbandonRequest(msgId, cont); // Send abandon message to server conn.writeMessage(msg); } catch (LdapException ex) { LogManager.GetCurrentClassLogger().Warn("Exception swallowed", ex); } // If not informing user, remove message from agent if (informUserEx == null) { agent.Abandon(msgId, null); } conn.removeMessage(this); } // Get rid of all replies queued if (informUserEx != null) { replies.Add(new LdapResponse(informUserEx, conn.ActiveReferral)); stopTimer(); // wake up waiting threads to receive exception sleepersAwake(); // Message will get cleaned up when last response removed from queue } else { // Wake up any waiting threads, so they can terminate. // If informing the user, we wake sleepers after // caller queues dummy response with error status sleepersAwake(); cleanup(); } }
/// <summary> Cleans up resources associated with this connection. /// This method may be called by finalize() for the connection, or it may /// be called by LdapConnection.disconnect(). /// Should not have a writeSemaphore lock in place, as deadlock can occur /// while abandoning connections. /// </summary> private void shutdown(System.String reason, int semaphoreId, InterThreadException notifyUser) { Message info = null; if (!clientActive) { return ; } clientActive = false; while (true) { // remove messages from connection list and send abandon try { System.Object temp_object; temp_object = messages[0]; messages.RemoveAt(0); info = (Message) temp_object; } catch (ArgumentOutOfRangeException ex) { // No more messages break; } info.Abandon(null, notifyUser); // also notifies the application } int semId = acquireWriteSemaphore(semaphoreId); // Now send unbind if socket not closed if ((bindProperties != null) && (out_Renamed != null) && (!bindProperties.Anonymous)) { try { LdapMessage msg = new LdapUnbindRequest(null); sbyte[] ber = msg.Asn1Object.getEncoding(encoder); out_Renamed.Write(SupportClass.ToByteArray(ber), 0, ber.Length); out_Renamed.Flush(); } catch (System.Exception ex) { ; // don't worry about error } } bindProperties = null; in_Renamed = null; out_Renamed = null; if (socket != null) { // Close the socket try { if(Ssl) { sock.Shutdown(SocketShutdown.Both); sock.Close(); } else socket.Close(); } catch (System.IO.IOException ie) { // ignore problem closing socket } socket = null; sock = null; } freeWriteSemaphore(semId); return ; }
/// <summary> Destroys a clone of <code>LdapConnection</code>. /// /// This method first determines if only one <code>LdapConnection</code> /// object is associated with this connection, i.e. if no clone exists. /// /// If no clone exists, the socket is closed, and the current /// <code>Connection</code> object is returned. /// /// If multiple <code>LdapConnection</code> objects are associated /// with this connection, i.e. clones exist, a {@link #copy} of the /// this object is made, but is not connected to any host. This /// disassociates that clone from the original connection. The new /// <code>Connection</code> object is returned. /// /// Only one destroyClone instance is allowed to run at any one time. /// /// If the connection is closed, any threads waiting for operations /// on that connection will wake with an LdapException indicating /// the connection is closed. /// /// </summary> /// <param name="apiCall"><code>true</code> indicates the application is closing the /// connection or or creating a new one by calling either the /// <code>connect</code> or <code>disconnect</code> methods /// of <code>LdapConnection</code>. <code>false</code> /// indicates that <code>LdapConnection</code> is being finalized. /// /// </param> /// <returns> a Connection object or null if finalizing. /// </returns> /* package */ internal Connection destroyClone(bool apiCall) { lock (this) { Connection conn = this; if (cloneCount > 0) { cloneCount--; // This is a clone, set a new connection object. if (apiCall) { conn = (Connection) this.copy(); } else { conn = null; } } else { if (in_Renamed != null) { // Not a clone and connected /* * Either the application has called disconnect or connect * resulting in the current connection being closed. If the * application has any queues waiting on messages, we * need wake these up so the application does not hang. * The boolean flag indicates whether the close came * from an API call or from the object being finalized. */ InterThreadException notify = new InterThreadException((apiCall?ExceptionMessages.CONNECTION_CLOSED:ExceptionMessages.CONNECTION_FINALIZED), null, LdapException.CONNECT_ERROR, null, null); // Destroy old connection shutdown("destroy clone", 0, notify); } } return conn; } }
/// <summary> This thread decodes and processes RfcLdapMessage's from the server. /// /// Note: This thread needs a graceful shutdown implementation. /// </summary> public virtual void Run() { System.String reason = "reader: thread stopping"; InterThreadException notify = null; Message info = null; System.IO.IOException ioex = null; this.enclosingInstance.reader = SupportClass.ThreadClass.Current(); // Enclosing_Instance.reader = SupportClass.ThreadClass.Current(); // Console.WriteLine("Inside run:" + this.enclosingInstance.reader.Name); try { for (; ; ) { // ------------------------------------------------------- // Decode an RfcLdapMessage directly from the socket. // ------------------------------------------------------- Asn1Identifier asn1ID; System.IO.Stream myIn; /* get current value of in, keep value consistant * though the loop, i.e. even during shutdown */ myIn = this.enclosingInstance.in_Renamed; if (myIn == null) { break; } asn1ID = new Asn1Identifier(myIn); int tag = asn1ID.Tag; if (asn1ID.Tag != Asn1Sequence.TAG) { continue; // loop looking for an RfcLdapMessage identifier } // Turn the message into an RfcMessage class Asn1Length asn1Len = new Asn1Length(myIn); RfcLdapMessage msg = new RfcLdapMessage(this.enclosingInstance.decoder, myIn, asn1Len.Length); // ------------------------------------------------------------ // Process the decoded RfcLdapMessage. // ------------------------------------------------------------ int msgId = msg.MessageID; // Find the message which requested this response. // It is possible to receive a response for a request which // has been abandoned. If abandoned, throw it away try { info = this.enclosingInstance.messages.findMessageById(msgId); info.putReply(msg); // queue & wake up waiting thread } catch (System.FieldAccessException ex) { /* * We get the NoSuchFieldException when we could not find * a matching message id. First check to see if this is * an unsolicited notification (msgID == 0). If it is not * we throw it away. If it is we call any unsolicited * listeners that might have been registered to listen for these * messages. */ /* Note the location of this code. We could have required * that message ID 0 be just like other message ID's but * since message ID 0 has to be treated specially we have * a separate check for message ID 0. Also note that * this test is after the regular message list has been * checked for. We could have always checked the list * of messages after checking if this is an unsolicited * notification but that would have inefficient as * message ID 0 is a rare event (as of this time). */ if (msgId == 0) { // Notify any listeners that might have been registered this.enclosingInstance.notifyAllUnsolicitedListeners(msg); /* * Was this a server shutdown unsolicited notification. * IF so we quit. Actually calling the return will * first transfer control to the finally clause which * will do the necessary clean up. */ if (this.enclosingInstance.unsolSvrShutDnNotification) { notify = new InterThreadException(ExceptionMessages.SERVER_SHUTDOWN_REQ, new System.Object[]{this.enclosingInstance.host, this.enclosingInstance.port}, LdapException.CONNECT_ERROR, null, null); return ; } } else { } } if ((this.enclosingInstance.stopReaderMessageID == msgId) || (this.enclosingInstance.stopReaderMessageID == Novell.Directory.Ldap.Connection.STOP_READING)) { // Stop the reader Thread. return ; } } } catch (System.IO.IOException ioe) { ioex = ioe; if ((this.enclosingInstance.stopReaderMessageID != Novell.Directory.Ldap.Connection.STOP_READING) && this.enclosingInstance.clientActive) { // Connection lost waiting for results from host:port notify = new InterThreadException(ExceptionMessages.CONNECTION_WAIT, new System.Object[]{this.enclosingInstance.host, this.enclosingInstance.port}, LdapException.CONNECT_ERROR, ioe, info); } // The connection is no good, don't use it any more this.enclosingInstance.in_Renamed = null; this.enclosingInstance.out_Renamed = null; } finally { /* * There can be four states that the reader can be in at this point: * 1) We are starting TLS and will be restarting the reader * after we have negotiated TLS. * - Indicated by whether stopReaderMessageID does not * equal CONTINUE_READING. * - Don't call Shutdown. * 2) We are stoping TLS and will be restarting after TLS is * stopped. * - Indicated by an IOException AND stopReaderMessageID equals * STOP_READING - in which case notify will be null. * - Don't call Shutdown * 3) We receive a Server Shutdown notification. * - Indicated by messageID equal to 0. * - call Shutdown. * 4) Another error occured * - Indicated by an IOException AND notify is not NULL * - call Shutdown. */ if ((!this.enclosingInstance.clientActive) || (notify != null)) { //#3 & 4 this.enclosingInstance.shutdown(reason, 0, notify); } else { this.enclosingInstance.stopReaderMessageID = Novell.Directory.Ldap.Connection.CONTINUE_READING; } } this.enclosingInstance.deadReaderException = ioex; this.enclosingInstance.deadReader = this.enclosingInstance.reader; this.enclosingInstance.reader = null; return ; }