KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > core > internal > resources > refresh > win32 > Win32Monitor


1 /*******************************************************************************
2  * Copyright (c) 2002, 2006 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  * IBM - Initial API and implementation
10  *******************************************************************************/

11 package org.eclipse.core.internal.resources.refresh.win32;
12
13 import java.io.File JavaDoc;
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 /**
27  * A monitor that works on Win32 platforms. Provides simple notification of
28  * entire trees by reporting that the root of the tree has changed to depth
29  * DEPTH_INFINITE.
30  */

31 class Win32Monitor extends Job implements IRefreshMonitor {
32     private static final long RESCHEDULE_DELAY = 1000;
33
34     /**
35      * A ChainedHandle is a linked list of handles.
36      */

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 JavaDoc file;
62
63         public FileHandle(File JavaDoc 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); //$NON-NLS-1$
136
handleValue = Win32Natives.INVALID_HANDLE_VALUE;
137             }
138         }
139
140         private long createHandleValue(String JavaDoc 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 JavaDoc 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 JavaDoc 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 JavaDoc(getHandleValue()), this);
187                 setHandleValueArrays(createHandleArrays());
188             } else {
189                 close();
190             }
191         }
192
193         protected void postRefreshRequest(IResource resource) {
194             //native callback occurs even if resource was changed within workspace
195
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         /**
209          * @param resource
210          */

211         public LinkedResourceHandle(IResource resource) {
212             this.resource = resource;
213             createFileHandleChain();
214         }
215
216         protected void createFileHandleChain() {
217             fileHandleChain = new ArrayList(1);
218             File JavaDoc file = new File JavaDoc(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 JavaDoc DEBUG_PREFIX = "Win32RefreshMonitor: "; //$NON-NLS-1$
297
private static final int WAIT_FOR_MULTIPLE_OBJECTS_TIMEOUT = 300;
298     /**
299      * Any errors that have occurred
300      */

301     protected MultiStatus errors;
302     /**
303      * Arrays of handles, split evenly when the number of handles is larger
304      * than Win32Natives.MAXIMUM_WAIT_OBJECTS
305      */

306     protected long[][] fHandleValueArrays;
307     /**
308      * Mapping of handles (java.lang.Long) to absolute paths
309      * (java.lang.String).
310      */

311     protected Map fHandleValueToHandle;
312     protected IRefreshResult refreshResult;
313
314     /*
315      * Creates a new monitor. @param result A result that will recieve refresh
316      * callbacks and error notifications
317      */

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     /**
328      * Logs an exception
329      */

330     protected synchronized void addException(String JavaDoc message) {
331         if (errors == null) {
332             String JavaDoc 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     /*
339      * Splits the given array into arrays of length no greater than <code> max
340      * </code> . The lengths of the sub arrays differ in size by no more than
341      * one element. <p> Examples: <ul><li> If an array of size 11 is split
342      * with a max of 4, the resulting arrays are of size 4, 4, and 3. </li>
343      * <li> If an array of size 18 is split with a max of 5, the resulting
344      * arrays are of size 5, 5, 4, and 4. </li></ul>
345      */

346     private long[][] balancedSplit(final long[] array, final int max) {
347         int elementCount = array.length;
348         // want to handle [1, max] rather than [0, max)
349
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     /*
372      * Since the Win32Natives.WaitForMultipleObjects(...) method cannot accept
373      * more than a certain number of objects, we are forced to split the array
374      * of objects to monitor and monitor each one individually. <p> This method
375      * splits the list of handles into arrays no larger than
376      * Win32Natives.MAXIMUM_WAIT_OBJECTS. The arrays are balenced so that they
377      * differ in size by no more than one element.
378      */

379     protected long[][] createHandleArrays() {
380         long[] handles;
381         // synchronized: in order to protect the map during iteration
382
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 JavaDoc) 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: in order to protect the map during iteration
402
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     /*
417      * Answers arrays of handles. The handles are split evenly when the number
418      * of handles becomes larger than Win32Natives.MAXIMUM_WAIT_OBJECTS.
419      * @return long[][]
420      */

421     private long[][] getHandleValueArrays() {
422         return fHandleValueArrays;
423     }
424
425     /**
426      * Adds a resource to be monitored by this native monitor
427      */

428     public boolean monitor(IResource resource) {
429         IPath location = resource.getLocation();
430         if (location == null) {
431             // cannot monitor remotely managed containers
432
return false;
433         }
434         Handle handle = createHandle(resource);
435         // synchronized: handle creation must be atomic
436
synchronized (this) {
437             handle.open();
438         }
439         if (!handle.isOpen()) {
440             //ignore errors if we can't even create a handle on the resource
441
//it will fall back to polling anyway
442
errors = null;
443             return false;
444         }
445         //make sure the job is running
446
schedule(RESCHEDULE_DELAY);
447         if (RefreshManager.DEBUG)
448             System.out.println(DEBUG_PREFIX + " added monitor for: " + resource); //$NON-NLS-1$
449
return true;
450     }
451
452     /**
453      * Removes the handle from the <code>fHandleValueToHandle</code> map.
454      *
455      * @param handle
456      * a handle, not <code>null</code>
457      */

458     protected void removeHandle(Handle handle) {
459         List handles = new ArrayList(1);
460         handles.add(handle);
461         removeHandles(handles);
462     }
463
464     /**
465      * Removes all of the handles in the given collection from the <code>fHandleValueToHandle</code>
466      * map. If collections from the <code>fHandleValueToHandle</code> map are
467      * used, copy them before passing them in as this method modifies the
468      * <code>fHandleValueToHandle</code> map.
469      *
470      * @param handles
471      * a collection of handles, not <code>null</code>
472      */

473     private void removeHandles(Collection handles) {
474         // synchronized: protect the array, removal must be atomic
475
synchronized (this) {
476             for (Iterator i = handles.iterator(); i.hasNext();) {
477                 Handle handle = (Handle) i.next();
478                 fHandleValueToHandle.remove(new Long JavaDoc(handle.getHandleValue()));
479                 handle.destroy();
480             }
481             setHandleValueArrays(createHandleArrays());
482         }
483     }
484
485     /*
486      * @see java.lang.Runnable#run()
487      */

488     protected IStatus run(IProgressMonitor monitor) {
489         long start = -System.currentTimeMillis();
490         if (RefreshManager.DEBUG)
491             System.out.println(DEBUG_PREFIX + "job started."); //$NON-NLS-1$
492
try {
493             long[][] handleArrays = getHandleValueArrays();
494             monitor.beginTask(Messages.WM_beginTask, handleArrays.length);
495             // If changes occur to the list of handles,
496
// ignore them until the next time through the loop.
497
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"); //$NON-NLS-1$ //$NON-NLS-2$
508
}
509         //always reschedule the job - so it will come back after errors or cancelation
510
//make sure it doesn't hog more that 5% of CPU
511
long delay = Math.max(RESCHEDULE_DELAY, start * 30);
512         if (RefreshManager.DEBUG)
513             System.out.println(DEBUG_PREFIX + "rescheduling in: " + delay / 1000 + " seconds"); //$NON-NLS-1$ //$NON-NLS-2$
514
final Bundle bundle = Platform.getBundle(ResourcesPlugin.PI_RESOURCES);
515         //if the bundle is null then the framework has shutdown - just bail out completely (bug 98219)
516
if (bundle == null)
517             return Status.OK_STATUS;
518         //don't reschedule the job if the resources plugin has been shut down
519
if (bundle.getState() == Bundle.ACTIVE)
520             schedule(delay);
521         MultiStatus result = errors;
522         errors = null;
523         //just log native refresh failures
524
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     /* (non-Javadoc)
534      * @see org.eclipse.core.runtime.jobs.Job#shouldRun()
535      */

536     public boolean shouldRun() {
537         return !fHandleValueToHandle.isEmpty();
538     }
539
540     /*
541      * @see org.eclipse.core.resources.refresh.IRefreshMonitor#unmonitor(IContainer)
542      */

543     public void unmonitor(IResource resource) {
544         if (resource == null) {
545             // resource == null means stop monitoring all resources
546
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         //stop the job if there are no more handles
555
if (fHandleValueToHandle.isEmpty())
556             cancel();
557     }
558
559     /**
560      * Performs the native call to wait for notification on one of the given
561      * handles.
562      *
563      * @param handleValues
564      * an array of handles, it must contain no duplicates.
565      */

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             // nothing happened.
571
return;
572         }
573         if (index == Win32Natives.WAIT_FAILED) {
574             // we ran into a problem
575
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         // a change occurred
583
// WaitForMultipleObjects returns WAIT_OBJECT_0 + index
584
index -= Win32Natives.WAIT_OBJECT_0;
585         Handle handle = (Handle) fHandleValueToHandle.get(new Long JavaDoc(handleValues[index]));
586         if (handle != null)
587             handle.handleNotification();
588     }
589 }
590
Popular Tags