3 // Permission is hereby granted, free of charge, to any person obtaining
4 // a copy of this software and associated documentation files (the
5 // "Software"), to deal in the Software without restriction, including
6 // without limitation the rights to use, copy, modify, merge, publish,
7 // distribute, sublicense, and/or sell copies of the Software, and to
8 // permit persons to whom the Software is furnished to do so, subject to
9 // the following conditions:
11 // The above copyright notice and this permission notice shall be
12 // included in all copies or substantial portions of the Software.
14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 /******************************************************************************
24 * Copyright (c) 2003 Novell Inc. www.novell.com
26 * Permission is hereby granted, free of charge, to any person obtaining a copy
27 * of this software and associated documentation files (the Software), to deal
28 * in the Software without restriction, including without limitation the rights
29 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
30 * copies of the Software, and to permit persons to whom the Software is
31 * furnished to do so, subject to the following conditions:
33 * The above copyright notice and this permission notice shall be included in
34 * all copies or substantial portions of the Software.
36 * THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
37 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
38 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
39 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
40 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
41 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
43 *******************************************************************************/
45 // Novell.Directory.Ldap.Message.cs
48 // Sunil Kumar (Sunilk@novell.com)
50 // (C) 2003 Novell, Inc (http://www.novell.com)
54 using Novell
.Directory
.Ldap
.Rfc2251
;
55 using Novell
.Directory
.Ldap
.Utilclass
;
57 namespace Novell
.Directory
.Ldap
60 /// <summary> Encapsulates an Ldap message, its state, and its replies.</summary>
64 private void InitBlock()
66 replies
= new MessageVector(5, 5);
68 /// <summary> Get number of messages queued.
69 /// Don't count the last message containing result code.
71 virtual internal int Count
77 int size
= replies
.Count
;
80 return (size
> 0?(size
- 1):size
);
90 /// <summary> sets the agent for this message</summary>
91 virtual internal MessageAgent Agent
103 /// <summary> Returns true if replies are queued
106 /// <returns> false if no replies are queued, otherwise true
109 internal virtual bool hasReplies()
116 return (replies
.Count
> 0);
119 virtual internal int MessageType
134 virtual internal int MessageID
145 /// <summary> gets the operation complete status for this message
148 /// <returns> the true if the operation is complete, i.e.
149 /// the LdapResult has been received.
151 virtual internal bool Complete
162 /// <summary> Gets the next reply from the reply queue or waits until one is there
165 /// <returns> the next reply message on the reply queue or null
168 internal virtual System
.Object
waitForReply()
174 // sync on message so don't confuse with timer thread
177 System
.Object msg
= null;
178 while (waitForReply_Renamed_Field
)
180 if ((replies
.Count
== 0))
184 System
.Threading
.Monitor
.Wait(replies
);
186 catch (System
.Threading
.ThreadInterruptedException ir
)
190 if (waitForReply_Renamed_Field
)
201 System
.Object temp_object
;
202 temp_object
= replies
[0];
204 msg
= temp_object
; // Atomic get and remove
206 if ((complete
|| !acceptReplies
) && (replies
.Count
== 0))
208 // Remove msg from connection queue when last reply read
209 conn
.removeMessage(this);
221 /// <summary> Gets the next reply from the reply queue if one exists
224 /// <returns> the next reply message on the reply queue or null if none
226 virtual internal System
.Object Reply
239 // Test and remove must be atomic
240 if ((replies
.Count
== 0))
242 return null; // No data
244 System
.Object temp_object
;
245 temp_object
= replies
[0];
247 msg
= temp_object
; // Atomic get and remove
249 if ((conn
!= null) && (complete
|| !acceptReplies
) && (replies
.Count
== 0))
251 // Remove msg from connection queue when last reply read
252 conn
.removeMessage(this);
259 /// <summary> Returns true if replies are accepted for this request.
262 /// <returns> false if replies are no longer accepted for this request
265 internal virtual bool acceptsReplies()
267 return acceptReplies
;
270 /// <summary> gets the LdapMessage request associated with this message
273 /// <returns> the LdapMessage request associated with this message
275 virtual internal LdapMessage Request
287 virtual internal bool BindRequest
293 return (bindprops
!= null);
299 /// <summary> gets the MessageAgent associated with this message
302 /// <returns> the MessageAgent associated with this message
304 virtual internal MessageAgent MessageAgent
315 private LdapMessage msg
; // msg request sent to server
316 private Connection conn
; // Connection object where msg sent
317 private MessageAgent agent
; // MessageAgent handling this request
318 private LdapMessageQueue queue
; // Application message queue
319 private int mslimit
; // client time limit in milliseconds
320 private SupportClass
.ThreadClass timer
= null; // Timeout thread
321 // Note: MessageVector is synchronized
322 private MessageVector replies
; // place to store replies
323 private int msgId
; // message ID of this request
324 private bool acceptReplies
= true; // false if no longer accepting replies
325 private bool waitForReply_Renamed_Field
= true; // true if wait for reply
326 private bool complete
= false; // true LdapResult received
327 private System
.String name
; // String name used for Debug
328 private BindProperties bindprops
; // Bind properties if a bind request
330 internal Message(LdapMessage msg
, int mslimit
, Connection conn
, MessageAgent agent
, LdapMessageQueue queue
, BindProperties bindprops
)
337 this.mslimit
= mslimit
;
338 this.msgId
= msg
.MessageID
;
339 this.bindprops
= bindprops
;
343 internal void sendMessage()
345 conn
.writeMessage(this);
346 // Start the timer thread
349 // Don't start the timer thread for abandon or Unbind
353 case LdapMessage
.ABANDON_REQUEST
:
354 case LdapMessage
.UNBIND_REQUEST
:
359 timer
= new Timeout(this, mslimit
, this);
360 timer
.IsBackground
= true; // If this is the last thread running, allow exit.
369 internal virtual void Abandon(LdapConstraints cons
, InterThreadException informUserEx
)
371 if (!waitForReply_Renamed_Field
)
375 acceptReplies
= false; // don't listen to anyone
376 waitForReply_Renamed_Field
= false; // don't let sleeping threads lie
381 // If a bind, release bind semaphore & wake up waiting threads
382 // Must do before writing abandon message, otherwise deadlock
383 if (bindprops
!= null)
386 if (conn
.BindSemIdClear
)
388 // Semaphore id for normal operations
393 // Semaphore id for sasl bind
395 conn
.clearBindSemId();
397 conn
.freeWriteSemaphore(id
);
400 // Create the abandon message, but don't track it.
401 LdapControl
[] cont
= null;
404 cont
= cons
.getControls();
406 LdapMessage msg
= new LdapAbandonRequest(msgId
, cont
);
407 // Send abandon message to server
408 conn
.writeMessage(msg
);
410 catch (LdapException ex
)
414 // If not informing user, remove message from agent
415 if (informUserEx
== null)
417 agent
.Abandon(msgId
, null);
419 conn
.removeMessage(this);
421 // Get rid of all replies queued
422 if (informUserEx
!= null)
424 replies
.Add(new LdapResponse(informUserEx
, conn
.ActiveReferral
));
426 // wake up waiting threads to receive exception
428 // Message will get cleaned up when last response removed from queue
432 // Wake up any waiting threads, so they can terminate.
433 // If informing the user, we wake sleepers after
434 // caller queues dummy response with error status
441 private void cleanup()
443 stopTimer(); // Make sure timer stopped
446 acceptReplies
= false;
449 conn
.removeMessage(this);
451 // Empty out any accumuluated replies
454 while (!(replies
.Count
== 0))
456 System
.Object temp_object
;
457 temp_object
= replies
[0];
459 System
.Object generatedAux
= temp_object
;
463 catch (System
.Exception ex
)
467 // Let GC clean up this stuff, leave name in case finalized is called
470 // agent = null; // leave this reference
472 //replies = null; //leave this since we use it as a semaphore
484 internal virtual void putReply(RfcLdapMessage message
)
490 replies
.Add(message
);
491 message
.RequestingMessage
= msg
; // Save request message info
492 switch (message
.Type
)
495 case LdapMessage
.SEARCH_RESPONSE
:
496 case LdapMessage
.SEARCH_RESULT_REFERENCE
:
502 // Accept no more results for this message
503 // Leave on connection queue so we can abandon if necessary
504 acceptReplies
= false;
506 if (bindprops
!= null)
508 res
= ((RfcResponse
) message
.Response
).getResultCode().intValue();
509 if (res
== LdapException
.SASL_BIND_IN_PROGRESS
)
514 // We either have success or failure on the bind
515 if (res
== LdapException
.SUCCESS
)
517 // Set bind properties into connection object
518 conn
.BindProperties
= bindprops
;
523 // If not a sasl bind in-progress, release the bind
524 // semaphore and wake up all waiting threads
526 if (conn
.BindSemIdClear
)
528 // Semaphore id for normal operations
533 // Semaphore id for sasl bind
535 conn
.clearBindSemId();
537 conn
.freeWriteSemaphore(id
);
543 // wake up waiting threads
548 /// <summary> stops the timeout timer from running</summary>
550 internal virtual void stopTimer()
552 // If timer thread started, stop it
560 /// <summary> Notifies all waiting threads</summary>
561 private void sleepersAwake()
563 // Notify any thread waiting for this message id
566 System
.Threading
.Monitor
.Pulse(replies
);
568 // Notify a thread waiting for any message id
569 agent
.sleepersAwake(false);
573 /// <summary> Timer class to provide timing for messages. Only called
574 /// if time to wait is non zero.
576 private sealed class Timeout
:SupportClass
.ThreadClass
578 private void InitBlock(Message enclosingInstance
)
580 this.enclosingInstance
= enclosingInstance
;
582 private Message enclosingInstance
;
583 public Message Enclosing_Instance
587 return enclosingInstance
;
591 private int timeToWait
= 0;
592 private Message message
;
595 internal Timeout(Message enclosingInstance
, int interval
, Message msg
):base()
597 InitBlock(enclosingInstance
);
598 timeToWait
= interval
;
603 /// <summary> The timeout thread. If it wakes from the sleep, future input
604 /// is stopped and the request is timed out.
606 override public void Run()
610 System
.Threading
.Thread
.Sleep(new System
.TimeSpan(10000 * timeToWait
));
611 message
.acceptReplies
= false;
612 // Note: Abandon clears the bind semaphore after failed bind.
613 message
.Abandon(null, new InterThreadException("Client request timed out", null, LdapException
.Ldap_TIMEOUT
, null, message
));
615 catch (System
.Threading
.ThreadInterruptedException ie
)
617 // the timer was stopped, do nothing
623 /// <summary> sets the agent for this message</summary>