1 11 package org.eclipse.core.internal.resources.refresh.win32; 12 13 import java.io.File ; 14 import java.util.*; 15 import org.eclipse.core.internal.refresh.RefreshManager; 16 import org.eclipse.core.internal.utils.Messages; 17 import org.eclipse.core.resources.IResource; 18 import org.eclipse.core.resources.ResourcesPlugin; 19 import org.eclipse.core.resources.refresh.IRefreshMonitor; 20 import org.eclipse.core.resources.refresh.IRefreshResult; 21 import org.eclipse.core.runtime.*; 22 import org.eclipse.core.runtime.jobs.Job; 23 import org.eclipse.osgi.util.NLS; 24 import org.osgi.framework.Bundle; 25 26 31 class Win32Monitor extends Job implements IRefreshMonitor { 32 private static final long RESCHEDULE_DELAY = 1000; 33 34 37 protected abstract class ChainedHandle extends Handle { 38 private ChainedHandle next; 39 private ChainedHandle previous; 40 41 public abstract boolean exists(); 42 43 public ChainedHandle getNext() { 44 return next; 45 } 46 47 public ChainedHandle getPrevious() { 48 return previous; 49 } 50 51 public void setNext(ChainedHandle next) { 52 this.next = next; 53 } 54 55 public void setPrevious(ChainedHandle previous) { 56 this.previous = previous; 57 } 58 } 59 60 protected class FileHandle extends ChainedHandle { 61 private File file; 62 63 public FileHandle(File file) { 64 this.file = file; 65 } 66 67 public boolean exists() { 68 return file.exists(); 69 } 70 71 public void handleNotification() { 72 if (!isOpen()) 73 return; 74 ChainedHandle next = getNext(); 75 if (next != null) { 76 if (next.isOpen()) { 77 if (!next.exists()) { 78 if (next instanceof LinkedResourceHandle) { 79 next.close(); 80 LinkedResourceHandle linkedResourceHandle = (LinkedResourceHandle) next; 81 linkedResourceHandle.postRefreshRequest(); 82 } else { 83 next.close(); 84 } 85 ChainedHandle previous = getPrevious(); 86 if (previous != null) 87 previous.open(); 88 } 89 } else { 90 next.open(); 91 if (next.isOpen()) { 92 Handle previous = getPrevious(); 93 previous.close(); 94 if (next instanceof LinkedResourceHandle) 95 ((LinkedResourceHandle) next).postRefreshRequest(); 96 } 97 } 98 } 99 findNextChange(); 100 } 101 102 public void open() { 103 if (!isOpen()) { 104 Handle next = getNext(); 105 if (next != null && next.isOpen()) { 106 openHandleOn(file); 107 } else { 108 if (exists()) { 109 openHandleOn(file); 110 } 111 Handle previous = getPrevious(); 112 if (previous != null) { 113 previous.open(); 114 } 115 } 116 } 117 } 118 } 119 120 protected abstract class Handle { 121 protected long handleValue; 122 123 public Handle() { 124 handleValue = Win32Natives.INVALID_HANDLE_VALUE; 125 } 126 127 public void close() { 128 if (isOpen()) { 129 if (!Win32Natives.FindCloseChangeNotification(handleValue)) { 130 int error = Win32Natives.GetLastError(); 131 if (error != Win32Natives.ERROR_INVALID_HANDLE) 132 addException(NLS.bind(Messages.WM_errCloseHandle, Integer.toString(error))); 133 } 134 if (RefreshManager.DEBUG) 135 System.out.println(DEBUG_PREFIX + "removed handle: " + handleValue); handleValue = Win32Natives.INVALID_HANDLE_VALUE; 137 } 138 } 139 140 private long createHandleValue(String path, boolean monitorSubtree, int flags) { 141 long handle = Win32Natives.FindFirstChangeNotification(path, monitorSubtree, flags); 142 if (handle == Win32Natives.INVALID_HANDLE_VALUE) { 143 int error = Win32Natives.GetLastError(); 144 addException(NLS.bind(Messages.WM_errCreateHandle, path, Integer.toString(error))); 145 } 146 return handle; 147 } 148 149 public void destroy() { 150 close(); 151 } 152 153 protected void findNextChange() { 154 if (!Win32Natives.FindNextChangeNotification(handleValue)) { 155 int error = Win32Natives.GetLastError(); 156 if (error != Win32Natives.ERROR_INVALID_HANDLE && error != Win32Natives.ERROR_SUCCESS) { 157 addException(NLS.bind(Messages.WM_errFindChange, Integer.toString(error))); 158 } 159 removeHandle(this); 160 } 161 } 162 163 public long getHandleValue() { 164 return handleValue; 165 } 166 167 public abstract void handleNotification(); 168 169 public boolean isOpen() { 170 return handleValue != Win32Natives.INVALID_HANDLE_VALUE; 171 } 172 173 public abstract void open(); 174 175 protected void openHandleOn(File file) { 176 openHandleOn(file.getAbsolutePath(), false); 177 } 178 179 protected void openHandleOn(IResource resource) { 180 openHandleOn(resource.getLocation().toOSString(), true); 181 } 182 183 private void openHandleOn(String path, boolean subtree) { 184 setHandleValue(createHandleValue(path, subtree, Win32Natives.FILE_NOTIFY_CHANGE_FILE_NAME | Win32Natives.FILE_NOTIFY_CHANGE_DIR_NAME | Win32Natives.FILE_NOTIFY_CHANGE_LAST_WRITE | Win32Natives.FILE_NOTIFY_CHANGE_SIZE)); 185 if (isOpen()) { 186 fHandleValueToHandle.put(new Long (getHandleValue()), this); 187 setHandleValueArrays(createHandleArrays()); 188 } else { 189 close(); 190 } 191 } 192 193 protected void postRefreshRequest(IResource resource) { 194 if (!resource.isSynchronized(IResource.DEPTH_INFINITE)) 196 refreshResult.refresh(resource); 197 } 198 199 public void setHandleValue(long handleValue) { 200 this.handleValue = handleValue; 201 } 202 } 203 204 protected class LinkedResourceHandle extends ChainedHandle { 205 private List fileHandleChain; 206 private IResource resource; 207 208 211 public LinkedResourceHandle(IResource resource) { 212 this.resource = resource; 213 createFileHandleChain(); 214 } 215 216 protected void createFileHandleChain() { 217 fileHandleChain = new ArrayList(1); 218 File file = new File (resource.getLocation().toOSString()); 219 file = file.getParentFile(); 220 while (file != null) { 221 fileHandleChain.add(0, new FileHandle(file)); 222 file = file.getParentFile(); 223 } 224 int size = fileHandleChain.size(); 225 for (int i = 0; i < size; i++) { 226 ChainedHandle handle = (ChainedHandle) fileHandleChain.get(i); 227 handle.setPrevious((i > 0) ? (ChainedHandle) fileHandleChain.get(i - 1) : null); 228 handle.setNext((i + 1 < size) ? (ChainedHandle) fileHandleChain.get(i + 1) : this); 229 } 230 setPrevious((size > 0) ? (ChainedHandle) fileHandleChain.get(size - 1) : null); 231 } 232 233 public void destroy() { 234 super.destroy(); 235 for (Iterator i = fileHandleChain.iterator(); i.hasNext();) { 236 Handle handle = (Handle) i.next(); 237 handle.destroy(); 238 } 239 } 240 241 public boolean exists() { 242 IPath location = resource.getLocation(); 243 return location == null ? false : location.toFile().exists(); 244 } 245 246 public void handleNotification() { 247 if (isOpen()) { 248 postRefreshRequest(resource); 249 findNextChange(); 250 } 251 } 252 253 public void open() { 254 if (!isOpen()) { 255 if (exists()) { 256 openHandleOn(resource); 257 } 258 FileHandle handle = (FileHandle) getPrevious(); 259 if (handle != null && !handle.isOpen()) { 260 handle.open(); 261 } 262 } 263 } 264 265 public void postRefreshRequest() { 266 postRefreshRequest(resource); 267 } 268 } 269 270 protected class ResourceHandle extends Handle { 271 private IResource resource; 272 273 public ResourceHandle(IResource resource) { 274 super(); 275 this.resource = resource; 276 } 277 278 public IResource getResource() { 279 return resource; 280 } 281 282 public void handleNotification() { 283 if (isOpen()) { 284 postRefreshRequest(resource); 285 findNextChange(); 286 } 287 } 288 289 public void open() { 290 if (!isOpen()) { 291 openHandleOn(resource); 292 } 293 } 294 } 295 296 private static final String DEBUG_PREFIX = "Win32RefreshMonitor: "; private static final int WAIT_FOR_MULTIPLE_OBJECTS_TIMEOUT = 300; 298 301 protected MultiStatus errors; 302 306 protected long[][] fHandleValueArrays; 307 311 protected Map fHandleValueToHandle; 312 protected IRefreshResult refreshResult; 313 314 318 public Win32Monitor(IRefreshResult result) { 319 super(Messages.WM_jobName); 320 this.refreshResult = result; 321 setPriority(Job.DECORATE); 322 setSystem(true); 323 fHandleValueToHandle = new HashMap(1); 324 setHandleValueArrays(createHandleArrays()); 325 } 326 327 330 protected synchronized void addException(String message) { 331 if (errors == null) { 332 String msg = Messages.WM_errors; 333 errors = new MultiStatus(ResourcesPlugin.PI_RESOURCES, 1, msg, null); 334 } 335 errors.add(new Status(IStatus.ERROR, ResourcesPlugin.PI_RESOURCES, 1, message, null)); 336 } 337 338 346 private long[][] balancedSplit(final long[] array, final int max) { 347 int elementCount = array.length; 348 int subArrayCount = ((elementCount - 1) / max) + 1; 350 int subArrayBaseLength = elementCount / subArrayCount; 351 int overflow = elementCount % subArrayCount; 352 long[][] result = new long[subArrayCount][]; 353 int count = 0; 354 for (int i = 0; i < subArrayCount; i++) { 355 int subArrayLength = subArrayBaseLength + (overflow-- > 0 ? 1 : 0); 356 long[] subArray = new long[subArrayLength]; 357 for (int j = 0; j < subArrayLength; j++) { 358 subArray[j] = array[count++]; 359 } 360 result[i] = subArray; 361 } 362 return result; 363 } 364 365 private Handle createHandle(IResource resource) { 366 if (resource.isLinked()) 367 return new LinkedResourceHandle(resource); 368 return new ResourceHandle(resource); 369 } 370 371 379 protected long[][] createHandleArrays() { 380 long[] handles; 381 synchronized (fHandleValueToHandle) { 383 Set keys = fHandleValueToHandle.keySet(); 384 int size = keys.size(); 385 if (size == 0) { 386 return new long[0][0]; 387 } 388 handles = new long[size]; 389 int count = 0; 390 for (Iterator i = keys.iterator(); i.hasNext();) { 391 handles[count++] = ((Long ) i.next()).longValue(); 392 } 393 } 394 return balancedSplit(handles, Win32Natives.MAXIMUM_WAIT_OBJECTS); 395 } 396 397 private Handle getHandle(IResource resource) { 398 if (resource == null) { 399 return null; 400 } 401 synchronized (fHandleValueToHandle) { 403 for (Iterator i = fHandleValueToHandle.values().iterator(); i.hasNext();) { 404 Handle handle = (Handle) i.next(); 405 if (handle instanceof ResourceHandle) { 406 ResourceHandle resourceHandle = (ResourceHandle) handle; 407 if (resourceHandle.getResource().equals(resource)) { 408 return handle; 409 } 410 } 411 } 412 } 413 return null; 414 } 415 416 421 private long[][] getHandleValueArrays() { 422 return fHandleValueArrays; 423 } 424 425 428 public boolean monitor(IResource resource) { 429 IPath location = resource.getLocation(); 430 if (location == null) { 431 return false; 433 } 434 Handle handle = createHandle(resource); 435 synchronized (this) { 437 handle.open(); 438 } 439 if (!handle.isOpen()) { 440 errors = null; 443 return false; 444 } 445 schedule(RESCHEDULE_DELAY); 447 if (RefreshManager.DEBUG) 448 System.out.println(DEBUG_PREFIX + " added monitor for: " + resource); return true; 450 } 451 452 458 protected void removeHandle(Handle handle) { 459 List handles = new ArrayList(1); 460 handles.add(handle); 461 removeHandles(handles); 462 } 463 464 473 private void removeHandles(Collection handles) { 474 synchronized (this) { 476 for (Iterator i = handles.iterator(); i.hasNext();) { 477 Handle handle = (Handle) i.next(); 478 fHandleValueToHandle.remove(new Long (handle.getHandleValue())); 479 handle.destroy(); 480 } 481 setHandleValueArrays(createHandleArrays()); 482 } 483 } 484 485 488 protected IStatus run(IProgressMonitor monitor) { 489 long start = -System.currentTimeMillis(); 490 if (RefreshManager.DEBUG) 491 System.out.println(DEBUG_PREFIX + "job started."); try { 493 long[][] handleArrays = getHandleValueArrays(); 494 monitor.beginTask(Messages.WM_beginTask, handleArrays.length); 495 for (int i = 0, length = handleArrays.length; i < length; i++) { 498 if (monitor.isCanceled()) 499 return Status.CANCEL_STATUS; 500 waitForNotification(handleArrays[i]); 501 monitor.worked(1); 502 } 503 } finally { 504 monitor.done(); 505 start += System.currentTimeMillis(); 506 if (RefreshManager.DEBUG) 507 System.out.println(DEBUG_PREFIX + "job finished in: " + start + "ms"); } 509 long delay = Math.max(RESCHEDULE_DELAY, start * 30); 512 if (RefreshManager.DEBUG) 513 System.out.println(DEBUG_PREFIX + "rescheduling in: " + delay / 1000 + " seconds"); final Bundle bundle = Platform.getBundle(ResourcesPlugin.PI_RESOURCES); 515 if (bundle == null) 517 return Status.OK_STATUS; 518 if (bundle.getState() == Bundle.ACTIVE) 520 schedule(delay); 521 MultiStatus result = errors; 522 errors = null; 523 if (result != null && !result.isOK()) 525 ResourcesPlugin.getPlugin().getLog().log(result); 526 return Status.OK_STATUS; 527 } 528 529 protected void setHandleValueArrays(long[][] arrays) { 530 fHandleValueArrays = arrays; 531 } 532 533 536 public boolean shouldRun() { 537 return !fHandleValueToHandle.isEmpty(); 538 } 539 540 543 public void unmonitor(IResource resource) { 544 if (resource == null) { 545 synchronized (fHandleValueToHandle) { 547 removeHandles(new ArrayList(fHandleValueToHandle.values())); 548 } 549 } else { 550 Handle handle = getHandle(resource); 551 if (handle != null) 552 removeHandle(handle); 553 } 554 if (fHandleValueToHandle.isEmpty()) 556 cancel(); 557 } 558 559 566 private void waitForNotification(long[] handleValues) { 567 int handleCount = handleValues.length; 568 int index = Win32Natives.WaitForMultipleObjects(handleCount, handleValues, false, WAIT_FOR_MULTIPLE_OBJECTS_TIMEOUT); 569 if (index == Win32Natives.WAIT_TIMEOUT) { 570 return; 572 } 573 if (index == Win32Natives.WAIT_FAILED) { 574 int error = Win32Natives.GetLastError(); 576 if (error != Win32Natives.ERROR_INVALID_HANDLE && error != Win32Natives.ERROR_SUCCESS) { 577 addException(NLS.bind(Messages.WM_nativeErr, Integer.toString(error))); 578 refreshResult.monitorFailed(this, null); 579 } 580 return; 581 } 582 index -= Win32Natives.WAIT_OBJECT_0; 585 Handle handle = (Handle) fHandleValueToHandle.get(new Long (handleValues[index])); 586 if (handle != null) 587 handle.handleNotification(); 588 } 589 } 590 | Popular Tags |