1 11 package org.eclipse.team.internal.ui.synchronize; 12 13 import java.util.ArrayList ; 14 import java.util.List ; 15 16 import org.eclipse.core.resources.IResource; 17 import org.eclipse.core.resources.ResourcesPlugin; 18 import org.eclipse.core.runtime.*; 19 import org.eclipse.core.runtime.jobs.*; 20 import org.eclipse.jface.action.IAction; 21 import org.eclipse.jface.dialogs.ErrorDialog; 22 import org.eclipse.jface.util.IPropertyChangeListener; 23 import org.eclipse.jface.util.PropertyChangeEvent; 24 import org.eclipse.osgi.util.NLS; 25 import org.eclipse.team.core.TeamException; 26 import org.eclipse.team.core.subscribers.Subscriber; 27 import org.eclipse.team.core.synchronize.SyncInfo; 28 import org.eclipse.team.core.synchronize.SyncInfoTree; 29 import org.eclipse.team.internal.core.Assert; 30 import org.eclipse.team.internal.core.subscribers.SubscriberSyncInfoCollector; 31 import org.eclipse.team.internal.ui.*; 32 import org.eclipse.team.ui.synchronize.ISynchronizeManager; 33 import org.eclipse.team.ui.synchronize.SubscriberParticipant; 34 import org.eclipse.ui.actions.ActionFactory; 35 import org.eclipse.ui.progress.IProgressConstants; 36 import org.eclipse.ui.progress.UIJob; 37 38 51 public final class RefreshSubscriberJob extends Job { 52 53 private final static boolean TEST_PROGRESS_VIEW = false; 54 57 private final static Object FAMILY_ID = new Object (); 58 59 62 private boolean reschedule = false; 63 64 67 private boolean restartOnCancel = true; 68 69 72 private static long scheduleDelay; 73 74 77 private IResource[] resources; 78 79 82 private SubscriberParticipant participant; 83 84 88 private String taskName; 89 90 93 private static List listeners = new ArrayList (1); 94 private static final int STARTED = 1; 95 private static final int DONE = 2; 96 97 100 private static final ILock lock = Platform.getJobManager().newLock(); 101 102 105 private static final IStatus POSTPONED = new Status(IStatus.CANCEL, TeamUIPlugin.ID, 0, "Scheduled refresh postponed due to conflicting operation", null); 107 112 private final class GotoActionWrapper extends WorkbenchAction { 113 private ActionFactory.IWorkbenchAction gotoAction; 114 private IStatus status; 115 public void run() { 116 if (status != null && !status.isOK()) { 117 ErrorDialog.openError(Utils.getShell(null), null, TeamUIMessages.RefreshSubscriberJob_3, status); } else if(gotoAction != null) { 119 gotoAction.run(); 120 } 121 } 122 public boolean isEnabled() { 123 if(gotoAction != null) { 124 return gotoAction.isEnabled(); 125 } 126 return true; 127 } 128 public String getText() { 129 if(gotoAction != null) { 130 return gotoAction.getText(); 131 } 132 return null; 133 } 134 public String getToolTipText() { 135 if (status != null && !status.isOK()) { 136 return status.getMessage(); 137 } 138 if(gotoAction != null) { 139 return gotoAction.getToolTipText(); 140 } 141 return Utils.shortenText(SynchronizeView.MAX_NAME_LENGTH, RefreshSubscriberJob.this.getName()); 142 } 143 public void dispose() { 144 super.dispose(); 145 if(gotoAction != null) { 146 gotoAction.dispose(); 147 } 148 } 149 public void setGotoAction(ActionFactory.IWorkbenchAction gotoAction) { 150 this.gotoAction = gotoAction; 151 setEnabled(isEnabled()); 152 setToolTipText(getToolTipText()); 153 gotoAction.addPropertyChangeListener(new IPropertyChangeListener() { 154 public void propertyChange(PropertyChangeEvent event) { 155 if(event.getProperty().equals(IAction.ENABLED)) { 156 Boolean bool = (Boolean ) event.getNewValue(); 157 GotoActionWrapper.this.setEnabled(bool.booleanValue()); 158 } 159 } 160 }); 161 } 162 public void setStatus(IStatus status) { 163 this.status = status; 164 } 165 } 166 167 170 private abstract class Notification implements ISafeRunnable { 171 private IRefreshSubscriberListener listener; 172 public void handleException(Throwable exception) { 173 } 175 public void run(IRefreshSubscriberListener listener) { 176 this.listener = listener; 177 Platform.run(this); 178 } 179 public void run() throws Exception { 180 notify(listener); 181 } 182 186 protected abstract void notify(IRefreshSubscriberListener listener); 187 } 188 189 193 private class NonblockingProgressMonitor extends ProgressMonitorWrapper { 194 private final RefreshSubscriberJob job; 195 private long blockTime; 196 private static final int THRESHOLD = 250; 197 private boolean wasBlocking = false; 198 protected NonblockingProgressMonitor(IProgressMonitor monitor, RefreshSubscriberJob job) { 199 super(monitor); 200 this.job = job; 201 } 202 public boolean isCanceled() { 203 if (super.isCanceled()) { 204 return true; 205 } 206 if (job.shouldReschedule() && job.isBlocking()) { 207 if (blockTime == 0) { 208 blockTime = System.currentTimeMillis(); 209 } else if (System.currentTimeMillis() - blockTime > THRESHOLD) { 210 wasBlocking = true; 212 return true; 213 } 214 } else { 215 blockTime = 0; 216 } 217 wasBlocking = false; 218 return false; 219 } 220 public boolean wasBlocking() { 221 return wasBlocking; 222 } 223 } 224 225 233 public RefreshSubscriberJob(SubscriberParticipant participant, String jobName, String taskName, IResource[] resources, IRefreshSubscriberListener listener) { 234 super(taskName); 235 Assert.isNotNull(resources); 236 Assert.isNotNull(participant); 237 Assert.isNotNull(resources); 238 this.resources = resources; 239 this.participant = participant; 240 this.taskName = jobName; 241 setPriority(Job.DECORATE); 242 setRefreshInterval(3600 ); 243 244 addJobChangeListener(new JobChangeAdapter() { 246 public void done(IJobChangeEvent event) { 247 if(shouldReschedule()) { 248 IStatus result = event.getResult(); 249 if(result.getSeverity() == IStatus.CANCEL && ! restartOnCancel) { 250 return; 251 } 252 long delay = scheduleDelay; 253 if (result == POSTPONED) { 254 delay = 5000; 256 } 257 RefreshSubscriberJob.this.schedule(delay); 258 restartOnCancel = true; 259 } 260 } 261 }); 262 if(listener != null) 263 initialize(listener); 264 } 265 266 270 public boolean shouldRun() { 271 return getSubscriber() != null; 273 } 274 275 public boolean belongsTo(Object family) { 276 if(family instanceof RefreshSubscriberJob) { 277 return ((RefreshSubscriberJob)family).getSubscriber() == getSubscriber(); 278 } else if (family instanceof SubscriberParticipant) { 279 return family == participant; 280 } else { 281 return (family == getFamily() || family == ISynchronizeManager.FAMILY_SYNCHRONIZE_OPERATION); 282 } 283 } 284 285 public static Object getFamily() { 286 return FAMILY_ID; 287 } 288 289 293 public IStatus run(IProgressMonitor monitor) { 294 if (shouldReschedule() && 297 (isJobInFamilyRunning(ResourcesPlugin.FAMILY_AUTO_BUILD) 298 || isJobInFamilyRunning(ResourcesPlugin.FAMILY_MANUAL_BUILD))) { 299 return POSTPONED; 300 } 301 boolean acquired = false; 307 try { 308 while (!acquired) { 309 try { 310 acquired = lock.acquire(1000); 311 } catch (InterruptedException e1) { 312 acquired = false; 313 } 314 Policy.checkCanceled(monitor); 315 } 316 Subscriber subscriber = getSubscriber(); 317 IResource[] roots = getResources(); 318 319 if(subscriber == null || roots == null) { 321 return Status.OK_STATUS; 322 } 323 SubscriberSyncInfoCollector collector = getCollector(); 324 RefreshEvent event = new RefreshEvent(reschedule ? IRefreshEvent.SCHEDULED_REFRESH : IRefreshEvent.USER_REFRESH, roots, collector.getSubscriber()); 325 RefreshChangeListener changeListener = new RefreshChangeListener(collector); 326 IStatus status = null; 327 NonblockingProgressMonitor wrappedMonitor = null; 328 try { 329 event.setStartTime(System.currentTimeMillis()); 330 if(monitor.isCanceled()) { 331 return Status.CANCEL_STATUS; 332 } 333 subscriber.addListener(changeListener); 336 notifyListeners(STARTED, event); 338 monitor.setTaskName(getName()); 340 wrappedMonitor = new NonblockingProgressMonitor(monitor, this); 341 subscriber.refresh(roots, IResource.DEPTH_INFINITE, wrappedMonitor); 342 setProperty(IProgressConstants.KEEPONE_PROPERTY, Boolean.valueOf(! isJobModal())); 344 } catch(OperationCanceledException e2) { 345 if (monitor.isCanceled()) { 346 status = Status.CANCEL_STATUS; 348 } else { 349 if (wrappedMonitor != null && wrappedMonitor.wasBlocking()) { 351 status = POSTPONED; 352 } else { 353 status = Status.CANCEL_STATUS; 354 } 355 } 356 } catch(TeamException e) { 357 status = e.getStatus(); 359 if (!isUser()) { 360 if (!TEST_PROGRESS_VIEW) { 361 Object prop = getProperty(IProgressConstants.ACTION_PROPERTY); 363 if (prop instanceof GotoActionWrapper) { 364 GotoActionWrapper wrapper = (GotoActionWrapper)prop; 365 wrapper.setStatus(e.getStatus()); 366 status = new Status(IStatus.OK, TeamUIPlugin.ID, IStatus.OK, e.getStatus().getMessage(), e); 367 } 368 } 369 } 370 } finally { 376 event.setStopTime(System.currentTimeMillis()); 377 subscriber.removeListener(changeListener); 378 } 379 380 event.setChanges(changeListener.getChanges(monitor)); 382 if (status == null) { 383 status = calculateStatus(event); 384 } 385 event.setStatus(status); 386 notifyListeners(DONE, event); 387 return event.getStatus(); 388 } finally { 389 if (acquired) lock.release(); 390 monitor.done(); 391 } 392 } 393 394 private boolean isJobInFamilyRunning(Object family) { 395 Job[] jobs = Platform.getJobManager().find(family); 396 if (jobs != null && jobs.length > 0) { 397 for (int i = 0; i < jobs.length; i++) { 398 Job job = jobs[i]; 399 if (job.getState() != Job.NONE) { 400 return true; 401 } 402 } 403 } 404 return false; 405 } 406 407 private IStatus calculateStatus(IRefreshEvent event) { 408 StringBuffer text = new StringBuffer (); 409 int code = IStatus.OK; 410 SyncInfo[] changes = event.getChanges(); 411 SubscriberSyncInfoCollector collector = getCollector(); 412 if (collector != null) { 413 int numChanges = refreshedResourcesContainChanges(event); 414 if (numChanges > 0) { 415 code = IRefreshEvent.STATUS_CHANGES; 416 if (changes.length > 0) { 417 String numNewChanges = Integer.toString(event.getChanges().length); 419 if (event.getChanges().length == 1) { 420 text.append(NLS.bind(TeamUIMessages.RefreshCompleteDialog_newChangesSingular, (new Object []{getName(), numNewChanges}))); } else { 422 text.append(NLS.bind(TeamUIMessages.RefreshCompleteDialog_newChangesPlural, (new Object []{getName(), numNewChanges}))); } 424 } else { 425 if (numChanges == 1) { 427 text.append(NLS.bind(TeamUIMessages.RefreshCompleteDialog_changesSingular, (new Object []{getName(), new Integer (numChanges)}))); } else { 429 text.append(NLS.bind(TeamUIMessages.RefreshCompleteDialog_changesPlural, (new Object []{getName(), new Integer (numChanges)}))); } 431 } 432 } else { 433 code = IRefreshEvent.STATUS_NO_CHANGES; 435 text.append(NLS.bind(TeamUIMessages.RefreshCompleteDialog_6, new String [] { getName() })); } 437 return new Status(IStatus.OK, TeamUIPlugin.ID, code, text.toString(), null); 438 } 439 return Status.OK_STATUS; 440 } 441 442 private int refreshedResourcesContainChanges(IRefreshEvent event) { 443 int numChanges = 0; 444 SubscriberSyncInfoCollector collector = getCollector(); 445 if (collector != null) { 446 SyncInfoTree set = collector.getSyncInfoSet(); 447 IResource[] resources = event.getResources(); 448 for (int i = 0; i < resources.length; i++) { 449 IResource resource = resources[i]; 450 SyncInfo[] infos = set.getSyncInfos(resource, IResource.DEPTH_INFINITE); 451 if(infos != null && infos.length > 0) { 452 numChanges += infos.length; 453 } 454 } 455 } 456 return numChanges; 457 } 458 459 private void initialize(final IRefreshSubscriberListener listener) { 460 final GotoActionWrapper actionWrapper = new GotoActionWrapper(); 461 462 IProgressMonitor group = Platform.getJobManager().createProgressGroup(); 463 group.beginTask(taskName, 100); 464 setProgressGroup(group, 80); 465 getCollector().setProgressGroup(group, 20); 466 setProperty(IProgressConstants.ICON_PROPERTY, participant.getImageDescriptor()); 467 setProperty(IProgressConstants.ACTION_PROPERTY, actionWrapper); 468 setProperty(IProgressConstants.KEEPONE_PROPERTY, Boolean.valueOf(! isJobModal())); 469 IRefreshSubscriberListener autoListener = new IRefreshSubscriberListener() { 471 public void refreshStarted(IRefreshEvent event) { 472 if(listener != null) { 473 listener.refreshStarted(event); 474 } 475 } 476 public ActionFactory.IWorkbenchAction refreshDone(IRefreshEvent event) { 477 if(listener != null) { 478 boolean isModal = isJobModal(); 479 final ActionFactory.IWorkbenchAction runnable = listener.refreshDone(event); 480 if(runnable != null) { 481 if(isModal) { 483 if(runnable != null) { 484 Job update = new UIJob("") { public IStatus runInUIThread(IProgressMonitor monitor) { 486 runnable.run(); 487 return Status.OK_STATUS; 488 } 489 }; 490 update.setSystem(true); 491 update.schedule(); 492 } 493 } else { 494 actionWrapper.setGotoAction(runnable); 497 } 498 } 499 RefreshSubscriberJob.removeRefreshListener(this); 500 } 501 return null; 502 } 503 }; 504 505 if (listener != null) { 506 RefreshSubscriberJob.addRefreshListener(autoListener); 507 } 508 } 509 510 protected IResource[] getResources() { 511 return resources; 512 } 513 514 protected Subscriber getSubscriber() { 515 return participant.getSubscriber(); 516 } 517 518 protected SubscriberSyncInfoCollector getCollector() { 519 return participant.getSubscriberSyncInfoCollector(); 520 } 521 522 public long getScheduleDelay() { 523 return scheduleDelay; 524 } 525 526 protected void start() { 527 if(getState() == Job.NONE) { 528 if(shouldReschedule()) { 529 schedule(getScheduleDelay()); 530 } 531 } 532 } 533 534 538 public void setRefreshInterval(long seconds) { 539 boolean restart = false; 540 if(getState() == Job.SLEEPING) { 541 restart = true; 542 cancel(); 543 } 544 scheduleDelay = seconds * 1000; 545 if(restart) { 546 start(); 547 } 548 } 549 550 public void setRestartOnCancel(boolean restartOnCancel) { 551 this.restartOnCancel = restartOnCancel; 552 } 553 554 public void setReschedule(boolean reschedule) { 555 this.reschedule = reschedule; 556 } 557 558 public boolean shouldReschedule() { 559 return reschedule; 560 } 561 562 public static void addRefreshListener(IRefreshSubscriberListener listener) { 563 synchronized(listeners) { 564 if(! listeners.contains(listener)) { 565 listeners.add(listener); 566 } 567 } 568 } 569 570 public static void removeRefreshListener(IRefreshSubscriberListener listener) { 571 synchronized(listeners) { 572 listeners.remove(listener); 573 } 574 } 575 576 protected void notifyListeners(final int state, final IRefreshEvent event) { 577 IRefreshSubscriberListener[] listenerArray; 579 synchronized (listeners) { 580 listenerArray = (IRefreshSubscriberListener[]) listeners.toArray(new IRefreshSubscriberListener[listeners.size()]); 581 } 582 for (int i = 0; i < listenerArray.length; i++) { 584 IRefreshSubscriberListener listener = listenerArray[i]; 585 Notification notification = new Notification() { 586 protected void notify(IRefreshSubscriberListener listener) { 587 switch (state) { 588 case STARTED: 589 listener.refreshStarted(event); 590 break; 591 case DONE: 592 listener.refreshDone(event); 593 break; 594 default: 595 break; 596 } 597 } 598 }; 599 notification.run(listener); 600 } 601 } 602 603 private boolean isJobModal() { 604 Boolean isModal = (Boolean )getProperty(IProgressConstants.PROPERTY_IN_DIALOG); 605 if(isModal == null) return false; 606 return isModal.booleanValue(); 607 } 608 } 609 | Popular Tags |