001: /*******************************************************************************
002: * Copyright (c) 2000, 2006 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: *******************************************************************************/package org.eclipse.ui.actions;
011:
012: import java.lang.reflect.InvocationTargetException;
013:
014: import org.eclipse.core.resources.IResource;
015: import org.eclipse.core.resources.IWorkspaceRunnable;
016: import org.eclipse.core.runtime.CoreException;
017: import org.eclipse.core.runtime.IProgressMonitor;
018: import org.eclipse.core.runtime.OperationCanceledException;
019: import org.eclipse.core.runtime.Platform;
020: import org.eclipse.core.runtime.jobs.ISchedulingRule;
021: import org.eclipse.core.runtime.jobs.Job;
022: import org.eclipse.jface.operation.IRunnableWithProgress;
023: import org.eclipse.jface.operation.IThreadListener;
024: import org.eclipse.ui.internal.ide.IDEWorkbenchPlugin;
025:
026: /**
027: * An operation which potentially makes changes to the workspace. All resource
028: * modification should be performed using this operation. The primary
029: * consequence of using this operation is that events which typically occur as a
030: * result of workspace changes (such as the firing of resource deltas,
031: * performance of autobuilds, etc.) are generally deferred until the outermost operation
032: * has successfully completed. The platform may still decide to broadcast
033: * periodic resource change notifications during the scope of the operation
034: * if the operation runs for a long time or another thread modifies the workspace
035: * concurrently.
036: * <p>
037: * If a scheduling rule is provided, the operation will obtain that scheduling
038: * rule for the duration of its <code>execute</code> method. If no scheduling
039: * rule is provided, the operation will obtain a scheduling rule that locks
040: * the entire workspace for the duration of the operation.
041: * </p>
042: * <p>
043: * Subclasses must implement <code>execute</code> to do the work of the
044: * operation.
045: * </p>
046: * @see ISchedulingRule
047: * @see org.eclipse.core.resources.IWorkspace#run(IWorkspaceRunnable, IProgressMonitor)
048: * */
049: public abstract class WorkspaceModifyOperation implements
050: IRunnableWithProgress, IThreadListener {
051: private ISchedulingRule rule;
052:
053: /**
054: * Creates a new operation.
055: */
056: protected WorkspaceModifyOperation() {
057: this (IDEWorkbenchPlugin.getPluginWorkspace().getRoot());
058: }
059:
060: /**
061: * Creates a new operation that will run using the provided
062: * scheduling rule.
063: * @param rule The ISchedulingRule to use or <code>null</code>.
064: * @since 3.0
065: */
066: protected WorkspaceModifyOperation(ISchedulingRule rule) {
067: this .rule = rule;
068: }
069:
070: /**
071: * Performs the steps that are to be treated as a single logical workspace
072: * change.
073: * <p>
074: * Subclasses must implement this method.
075: * </p>
076: *
077: * @param monitor the progress monitor to use to display progress and field
078: * user requests to cancel
079: * @exception CoreException if the operation fails due to a CoreException
080: * @exception InvocationTargetException if the operation fails due to an exception other than CoreException
081: * @exception InterruptedException if the operation detects a request to cancel,
082: * using <code>IProgressMonitor.isCanceled()</code>, it should exit by throwing
083: * <code>InterruptedException</code>. It is also possible to throw
084: * <code>OperationCanceledException</code>, which gets mapped to <code>InterruptedException</code>
085: * by the <code>run</code> method.
086: */
087: protected abstract void execute(IProgressMonitor monitor)
088: throws CoreException, InvocationTargetException,
089: InterruptedException;
090:
091: /**
092: * The <code>WorkspaceModifyOperation</code> implementation of this
093: * <code>IRunnableWithProgress</code> method initiates a batch of changes by
094: * invoking the <code>execute</code> method as a workspace runnable
095: * (<code>IWorkspaceRunnable</code>).
096: */
097: public synchronized final void run(IProgressMonitor monitor)
098: throws InvocationTargetException, InterruptedException {
099: final InvocationTargetException[] iteHolder = new InvocationTargetException[1];
100: try {
101: IWorkspaceRunnable workspaceRunnable = new IWorkspaceRunnable() {
102: public void run(IProgressMonitor pm)
103: throws CoreException {
104: try {
105: execute(pm);
106: } catch (InvocationTargetException e) {
107: // Pass it outside the workspace runnable
108: iteHolder[0] = e;
109: } catch (InterruptedException e) {
110: // Re-throw as OperationCanceledException, which will be
111: // caught and re-thrown as InterruptedException below.
112: throw new OperationCanceledException(e
113: .getMessage());
114: }
115: // CoreException and OperationCanceledException are propagated
116: }
117: };
118: IDEWorkbenchPlugin.getPluginWorkspace().run(
119: workspaceRunnable, rule, IResource.NONE, monitor);
120: } catch (CoreException e) {
121: throw new InvocationTargetException(e);
122: } catch (OperationCanceledException e) {
123: throw new InterruptedException(e.getMessage());
124: }
125: // Re-throw the InvocationTargetException, if any occurred
126: if (iteHolder[0] != null) {
127: throw iteHolder[0];
128: }
129: }
130:
131: /* (non-Javadoc)
132: * @see IThreadListener#threadChange(Thread);
133: * @since 3.2
134: */
135: public void threadChange(Thread thread) {
136: //we must make sure we aren't transferring control away from a thread that
137: //already owns a scheduling rule because this is deadlock prone (bug 105491)
138: if (rule == null) {
139: return;
140: }
141: Job currentJob = Platform.getJobManager().currentJob();
142: if (currentJob == null) {
143: return;
144: }
145: ISchedulingRule currentRule = currentJob.getRule();
146: if (currentRule == null) {
147: return;
148: }
149: throw new IllegalStateException(
150: "Cannot fork a thread from a thread owning a rule"); //$NON-NLS-1$
151: }
152:
153: }
|