KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > alfresco > filesys > smb > server > notify > NotifyChangeHandler


1 /*
2  * Copyright (C) 2005 Alfresco, Inc.
3  *
4  * Licensed under the Mozilla Public License version 1.1
5  * with a permitted attribution clause. You may obtain a
6  * copy of the License at
7  *
8  * http://www.alfresco.org/legal/license.txt
9  *
10  * Unless required by applicable law or agreed to in writing,
11  * software distributed under the License is distributed on an
12  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
13  * either express or implied. See the License for the specific
14  * language governing permissions and limitations under the
15  * License.
16  */

17 package org.alfresco.filesys.smb.server.notify;
18
19 import java.util.Vector JavaDoc;
20
21 import org.alfresco.filesys.server.filesys.DiskDeviceContext;
22 import org.alfresco.filesys.server.filesys.FileName;
23 import org.alfresco.filesys.server.filesys.NotifyChange;
24 import org.alfresco.filesys.smb.PacketType;
25 import org.alfresco.filesys.smb.server.NTTransPacket;
26 import org.alfresco.filesys.smb.server.SMBSrvPacket;
27 import org.alfresco.filesys.smb.server.SMBSrvSession;
28 import org.alfresco.filesys.util.DataPacker;
29 import org.apache.commons.logging.Log;
30 import org.apache.commons.logging.LogFactory;
31
32 /**
33  * Notify Change Handler Class
34  */

