(DISTFILES): Comment out a few missing files.
[mono-project.git] / mcs / class / Novell.Directory.Ldap / Novell.Directory.Ldap / Message.cs
blobb36d618034436a6371de3f8357d4ab1fee35f6c4
2 //
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:
10 //
11 // The above copyright notice and this permission notice shall be
12 // included in all copies or substantial portions of the Software.
13 //
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 /******************************************************************************
23 * The MIT License
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
42 * SOFTWARE.
43 *******************************************************************************/
45 // Novell.Directory.Ldap.Message.cs
47 // Author:
48 // Sunil Kumar (Sunilk@novell.com)
50 // (C) 2003 Novell, Inc (http://www.novell.com)
53 using System;
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>
61 /* package */
62 class Message
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.
70 /// </summary>
71 virtual internal int Count
73 /* package */
75 get
77 int size = replies.Count;
78 if (complete)
80 return (size > 0?(size - 1):size);
82 else
84 return size;
90 /// <summary> sets the agent for this message</summary>
91 virtual internal MessageAgent Agent
93 /* package */
95 set
97 this.agent = value;
98 return ;
103 /// <summary> Returns true if replies are queued
104 ///
105 /// </summary>
106 /// <returns> false if no replies are queued, otherwise true
107 /// </returns>
108 /* package */
109 internal virtual bool hasReplies()
111 if (replies == null)
113 // abandoned request
114 return false;
116 return (replies.Count > 0);
119 virtual internal int MessageType
121 /* package */
125 if (msg == null)
127 return - 1;
129 return msg.Type;
134 virtual internal int MessageID
136 /* package */
140 return msgId;
145 /// <summary> gets the operation complete status for this message
146 ///
147 /// </summary>
148 /// <returns> the true if the operation is complete, i.e.
149 /// the LdapResult has been received.
150 /// </returns>
151 virtual internal bool Complete
153 /* package */
157 return complete;
162 /// <summary> Gets the next reply from the reply queue or waits until one is there
163 ///
164 /// </summary>
165 /// <returns> the next reply message on the reply queue or null
166 /// </returns>
167 /* package */
168 internal virtual System.Object waitForReply()
170 if (replies == null)
172 return null;
174 // sync on message so don't confuse with timer thread
175 lock (replies)
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)
188 ; // do nothing
190 if (waitForReply_Renamed_Field)
192 continue;
194 else
196 break;
199 else
201 System.Object temp_object;
202 temp_object = replies[0];
203 replies.RemoveAt(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);
211 else
214 return msg;
216 return null;
221 /// <summary> Gets the next reply from the reply queue if one exists
222 ///
223 /// </summary>
224 /// <returns> the next reply message on the reply queue or null if none
225 /// </returns>
226 virtual internal System.Object Reply
228 /* package */
232 System.Object msg;
233 if (replies == null)
235 return null;
237 lock (replies)
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];
246 replies.RemoveAt(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);
254 return msg;
259 /// <summary> Returns true if replies are accepted for this request.
260 ///
261 /// </summary>
262 /// <returns> false if replies are no longer accepted for this request
263 /// </returns>
264 /* package */
265 internal virtual bool acceptsReplies()
267 return acceptReplies;
270 /// <summary> gets the LdapMessage request associated with this message
271 ///
272 /// </summary>
273 /// <returns> the LdapMessage request associated with this message
274 /// </returns>
275 virtual internal LdapMessage Request
277 /*package*/
281 return msg;
287 virtual internal bool BindRequest
289 /* package */
293 return (bindprops != null);
299 /// <summary> gets the MessageAgent associated with this message
300 ///
301 /// </summary>
302 /// <returns> the MessageAgent associated with this message
303 /// </returns>
304 virtual internal MessageAgent MessageAgent
306 /* package */
310 return agent;
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)
332 InitBlock();
333 this.msg = msg;
334 this.conn = conn;
335 this.agent = agent;
336 this.queue = queue;
337 this.mslimit = mslimit;
338 this.msgId = msg.MessageID;
339 this.bindprops = bindprops;
340 return ;
343 internal void sendMessage()
345 conn.writeMessage(this);
346 // Start the timer thread
347 if (mslimit != 0)
349 // Don't start the timer thread for abandon or Unbind
350 switch (msg.Type)
353 case LdapMessage.ABANDON_REQUEST:
354 case LdapMessage.UNBIND_REQUEST:
355 mslimit = 0;
356 break;
358 default:
359 timer = new Timeout(this, mslimit, this);
360 timer.IsBackground = true; // If this is the last thread running, allow exit.
361 timer.Start();
362 break;
366 return ;
369 internal virtual void Abandon(LdapConstraints cons, InterThreadException informUserEx)
371 if (!waitForReply_Renamed_Field)
373 return ;
375 acceptReplies = false; // don't listen to anyone
376 waitForReply_Renamed_Field = false; // don't let sleeping threads lie
377 if (!complete)
381 // If a bind, release bind semaphore & wake up waiting threads
382 // Must do before writing abandon message, otherwise deadlock
383 if (bindprops != null)
385 int id;
386 if (conn.BindSemIdClear)
388 // Semaphore id for normal operations
389 id = msgId;
391 else
393 // Semaphore id for sasl bind
394 id = conn.BindSemId;
395 conn.clearBindSemId();
397 conn.freeWriteSemaphore(id);
400 // Create the abandon message, but don't track it.
401 LdapControl[] cont = null;
402 if (cons != 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)
412 ; // do nothing
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));
425 stopTimer();
426 // wake up waiting threads to receive exception
427 sleepersAwake();
428 // Message will get cleaned up when last response removed from queue
430 else
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
435 sleepersAwake();
436 cleanup();
438 return ;
441 private void cleanup()
443 stopTimer(); // Make sure timer stopped
446 acceptReplies = false;
447 if (conn != null)
449 conn.removeMessage(this);
451 // Empty out any accumuluated replies
452 if (replies != null)
454 while (!(replies.Count == 0))
456 System.Object temp_object;
457 temp_object = replies[0];
458 replies.RemoveAt(0);
459 System.Object generatedAux = temp_object;
463 catch (System.Exception ex)
465 // nothing
467 // Let GC clean up this stuff, leave name in case finalized is called
468 conn = null;
469 msg = null;
470 // agent = null; // leave this reference
471 queue = null;
472 //replies = null; //leave this since we use it as a semaphore
473 bindprops = null;
474 return ;
477 ~Message()
479 cleanup();
480 return ;
484 internal virtual void putReply(RfcLdapMessage message)
486 if (!acceptReplies)
488 return ;
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:
497 break;
499 default:
500 int res;
501 stopTimer();
502 // Accept no more results for this message
503 // Leave on connection queue so we can abandon if necessary
504 acceptReplies = false;
505 complete = true;
506 if (bindprops != null)
508 res = ((RfcResponse) message.Response).getResultCode().intValue();
509 if (res == LdapException.SASL_BIND_IN_PROGRESS)
512 else
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;
520 else
523 // If not a sasl bind in-progress, release the bind
524 // semaphore and wake up all waiting threads
525 int id;
526 if (conn.BindSemIdClear)
528 // Semaphore id for normal operations
529 id = msgId;
531 else
533 // Semaphore id for sasl bind
534 id = conn.BindSemId;
535 conn.clearBindSemId();
537 conn.freeWriteSemaphore(id);
540 break;
543 // wake up waiting threads
544 sleepersAwake();
545 return ;
548 /// <summary> stops the timeout timer from running</summary>
549 /* package */
550 internal virtual void stopTimer()
552 // If timer thread started, stop it
553 if (timer != null)
555 timer.Interrupt();
557 return ;
560 /// <summary> Notifies all waiting threads</summary>
561 private void sleepersAwake()
563 // Notify any thread waiting for this message id
564 lock (replies)
566 System.Threading.Monitor.Pulse(replies);
568 // Notify a thread waiting for any message id
569 agent.sleepersAwake(false);
570 return ;
573 /// <summary> Timer class to provide timing for messages. Only called
574 /// if time to wait is non zero.
575 /// </summary>
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;
594 /* package */
595 internal Timeout(Message enclosingInstance, int interval, Message msg):base()
597 InitBlock(enclosingInstance);
598 timeToWait = interval;
599 message = msg;
600 return ;
603 /// <summary> The timeout thread. If it wakes from the sleep, future input
604 /// is stopped and the request is timed out.
605 /// </summary>
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
619 return ;
623 /// <summary> sets the agent for this message</summary>