1 17 package org.alfresco.filesys.smb.server.notify; 18 19 import java.util.Vector ; 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 35 public class NotifyChangeHandler implements Runnable 36 { 37 private static final Log logger = LogFactory.getLog("org.alfresco.smb.protocol"); 38 39 41 private NotifyRequestList m_notifyList; 42 private int m_globalNotifyMask; 43 44 46 private DiskDeviceContext m_diskCtx; 47 48 50 private Thread m_procThread; 51 52 54 private NotifyChangeEventList m_eventList; 55 56 58 private boolean m_debug = false; 59 60 62 private boolean m_shutdown; 63 64 69 public NotifyChangeHandler(DiskDeviceContext diskCtx) 70 { 71 72 74 m_diskCtx = diskCtx; 75 76 78 m_eventList = new NotifyChangeEventList(); 79 80 82 m_procThread = new Thread (this); 83 84 m_procThread.setDaemon(true); 85 m_procThread.setName("Notify_" + m_diskCtx.getDeviceName()); 86 87 m_procThread.start(); 88 } 89 90 95 public final void addNotifyRequest(NotifyRequest req) 96 { 97 98 100 if (m_notifyList == null) 101 m_notifyList = new NotifyRequestList(); 102 103 105 req.setDiskContext(m_diskCtx); 106 m_notifyList.addRequest(req); 107 108 110 m_globalNotifyMask = m_notifyList.getGlobalFilter(); 111 } 112 113 118 public final void removeNotifyRequest(NotifyRequest req) 119 { 120 removeNotifyRequest(req, true); 121 } 122 123 129 public final void removeNotifyRequest(NotifyRequest req, boolean updateMask) 130 { 131 132 134 if (m_notifyList == null) 135 return; 136 137 139 m_notifyList.removeRequest(req); 140 141 143 if (updateMask == true) 144 m_globalNotifyMask = m_notifyList.getGlobalFilter(); 145 } 146 147 152 public final void removeNotifyRequests(SMBSrvSession sess) 153 { 154 155 157 m_notifyList.removeAllRequestsForSession(sess); 158 159 161 m_globalNotifyMask = m_notifyList.getGlobalFilter(); 162 } 163 164 170 public final boolean hasFileNameChange() 171 { 172 return hasFilterFlag(NotifyChange.FileName); 173 } 174 175 181 public final boolean hasDirectoryNameChange() 182 { 183 return hasFilterFlag(NotifyChange.DirectoryName); 184 } 185 186 191 public final boolean hasAttributeChange() 192 { 193 return hasFilterFlag(NotifyChange.Attributes); 194 } 195 196 201 public final boolean hasFileSizeChange() 202 { 203 return hasFilterFlag(NotifyChange.Size); 204 } 205 206 211 public final boolean hasFileWriteTimeChange() 212 { 213 return hasFilterFlag(NotifyChange.LastWrite); 214 } 215 216 221 public final boolean hasFileAccessTimeChange() 222 { 223 return hasFilterFlag(NotifyChange.LastAccess); 224 } 225 226 231 public final boolean hasFileCreateTimeChange() 232 { 233 return hasFilterFlag(NotifyChange.Creation); 234 } 235 236 241 public final boolean hasSecurityDescriptorChange() 242 { 243 return hasFilterFlag(NotifyChange.Security); 244 } 245 246 251 public final boolean hasDebug() 252 { 253 return m_debug; 254 } 255 256 261 public final int getGlobalNotifyMask() 262 { 263 return m_globalNotifyMask; 264 } 265 266 271 public final int getRequestQueueSize() 272 { 273 return m_notifyList != null ? m_notifyList.numberOfRequests() : 0; 274 } 275 276 282 private final boolean hasFilterFlag(int flag) 283 { 284 return (m_globalNotifyMask & flag) != 0 ? true : false; 285 } 286 287 293 public final void notifyFileChanged(int action, String path) 294 { 295 296 298 if (getGlobalNotifyMask() == 0 || hasFileNameChange() == false) 299 return; 300 301 303 queueNotification(new NotifyChangeEvent(NotifyChange.FileName, action, path, false)); 304 } 305 306 312 public final void notifyRename(String oldName, String newName) 313 { 314 315 317 if (getGlobalNotifyMask() == 0 || (hasFileNameChange() == false && hasDirectoryNameChange() == false)) 318 return; 319 320 322 queueNotification(new NotifyChangeEvent(NotifyChange.FileName, NotifyChange.ActionRenamedNewName, oldName, 323 newName, false)); 324 } 325 326 332 public final void notifyDirectoryChanged(int action, String path) 333 { 334 335 337 if (getGlobalNotifyMask() == 0 || hasDirectoryNameChange() == false) 338 return; 339 340 342 queueNotification(new NotifyChangeEvent(NotifyChange.DirectoryName, action, path, true)); 343 } 344 345 351 public final void notifyAttributesChanged(String path, boolean isdir) 352 { 353 354 356 if (getGlobalNotifyMask() == 0 || hasAttributeChange() == false) 357 return; 358 359 361 queueNotification(new NotifyChangeEvent(NotifyChange.Attributes, NotifyChange.ActionModified, path, isdir)); 362 } 363 364 369 public final void notifyFileSizeChanged(String path) 370 { 371 372 374 if (getGlobalNotifyMask() == 0 || hasFileSizeChange() == false) 375 return; 376 377 379 queueNotification(new NotifyChangeEvent(NotifyChange.Size, NotifyChange.ActionModified, path, false)); 380 } 381 382 388 public final void notifyLastWriteTimeChanged(String path, boolean isdir) 389 { 390 391 393 if (getGlobalNotifyMask() == 0 || hasFileWriteTimeChange() == false) 394 return; 395 396 398 queueNotification(new NotifyChangeEvent(NotifyChange.LastWrite, NotifyChange.ActionModified, path, isdir)); 399 } 400 401 407 public final void notifyLastAccessTimeChanged(String path, boolean isdir) 408 { 409 410 412 if (getGlobalNotifyMask() == 0 || hasFileAccessTimeChange() == false) 413 return; 414 415 417 queueNotification(new NotifyChangeEvent(NotifyChange.LastAccess, NotifyChange.ActionModified, path, isdir)); 418 } 419 420 426 public final void notifyCreationTimeChanged(String path, boolean isdir) 427 { 428 429 431 if (getGlobalNotifyMask() == 0 || hasFileCreateTimeChange() == false) 432 return; 433 434 436 queueNotification(new NotifyChangeEvent(NotifyChange.Creation, NotifyChange.ActionModified, path, isdir)); 437 } 438 439 445 public final void notifySecurityDescriptorChanged(String path, boolean isdir) 446 { 447 448 450 if (getGlobalNotifyMask() == 0 || hasSecurityDescriptorChange() == false) 451 return; 452 453 455 queueNotification(new NotifyChangeEvent(NotifyChange.Security, NotifyChange.ActionModified, path, isdir)); 456 } 457 458 463 public final void setDebug(boolean ena) 464 { 465 m_debug = ena; 466 } 467 468 471 public final void shutdownRequest() 472 { 473 474 476 if (m_procThread != null) 477 { 478 479 481 m_shutdown = true; 482 483 485 synchronized (m_eventList) 486 { 487 m_eventList.notifyAll(); 488 } 489 } 490 } 491 492 498 public final void sendBufferedNotifications(NotifyRequest req, NotifyChangeEventList evtList) 499 { 500 501 503 if (logger.isDebugEnabled() && hasDebug()) 504 logger.debug("Send buffered notifications, req=" + req + ", evtList=" 505 + (evtList != null ? "" + evtList.numberOfEvents() : "null")); 506 507 509 long tmo = System.currentTimeMillis() + NotifyRequest.DefaultRequestTimeout; 510 511 513 NTTransPacket ntpkt = new NTTransPacket(); 514 515 517 ntpkt.setParameterCount(18); 518 ntpkt.resetBytePointerAlign(); 519 520 int pos = ntpkt.getPosition(); 521 ntpkt.setNTParameter(1, 0); ntpkt.setNTParameter(3, pos - 4); 524 526 if (req.hasNotifyEnum()) 527 { 528 529 531 ntpkt.setNTParameter(0, 0); ntpkt.setNTParameter(2, 0); ntpkt.setNTParameter(6, pos - 4); 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 543 req.setCompleted(true, tmo); 544 req.setNotifyEnum(false); 545 546 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 558 if (req.getSession().sendAsynchResponseSMB(ntpkt, ntpkt.getLength()) == false) 559 { 560 561 563 ntpkt = new NTTransPacket(ntpkt); 564 } 565 } 566 catch (Exception ex) 567 { 568 } 569 } 570 else if (evtList != null) 571 { 572 573 575 for (int i = 0; i < evtList.numberOfEvents(); i++) 576 { 577 578 580 NotifyChangeEvent evt = evtList.getEventAt(i); 581 582 584 String relName = FileName.makeRelativePath(req.getWatchPath(), evt.getFileName()); 585 if (relName == null) 586 relName = evt.getShortFileName(); 587 588 590 if (logger.isDebugEnabled() && hasDebug()) 591 logger.debug(" Notify evtPath=" + evt.getFileName() + ", reqPath=" + req.getWatchPath() 592 + ", relative=" + relName); 593 594 596 ntpkt.packInt(0); ntpkt.packInt(evt.getAction()); ntpkt.packInt(relName.length() * 2); ntpkt.packString(relName, true, false); 600 601 604 if (evt.getAction() == NotifyChange.ActionRenamedNewName && evt.hasOldFileName()) 605 { 606 607 609 int newPos = DataPacker.longwordAlign(ntpkt.getPosition()); 610 DataPacker.putIntelInt(newPos - pos, ntpkt.getBuffer(), pos); 611 612 614 relName = FileName.makeRelativePath(req.getWatchPath(), evt.getOldFileName()); 615 if (relName == null) 616 relName = evt.getOldFileName(); 617 618 620 ntpkt.packInt(0); ntpkt.packInt(NotifyChange.ActionRenamedOldName); 622 ntpkt.packInt(relName.length() * 2); ntpkt.packString(relName, true, false); 624 } 625 626 628 int prmLen = ntpkt.getPosition() - pos; 629 ntpkt.alignBytePointer(); 630 pos = (pos + 3) & 0xFFFFFFFC; 631 632 634 ntpkt.setNTParameter(0, prmLen); ntpkt.setNTParameter(2, prmLen); ntpkt.setNTParameter(6, ntpkt.getPosition() - 4); 637 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 647 req.setCompleted(true, tmo); 648 649 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 661 if (req.getSession().sendAsynchResponseSMB(ntpkt, ntpkt.getLength()) == false) 662 { 663 664 666 ntpkt = new NTTransPacket(ntpkt); 667 } 668 } 669 catch (Exception ex) 670 { 671 } 672 } 673 } 674 675 677 if (logger.isDebugEnabled() && hasDebug()) 678 logger.debug("sendBufferedNotifications() done"); 679 } 680 681 686 protected final void queueNotification(NotifyChangeEvent evt) 687 { 688 689 691 if (logger.isDebugEnabled() && hasDebug()) 692 logger.debug("Queue notification event=" + evt.toString()); 693 694 696 synchronized (m_eventList) 697 { 698 699 701 m_eventList.addEvent(evt); 702 703 705 m_eventList.notifyAll(); 706 } 707 } 708 709 715 protected final int sendChangeNotification(NotifyChangeEvent evt) 716 { 717 718 720 if (logger.isDebugEnabled() && hasDebug()) 721 logger.debug("sendChangeNotification event=" + evt); 722 723 725 Vector <NotifyRequest> reqList = findMatchingRequests(evt.getFilter(), evt.getFileName(), evt.isDirectory()); 726 if (reqList == null || reqList.size() == 0) 727 return 0; 728 729 731 if (logger.isDebugEnabled() && hasDebug()) 732 logger.debug(" Found " + reqList.size() + " matching change listeners"); 733 734 736 long tmo = System.currentTimeMillis() + NotifyRequest.DefaultRequestTimeout; 737 738 740 NTTransPacket ntpkt = new NTTransPacket(); 741 742 744 for (int i = 0; i < reqList.size(); i++) 745 { 746 747 749 NotifyRequest req = reqList.get(i); 750 751 753 ntpkt.setParameterCount(18); 754 ntpkt.resetBytePointerAlign(); 755 756 int pos = ntpkt.getPosition(); 757 ntpkt.setNTParameter(1, 0); ntpkt.setNTParameter(3, pos - 4); 760 762 String relName = FileName.makeRelativePath(req.getWatchPath(), evt.getFileName()); 763 if (relName == null) 764 relName = evt.getShortFileName(); 765 766 768 if (logger.isDebugEnabled() && hasDebug()) 769 logger.debug(" Notify evtPath=" + evt.getFileName() + ", reqPath=" + req.getWatchPath() 770 + ", relative=" + relName); 771 772 774 ntpkt.packInt(0); ntpkt.packInt(evt.getAction()); ntpkt.packInt(relName.length() * 2); ntpkt.packString(relName, true, false); 778 779 782 if (evt.getAction() == NotifyChange.ActionRenamedNewName && evt.hasOldFileName()) 783 { 784 785 787 int newPos = DataPacker.longwordAlign(ntpkt.getPosition()); 788 DataPacker.putIntelInt(newPos - pos, ntpkt.getBuffer(), pos); 789 790 792 relName = FileName.makeRelativePath(req.getWatchPath(), evt.getOldFileName()); 793 if (relName == null) 794 relName = evt.getOldFileName(); 795 796 798 ntpkt.packInt(0); ntpkt.packInt(NotifyChange.ActionRenamedOldName); 800 ntpkt.packInt(relName.length() * 2); ntpkt.packString(relName, true, false); 802 } 803 804 806 int prmLen = ntpkt.getPosition() - pos; 807 ntpkt.alignBytePointer(); 808 pos = (pos + 3) & 0xFFFFFFFC; 809 810 812 ntpkt.setNTParameter(0, prmLen); ntpkt.setNTParameter(2, prmLen); ntpkt.setNTParameter(6, ntpkt.getPosition() - 4); 815 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 825 if (req.isCompleted() == false) 826 { 827 828 830 req.setCompleted(true, tmo); 831 832 834 ntpkt.setMultiplexId(req.getMultiplexId()); 835 ntpkt.setTreeId(req.getTreeId()); 836 ntpkt.setUserId(req.getUserId()); 837 ntpkt.setProcessId(req.getProcessId()); 838 839 841 843 try 844 { 845 846 848 if (req.getSession().sendAsynchResponseSMB(ntpkt, ntpkt.getLength()) == false) 849 { 850 851 853 ntpkt = new NTTransPacket(ntpkt); 854 } 855 } 856 catch (Exception ex) 857 { 858 ex.printStackTrace(); 859 } 860 } 861 else 862 { 863 864 866 req.addEvent(evt); 867 868 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 877 req.getSession().setNotifyPending(false); 878 879 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 889 if (logger.isDebugEnabled() && hasDebug()) 890 logger.debug("sendChangeNotification() done"); 891 892 894 return reqList.size(); 895 } 896 897 905 protected final synchronized Vector <NotifyRequest> findMatchingRequests(int typ, String path, boolean isdir) 906 { 907 908 910 Vector <NotifyRequest> reqList = new Vector <NotifyRequest>(); 911 912 914 String matchPath = path.toUpperCase(); 915 916 918 int idx = 0; 919 long curTime = System.currentTimeMillis(); 920 921 boolean removedReq = false; 922 923 while (idx < m_notifyList.numberOfRequests()) 924 { 925 926 928 NotifyRequest curReq = m_notifyList.getRequest(idx); 929 930 932 if (logger.isDebugEnabled() && hasDebug()) 933 logger.debug("findMatchingRequests() req=" + curReq.toString()); 934 935 937 if (curReq.hasExpired(curTime)) 938 { 939 940 942 m_notifyList.removeRequestAt(idx); 943 944 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 962 removedReq = true; 963 964 966 continue; 967 } 968 969 971 if (curReq.hasFilter(typ)) 972 { 973 974 976 if (logger.isDebugEnabled() && hasDebug()) 977 logger.debug(" hasFilter typ=" + typ + ", watchTree=" + curReq.hasWatchTree() + ", watchPath=" 978 + curReq.getWatchPath() + ", matchPath=" + matchPath + ", isDir=" + isdir); 979 980 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 996 String [] paths = FileName.splitPath(matchPath); 997 998 if (paths != null && paths[0] != null) 999 { 1000 1001 1003 if (curReq.getWatchPath().equalsIgnoreCase(paths[0])) 1004 wantReq = true; 1005 } 1006 } 1007 1008 1010 if (wantReq == true) 1011 { 1012 1013 1019 curReq.getSession().setNotifyPending(true); 1020 1021 1023 reqList.add(curReq); 1024 1025 1027 if (logger.isDebugEnabled() && hasDebug()) 1028 logger.debug(" Added request to matching list"); 1029 } 1030 } 1031 1032 1034 idx++; 1035 } 1036 1037 1039 if (removedReq == true) 1040 m_globalNotifyMask = m_notifyList.getGlobalFilter(); 1041 1042 1044 return reqList; 1045 } 1046 1047 1050 public void run() 1051 { 1052 1053 1055 while (m_shutdown == false) 1056 { 1057 1058 1060 synchronized (m_eventList) 1061 { 1062 try 1063 { 1064 m_eventList.wait(); 1065 } 1066 catch (InterruptedException ex) 1067 { 1068 } 1069 } 1070 1071 1073 if (m_shutdown == true) 1074 break; 1075 1076 1078 while (m_eventList.numberOfEvents() > 0) 1079 { 1080 1081 1083 NotifyChangeEvent evt = null; 1084 1085 synchronized (m_eventList) 1086 { 1087 evt = m_eventList.removeEventAt(0); 1088 } 1089 1090 1092 if (evt == null) 1093 break; 1094 1095 try 1096 { 1097 1098 1100 int cnt = sendChangeNotification(evt); 1101 1102 1104 if (logger.isDebugEnabled() && hasDebug()) 1105 logger.debug("Change notify event=" + evt.toString() + ", clients=" + cnt); 1106 } 1107 catch (Throwable ex) 1108 { 1109 logger.error("NotifyChangeHandler thread", ex); 1110 } 1111 } 1112 } 1113 1114 1116 if (logger.isDebugEnabled() && hasDebug()) 1117 logger.debug("NotifyChangeHandler thread exit"); 1118 } 1119} 1120 | Popular Tags |