35 public class NotifyChangeHandler implements Runnable JavaDoc
36 {
37     private static final Log logger = LogFactory.getLog("org.alfresco.smb.protocol");
38
39     // Change notification request list and global filter mask
40

41     private NotifyRequestList m_notifyList;
42     private int m_globalNotifyMask;
43
44     // Associated disk device context
45

46     private DiskDeviceContext m_diskCtx;
47
48     // Change notification processing thread
49

50     private Thread JavaDoc m_procThread;
51
52     // Change events queue
53

54     private NotifyChangeEventList m_eventList;
55
56     // Debug output enable
57

58     private boolean m_debug = false;
59
60     // Shutdown request flag
61

62     private boolean m_shutdown;
63
64     /**
65      * Class constructor
66      *
67      * @param diskCtx DiskDeviceContext
68      */

69     public NotifyChangeHandler(DiskDeviceContext diskCtx)
70     {
71
72         // Save the associated disk context details
73

74         m_diskCtx = diskCtx;
75
76         // Allocate the events queue
77

78         m_eventList = new NotifyChangeEventList();
79
80         // Create the processing thread
81

82         m_procThread = new Thread JavaDoc(this);
83
84         m_procThread.setDaemon(true);
85         m_procThread.setName("Notify_" + m_diskCtx.getDeviceName());
86
87         m_procThread.start();
88     }
89
90     /**
91      * Add a request to the change notification list
92      *
93      * @param req NotifyRequest
94      */

95     public final void addNotifyRequest(NotifyRequest req)
96     {
97
98         // Check if the request list has been allocated
99

100         if (m_notifyList == null)
101             m_notifyList = new NotifyRequestList();
102
103         // Add the request to the list
104

105         req.setDiskContext(m_diskCtx);
106         m_notifyList.addRequest(req);
107
108         // Regenerate the global notify change filter mask
109

110         m_globalNotifyMask = m_notifyList.getGlobalFilter();
111     }
112
113     /**
114      * Remove a request from the notify change request list
115      *
116      * @param req NotifyRequest
117      */

118     public final void removeNotifyRequest(NotifyRequest req)
119     {
120         removeNotifyRequest(req, true);
121     }
122
123     /**
124      * Remove a request from the notify change request list
125      *
126      * @param req NotifyRequest
127      * @param updateMask boolean
128      */

129     public final void removeNotifyRequest(NotifyRequest req, boolean updateMask)
130     {
131
132         // Check if the request list has been allocated
133

134         if (m_notifyList == null)
135             return;
136
137         // Remove the request from the list
138

139         m_notifyList.removeRequest(req);
140
141         // Regenerate the global notify change filter mask
142

143         if (updateMask == true)
144             m_globalNotifyMask = m_notifyList.getGlobalFilter();
145     }
146
147     /**
148      * Remove all notification requests owned by the specified session
149      *
150      * @param sess SMBSrvSession
151      */

152     public final void removeNotifyRequests(SMBSrvSession sess)
153     {
154
155         // Remove all requests owned by the session
156

157         m_notifyList.removeAllRequestsForSession(sess);
158
159         // Recalculate the global notify change filter mask
160

161         m_globalNotifyMask = m_notifyList.getGlobalFilter();
162     }
163
164     /**
165      * Determine if the filter has file name change notification, triggered if a file is created,
166      * renamed or deleted
167      *
168      * @return boolean
169      */

170     public final boolean hasFileNameChange()
171     {
172         return hasFilterFlag(NotifyChange.FileName);
173     }
174
175     /**
176      * Determine if the filter has directory name change notification, triggered if a directory is
177      * created or deleted.
178      *
179      * @return boolean
180      */

181     public final boolean hasDirectoryNameChange()
182     {
183         return hasFilterFlag(NotifyChange.DirectoryName);
184     }
185
186     /**
187      * Determine if the filter has attribute change notification
188      *
189      * @return boolean
190      */

191     public final boolean hasAttributeChange()
192     {
193         return hasFilterFlag(NotifyChange.Attributes);
194     }
195
196     /**
197      * Determine if the filter has file size change notification
198      *
199      * @return boolean
200      */

201     public final boolean hasFileSizeChange()
202     {
203         return hasFilterFlag(NotifyChange.Size);
204     }
205
206     /**
207      * Determine if the filter has last write time change notification
208      *
209      * @return boolean
210      */

211     public final boolean hasFileWriteTimeChange()
212     {
213         return hasFilterFlag(NotifyChange.LastWrite);
214     }
215
216     /**
217      * Determine if the filter has last access time change notification
218      *
219      * @return boolean
220      */

221     public final boolean hasFileAccessTimeChange()
222     {
223         return hasFilterFlag(NotifyChange.LastAccess);
224     }
225
226     /**
227      * Determine if the filter has creation time change notification
228      *
229      * @return boolean
230      */

231     public final boolean hasFileCreateTimeChange()
232     {
233         return hasFilterFlag(NotifyChange.Creation);
234     }
235
236     /**
237      * Determine if the filter has the security descriptor change notification
238      *
239      * @return boolean
240      */

241     public final boolean hasSecurityDescriptorChange()
242     {
243         return hasFilterFlag(NotifyChange.Security);
244     }
245
246     /**
247      * Check if debug output is enabled
248      *
249      * @return boolean
250      */

251     public final boolean hasDebug()
252     {
253         return m_debug;
254     }
255
256     /**
257      * Return the global notify filter mask
258      *
259      * @return int
260      */

261     public final int getGlobalNotifyMask()
262     {
263         return m_globalNotifyMask;
264     }
265
266     /**
267      * Return the notify request queue size
268      *
269      * @return int
270      */

271     public final int getRequestQueueSize()
272     {
273         return m_notifyList != null ? m_notifyList.numberOfRequests() : 0;
274     }
275
276     /**
277      * Check if the change filter has the specified flag enabled
278      *
279      * @param flag
280      * @return boolean
281      */

282     private final boolean hasFilterFlag(int flag)
283     {
284         return (m_globalNotifyMask & flag) != 0 ? true : false;
285     }
286
287     /**
288      * File changed notification
289      *
290      * @param action int
291      * @param path String
292      */

293     public final void notifyFileChanged(int action, String JavaDoc path)
294     {
295
296         // Check if file change notifications are enabled
297

298         if (getGlobalNotifyMask() == 0 || hasFileNameChange() == false)
299             return;
300
301         // Queue the change notification
302

303         queueNotification(new NotifyChangeEvent(NotifyChange.FileName, action, path, false));
304     }
305
306     /**
307      * File/directory renamed notification
308      *
309      * @param oldName String
310      * @param newName String
311      */

312     public final void notifyRename(String JavaDoc oldName, String JavaDoc newName)
313     {
314
315         // Check if file change notifications are enabled
316

317         if (getGlobalNotifyMask() == 0 || (hasFileNameChange() == false && hasDirectoryNameChange() == false))
318             return;
319
320         // Queue the change notification event
321

322         queueNotification(new NotifyChangeEvent(NotifyChange.FileName, NotifyChange.ActionRenamedNewName, oldName,
323                 newName, false));
324     }
325
326     /**
327      * Directory changed notification
328      *
329      * @param action int
330      * @param path String
331      */

332     public final void notifyDirectoryChanged(int action, String JavaDoc path)
333     {
334
335         // Check if file change notifications are enabled
336

337         if (getGlobalNotifyMask() == 0 || hasDirectoryNameChange() == false)
338             return;
339
340         // Queue the change notification event
341

342         queueNotification(new NotifyChangeEvent(NotifyChange.DirectoryName, action, path, true));
343     }
344
345     /**
346      * Attributes changed notification
347      *
348      * @param path String
349      * @param isdir boolean
350      */

351     public final void notifyAttributesChanged(String JavaDoc path, boolean isdir)
352     {
353
354         // Check if file change notifications are enabled
355

356         if (getGlobalNotifyMask() == 0 || hasAttributeChange() == false)
357             return;
358
359         // Queue the change notification event
360

361         queueNotification(new NotifyChangeEvent(NotifyChange.Attributes, NotifyChange.ActionModified, path, isdir));
362     }
363
364     /**
365      * File size changed notification
366      *
367      * @param path String
368      */

369     public final void notifyFileSizeChanged(String JavaDoc path)
370     {
371
372         // Check if file change notifications are enabled
373

374         if (getGlobalNotifyMask() == 0 || hasFileSizeChange() == false)
375             return;
376
377         // Send the change notification
378

379         queueNotification(new NotifyChangeEvent(NotifyChange.Size, NotifyChange.ActionModified, path, false));
380     }
381
382     /**
383      * Last write time changed notification
384      *
385      * @param path String
386      * @param isdir boolean
387      */

388     public final void notifyLastWriteTimeChanged(String JavaDoc path, boolean isdir)
389     {
390
391         // Check if file change notifications are enabled
392

393         if (getGlobalNotifyMask() == 0 || hasFileWriteTimeChange() == false)
394             return;
395
396         // Send the change notification
397

398         queueNotification(new NotifyChangeEvent(NotifyChange.LastWrite, NotifyChange.ActionModified, path, isdir));
399     }
400
401     /**
402      * Last access time changed notification
403      *
404      * @param path String
405      * @param isdir boolean
406      */

407     public final void notifyLastAccessTimeChanged(String JavaDoc path, boolean isdir)
408     {
409
410         // Check if file change notifications are enabled
411

412         if (getGlobalNotifyMask() == 0 || hasFileAccessTimeChange() == false)
413             return;
414
415         // Send the change notification
416

417         queueNotification(new NotifyChangeEvent(NotifyChange.LastAccess, NotifyChange.ActionModified, path, isdir));
418     }
419
420     /**
421      * Creation time changed notification
422      *
423      * @param path String
424      * @param isdir boolean
425      */

426     public final void notifyCreationTimeChanged(String JavaDoc path, boolean isdir)
427     {
428
429         // Check if file change notifications are enabled
430

431         if (getGlobalNotifyMask() == 0 || hasFileCreateTimeChange() == false)
432             return;
433
434         // Send the change notification
435

436         queueNotification(new NotifyChangeEvent(NotifyChange.Creation, NotifyChange.ActionModified, path, isdir));
437     }
438
439     /**
440      * Security descriptor changed notification
441      *
442      * @param path String
443      * @param isdir boolean
444      */

445     public final void notifySecurityDescriptorChanged(String JavaDoc path, boolean isdir)
446     {
447
448         // Check if file change notifications are enabled
449

450         if (getGlobalNotifyMask() == 0 || hasSecurityDescriptorChange() == false)
451             return;
452
453         // Send the change notification
454

455         queueNotification(new NotifyChangeEvent(NotifyChange.Security, NotifyChange.ActionModified, path, isdir));
456     }
457
458     /**
459      * Enable debug output
460      *
461      * @param ena boolean
462      */

463     public final void setDebug(boolean ena)
464     {
465         m_debug = ena;
466     }
467
468     /**
469      * Shutdown the change notification processing thread
470      */

471     public final void shutdownRequest()
472     {
473
474         // Check if the processing thread is valid
475

476         if (m_procThread != null)
477         {
478
479             // Set the shutdown flag
480

481             m_shutdown = true;
482
483             // Wakeup the processing thread
484

485             synchronized (m_eventList)
486             {
487                 m_eventList.notifyAll();
488             }
489         }
490     }
491
492     /**
493      * Send buffered change notifications for a session
494      *
495      * @param req NotifyRequest
496      * @param evtList NotifyChangeEventList
497      */

498     public final void sendBufferedNotifications(NotifyRequest req, NotifyChangeEventList evtList)
499     {
500
501         // DEBUG
502

503         if (logger.isDebugEnabled() && hasDebug())
504             logger.debug("Send buffered notifications, req=" + req + ", evtList="
505                     + (evtList != null ? "" + evtList.numberOfEvents() : "null"));
506
507         // Initialize the notification request timeout
508

509         long tmo = System.currentTimeMillis() + NotifyRequest.DefaultRequestTimeout;
510
511         // Allocate the NT transaction packet to send the asynchronous notification
512

513         NTTransPacket ntpkt = new NTTransPacket();
514
515         // Build the change notification response SMB
516

517         ntpkt.setParameterCount(18);
518         ntpkt.resetBytePointerAlign();
519
520         int pos = ntpkt.getPosition();
521         ntpkt.setNTParameter(1, 0); // total data count
522
ntpkt.setNTParameter(3, pos - 4); // offset to parameter block
523

524         // Check if the notify enum status is set
525

526         if (req.hasNotifyEnum())
527         {
528
529             // Set the parameter block length
530

531             ntpkt.setNTParameter(0, 0); // total parameter block count
532
ntpkt.setNTParameter(2, 0); // parameter block count for this packet
533
ntpkt.setNTParameter(6, pos - 4); // data block offset
534
ntpkt.setByteCount();
535
536             ntpkt.setCommand(PacketType.NTTransact);
537
538             ntpkt.setFlags(SMBSrvPacket.FLG_CANONICAL + SMBSrvPacket.FLG_CASELESS);
539             ntpkt.setFlags2(SMBSrvPacket.FLG2_UNICODE + SMBSrvPacket.FLG2_LONGERRORCODE);
540
541             // Set the notification request id to indicate that it has completed
542

543             req.setCompleted(true, tmo);
544             req.setNotifyEnum(false);
545
546             // Set the response for the current notify request
547

548             ntpkt.setMultiplexId(req.getMultiplexId());
549             ntpkt.setTreeId(req.getTreeId());
550             ntpkt.setUserId(req.getUserId());
551             ntpkt.setProcessId(req.getProcessId());
552
553             try
554             {
555
556                 // Send the response to the current session
557

558                 if (req.getSession().sendAsynchResponseSMB(ntpkt, ntpkt.getLength()) == false)
559                 {
560
561                     // Asynchronous request was queued, clone the request packet
562

563                     ntpkt = new NTTransPacket(ntpkt);
564                 }
565             }
566             catch (Exception JavaDoc ex)
567             {
568             }
569         }
570         else if (evtList != null)
571         {
572
573             // Pack the change notification events
574

575             for (int i = 0; i < evtList.numberOfEvents(); i++)
576             {
577
578                 // Get the current event from the list
579

580                 NotifyChangeEvent evt = evtList.getEventAt(i);
581
582                 // Get the relative file name for the event
583

584                 String JavaDoc relName = FileName.makeRelativePath(req.getWatchPath(), evt.getFileName());
585                 if (relName == null)
586                     relName = evt.getShortFileName();
587
588                 // DEBUG
589

590                 if (logger.isDebugEnabled() && hasDebug())
591                     logger.debug(" Notify evtPath=" + evt.getFileName() + ", reqPath=" + req.getWatchPath()
592                             + ", relative=" + relName);
593
594                 // Pack the notification structure
595

596                 ntpkt.packInt(0); // offset to next structure
597
ntpkt.packInt(evt.getAction()); // action
598
ntpkt.packInt(relName.length() * 2); // file name length
599
ntpkt.packString(relName, true, false);
600
601                 // Check if the event is a file/directory rename, if so then add the old
602
// file/directory details
603

604                 if (evt.getAction() == NotifyChange.ActionRenamedNewName && evt.hasOldFileName())
605                 {
606
607                     // Set the offset from the first structure to this structure
608

609                     int newPos = DataPacker.longwordAlign(ntpkt.getPosition());
610                     DataPacker.putIntelInt(newPos - pos, ntpkt.getBuffer(), pos);
611
612                     // Get the old file name
613

614                     relName = FileName.makeRelativePath(req.getWatchPath(), evt.getOldFileName());
615                     if (relName == null)
616                         relName = evt.getOldFileName();
617
618                     // Add the old file/directory name details
619

620                     ntpkt.packInt(0); // offset to next structure
621
ntpkt.packInt(NotifyChange.ActionRenamedOldName);
622                     ntpkt.packInt(relName.length() * 2); // file name length
623
ntpkt.packString(relName, true, false);
624                 }
625
626                 // Calculate the parameter block length, longword align the buffer position
627

628                 int prmLen = ntpkt.getPosition() - pos;
629                 ntpkt.alignBytePointer();
630                 pos = (pos + 3) & 0xFFFFFFFC;
631
632                 // Set the parameter block length
633

634                 ntpkt.setNTParameter(0, prmLen); // total parameter block count
635
ntpkt.setNTParameter(2, prmLen); // parameter block count for this packet
636
ntpkt.setNTParameter(6, ntpkt.getPosition() - 4);
637                 // data block offset
638
ntpkt.setByteCount();
639
640                 ntpkt.setCommand(PacketType.NTTransact);
641
642                 ntpkt.setFlags(SMBSrvPacket.FLG_CANONICAL + SMBSrvPacket.FLG_CASELESS);
643                 ntpkt.setFlags2(SMBSrvPacket.FLG2_UNICODE + SMBSrvPacket.FLG2_LONGERRORCODE);
644
645                 // Set the notification request id to indicate that it has completed
646

647                 req.setCompleted(true, tmo);
648
649                 // Set the response for the current notify request
650

651                 ntpkt.setMultiplexId(req.getMultiplexId());
652                 ntpkt.setTreeId(req.getTreeId());
653                 ntpkt.setUserId(req.getUserId());
654                 ntpkt.setProcessId(req.getProcessId());
655
656                 try
657                 {
658
659                     // Send the response to the current session
660

661                     if (req.getSession().sendAsynchResponseSMB(ntpkt, ntpkt.getLength()) == false)
662                     {
663
664                         // Asynchronous request was queued, clone the request packet
665

666                         ntpkt = new NTTransPacket(ntpkt);
667                     }
668                 }
669                 catch (Exception JavaDoc ex)
670                 {
671                 }
672             }
673         }
674
675         // DEBUG
676

677         if (logger.isDebugEnabled() && hasDebug())
678             logger.debug("sendBufferedNotifications() done");
679     }
680
681     /**
682      * Queue a change notification event for processing
683      *
684      * @param evt NotifyChangeEvent
685      */

686     protected final void queueNotification(NotifyChangeEvent evt)
687     {
688
689         // DEBUG
690

691         if (logger.isDebugEnabled() && hasDebug())
692             logger.debug("Queue notification event=" + evt.toString());
693
694         // Queue the notification event to the main notification handler thread
695

696         synchronized (m_eventList)
697         {
698
699             // Add the event to the list
700

701             m_eventList.addEvent(evt);
702
703             // Notify the processing thread that there are events to process
704

705             m_eventList.notifyAll();
706         }
707     }
708
709     /**
710      * Send change notifications to sessions with notification enabled that match the change event.
711      *
712      * @param evt NotifyChangeEvent
713      * @return int
714      */

715     protected final int sendChangeNotification(NotifyChangeEvent evt)
716     {
717
718         // DEBUG
719

720         if (logger.isDebugEnabled() && hasDebug())
721             logger.debug("sendChangeNotification event=" + evt);
722
723         // Get a list of notification requests that match the type/path
724

725         Vector JavaDoc<NotifyRequest> reqList = findMatchingRequests(evt.getFilter(), evt.getFileName(), evt.isDirectory());
726         if (reqList == null || reqList.size() == 0)
727             return 0;
728
729         // DEBUG
730

731         if (logger.isDebugEnabled() && hasDebug())
732             logger.debug(" Found " + reqList.size() + " matching change listeners");
733
734         // Initialize the notification request timeout
735

736         long tmo = System.currentTimeMillis() + NotifyRequest.DefaultRequestTimeout;
737
738         // Allocate the NT transaction packet to send the asynchronous notification
739

740         NTTransPacket ntpkt = new NTTransPacket();
741
742         // Send the notify response to each client in the list
743

744         for (int i = 0; i < reqList.size(); i++)
745         {
746
747             // Get the current request
748

749             NotifyRequest req = reqList.get(i);
750
751             // Build the change notification response SMB
752

753             ntpkt.setParameterCount(18);
754             ntpkt.resetBytePointerAlign();
755
756             int pos = ntpkt.getPosition();
757             ntpkt.setNTParameter(1, 0); // total data count
758
ntpkt.setNTParameter(3, pos - 4); // offset to parameter block
759

760             // Get the relative file name for the event
761

762             String JavaDoc relName = FileName.makeRelativePath(req.getWatchPath(), evt.getFileName());
763             if (relName == null)
764                 relName = evt.getShortFileName();
765
766             // DEBUG
767

768             if (logger.isDebugEnabled() && hasDebug())
769                 logger.debug(" Notify evtPath=" + evt.getFileName() + ", reqPath=" + req.getWatchPath()
770                         + ", relative=" + relName);
771
772             // Pack the notification structure
773

774             ntpkt.packInt(0); // offset to next structure
775
ntpkt.packInt(evt.getAction()); // action
776
ntpkt.packInt(relName.length() * 2); // file name length
777
ntpkt.packString(relName, true, false);
778
779             // Check if the event is a file/directory rename, if so then add the old file/directory
780
// details
781

782             if (evt.getAction() == NotifyChange.ActionRenamedNewName && evt.hasOldFileName())
783             {
784
785                 // Set the offset from the first structure to this structure
786

787                 int newPos = DataPacker.longwordAlign(ntpkt.getPosition());
788                 DataPacker.putIntelInt(newPos - pos, ntpkt.getBuffer(), pos);
789
790                 // Get the old file name
791

792                 relName = FileName.makeRelativePath(req.getWatchPath(), evt.getOldFileName());
793                 if (relName == null)
794                     relName = evt.getOldFileName();
795
796                 // Add the old file/directory name details
797

798                 ntpkt.packInt(0); // offset to next structure
799
ntpkt.packInt(NotifyChange.ActionRenamedOldName);
800                 ntpkt.packInt(relName.length() * 2); // file name length
801
ntpkt.packString(relName, true, false);
802             }
803
804             // Calculate the parameter block length, longword align the buffer position
805

806             int prmLen = ntpkt.getPosition() - pos;
807             ntpkt.alignBytePointer();
808             pos = (pos + 3) & 0xFFFFFFFC;
809
810             // Set the parameter block length
811

812             ntpkt.setNTParameter(0, prmLen); // total parameter block count
813
ntpkt.setNTParameter(2, prmLen); // parameter block count for this packet
814
ntpkt.setNTParameter(6, ntpkt.getPosition() - 4);
815             // data block offset
816
ntpkt.setByteCount();
817
818             ntpkt.setCommand(PacketType.NTTransact);
819
820             ntpkt.setFlags(SMBSrvPacket.FLG_CANONICAL + SMBSrvPacket.FLG_CASELESS);
821             ntpkt.setFlags2(SMBSrvPacket.FLG2_UNICODE + SMBSrvPacket.FLG2_LONGERRORCODE);
822
823             // Check if the request is already complete
824

825             if (req.isCompleted() == false)
826             {
827
828                 // Set the notification request id to indicate that it has completed
829

830                 req.setCompleted(true, tmo);
831
832                 // Set the response for the current notify request
833

834                 ntpkt.setMultiplexId(req.getMultiplexId());
835                 ntpkt.setTreeId(req.getTreeId());
836                 ntpkt.setUserId(req.getUserId());
837                 ntpkt.setProcessId(req.getProcessId());
838
839                 // DEBUG
840

841                 // ntpkt.DumpPacket();
842

843                 try
844                 {
845
846                     // Send the response to the current session
847

848                     if (req.getSession().sendAsynchResponseSMB(ntpkt, ntpkt.getLength()) == false)
849                     {
850
851                         // Asynchronous request was queued, clone the request packet
852

853                         ntpkt = new NTTransPacket(ntpkt);
854                     }
855                 }
856                 catch (Exception JavaDoc ex)
857                 {
858                     ex.printStackTrace();
859                 }
860             }
861             else
862             {
863
864                 // Buffer the event so it can be sent when the client resets the notify request
865

866                 req.addEvent(evt);
867
868                 // DEBUG
869

870                 if (logger.isDebugEnabled() && req.getSession().hasDebug(SMBSrvSession.DBG_NOTIFY))
871                     logger.debug("Buffered notify req=" + req + ", event=" + evt + ", sess="
872                             + req.getSession().getSessionId());
873             }
874
875             // Reset the notification pending flag for the session
876

877             req.getSession().setNotifyPending(false);
878
879             // DEBUG
880

881             if (logger.isDebugEnabled() && req.getSession().hasDebug(SMBSrvSession.DBG_NOTIFY))
882                 logger
883                         .debug("Asynch notify req=" + req + ", event=" + evt + ", sess="
884                                 + req.getSession().getUniqueId());
885         }
886
887         // DEBUG
888

889         if (logger.isDebugEnabled() && hasDebug())
890             logger.debug("sendChangeNotification() done");
891
892         // Return the count of matching requests
893

894         return reqList.size();
895     }
896
897     /**
898      * Find notify requests that match the type and path
899      *
900      * @param typ int
901      * @param path String
902      * @param isdir boolean
903      * @return Vector<NotifyRequest>
904      */

905     protected final synchronized Vector JavaDoc<NotifyRequest> findMatchingRequests(int typ, String JavaDoc path, boolean isdir)
906     {
907
908         // Create a vector to hold the matching requests
909

910         Vector JavaDoc<NotifyRequest> reqList = new Vector JavaDoc<NotifyRequest>();
911
912         // Normalise the path string
913

914         String JavaDoc matchPath = path.toUpperCase();
915
916         // Search for matching requests and remove them from the main request list
917

918         int idx = 0;
919         long curTime = System.currentTimeMillis();
920
921         boolean removedReq = false;
922
923         while (idx < m_notifyList.numberOfRequests())
924         {
925
926             // Get the current request
927

928             NotifyRequest curReq = m_notifyList.getRequest(idx);
929
930             // DEBUG
931

932             if (logger.isDebugEnabled() && hasDebug())
933                 logger.debug("findMatchingRequests() req=" + curReq.toString());
934
935             // Check if the request has expired
936

937             if (curReq.hasExpired(curTime))
938             {
939
940                 // Remove the request from the list
941

942                 m_notifyList.removeRequestAt(idx);
943
944                 // DEBUG
945

946                 if (logger.isDebugEnabled() && hasDebug())
947                 {
948                     logger.debug("Removed expired request req=" + curReq.toString());
949                     if (curReq.getBufferedEventList() != null)
950                     {
951                         NotifyChangeEventList bufList = curReq.getBufferedEventList();
952                         logger.debug(" Buffered events = " + bufList.numberOfEvents());
953                         for (int b = 0; b < bufList.numberOfEvents(); b++)
954                             logger.debug(" " + (b + 1) + ": " + bufList.getEventAt(b));
955                     }
956                 }
957
958                 // Indicate that q request has been removed from the queue, the global filter mask
959
// will need
960
// to be recalculated
961

962                 removedReq = true;
963
964                 // Restart the loop
965

966                 continue;
967             }
968
969             // Check if the request matches the filter
970

971             if (curReq.hasFilter(typ))
972             {
973
974                 // DEBUG
975

976                 if (logger.isDebugEnabled() && hasDebug())
977                     logger.debug(" hasFilter typ=" + typ + ", watchTree=" + curReq.hasWatchTree() + ", watchPath="
978                             + curReq.getWatchPath() + ", matchPath=" + matchPath + ", isDir=" + isdir);
979
980                 // Check if the path matches or is a subdirectory and the whole tree is being
981
// watched
982

983                 boolean wantReq = false;
984
985                 if (matchPath.length() == 0 && curReq.hasWatchTree())
986                     wantReq = true;
987                 else if (curReq.hasWatchTree() == true && matchPath.startsWith(curReq.getWatchPath()) == true)
988                     wantReq = true;
989                 else if (isdir == true && matchPath.compareTo(curReq.getWatchPath()) == 0)
990                     wantReq = true;
991                 else if (isdir == false)
992                 {
993
994                     // Strip the file name from the path and compare
995

996                     String JavaDoc[] paths = FileName.splitPath(matchPath);
997
998                     if (paths != null && paths[0] != null)
999                     {
1000
1001                        // Check if the directory part of the path is the directory being watched
1002

1003                        if (curReq.getWatchPath().equalsIgnoreCase(paths[0]))
1004                            wantReq = true;
1005                    }
1006                }
1007
1008                // Check if the request is required
1009

1010                if (wantReq == true)
1011                {
1012
1013                    // For all notify requests in the matching list we set the 'notify pending'
1014
// state on the associated SMB
1015
// session so that any socket writes on those sessions are synchronized until
1016
// the change notification
1017
// response has been sent.
1018

1019                    curReq.getSession().setNotifyPending(true);
1020
1021                    // Add the request to the matching list
1022

1023                    reqList.add(curReq);
1024
1025                    // DEBUG
1026

1027                    if (logger.isDebugEnabled() && hasDebug())
1028                        logger.debug(" Added request to matching list");
1029                }
1030            }
1031
1032            // Move to the next request in the list
1033

1034            idx++;
1035        }
1036
1037        // If requests were removed from the queue the global filter mask must be recalculated
1038

1039        if (removedReq == true)
1040            m_globalNotifyMask = m_notifyList.getGlobalFilter();
1041
1042        // Return the matching request list
1043

1044        return reqList;
1045    }
1046
1047    /**
1048     * Asynchronous change notification processing thread
1049     */

1050    public void run()
1051    {
1052
1053        // Loop until shutdown
1054

1055        while (m_shutdown == false)
1056        {
1057
1058            // Wait for some events to process
1059

1060            synchronized (m_eventList)
1061            {
1062                try
1063                {
1064                    m_eventList.wait();
1065                }
1066                catch (InterruptedException JavaDoc ex)
1067                {
1068                }
1069            }
1070
1071            // Check if the shutdown flag has been set
1072

1073            if (m_shutdown == true)
1074                break;
1075
1076            // Loop until all pending events have been processed
1077

1078            while (m_eventList.numberOfEvents() > 0)
1079            {
1080
1081                // Remove the event at the head of the queue
1082

1083                NotifyChangeEvent evt = null;
1084
1085                synchronized (m_eventList)
1086                {
1087                    evt = m_eventList.removeEventAt(0);
1088                }
1089
1090                // Check if the event is valid
1091

1092                if (evt == null)
1093                    break;
1094
1095                try
1096                {
1097
1098                    // Send out change notifications to clients that match the filter/path
1099

1100                    int cnt = sendChangeNotification(evt);
1101
1102                    // DEBUG
1103

1104                    if (logger.isDebugEnabled() && hasDebug())
1105                        logger.debug("Change notify event=" + evt.toString() + ", clients=" + cnt);
1106                }
1107                catch (Throwable JavaDoc ex)
1108                {
1109                    logger.error("NotifyChangeHandler thread", ex);
1110                }
1111            }
1112        }
1113
1114        // DEBUG
1115

1116        if (logger.isDebugEnabled() && hasDebug())
1117            logger.debug("NotifyChangeHandler thread exit");
1118    }
1119}
1120
Popular Tags