/******************************************************************************* * Copyright (c) 2013, 2014 Wind River Systems, Inc. and others. All rights reserved. * This program and the accompanying materials are made available under the terms * of the Eclipse Public License v1.0 which accompanies this distribution, and is * available at http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Wind River Systems - initial API and implementation *******************************************************************************/ package org.eclipse.tcf.te.tcf.core.jobs; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.tcf.protocol.Protocol; import org.eclipse.tcf.te.runtime.callback.Callback; import org.eclipse.tcf.te.runtime.interfaces.callback.ICallback; import org.eclipse.tcf.te.runtime.interfaces.properties.IPropertiesContainer; import org.eclipse.tcf.te.runtime.properties.PropertiesContainer; import org.eclipse.tcf.te.runtime.statushandler.StatusHandlerManager; import org.eclipse.tcf.te.runtime.statushandler.interfaces.IStatusHandler; import org.eclipse.tcf.te.runtime.statushandler.interfaces.IStatusHandlerConstants; import org.eclipse.tcf.te.tcf.core.help.IContextHelpIds; import org.eclipse.tcf.te.tcf.core.nls.Messages; /** * Abstract TCF job implementation. * <p> * The job implementation assures that the job logic is executed within the * TCF event dispatch thread. The job passes on the execution status to a * registered status handler if no parent callback is set. */ public abstract class AbstractJob extends Job { // The parent callback. /* default */ final ICallback parentCallback; /** * Constructor. * * @param name The job name. Must not be <code>null</code>. */ public AbstractJob(String name) { this(name, null); } /** * Constructor. * * @param name The job name. Must not be <code>null</code>. * @param parentCallback The parent callback or <code>null</code>. */ public AbstractJob(String name, ICallback parentCallback) { super(name); this.parentCallback = parentCallback; } /* (non-Javadoc) * @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor) */ @Override protected final IStatus run(final IProgressMonitor monitor) { // The first runnable is setting the thread which will finish the job at the end Protocol.invokeLater(new Runnable() { @Override public void run() { AbstractJob.this.setThread(Thread.currentThread()); } }); final IProgressMonitor finMonitor = monitor; // The callback to invoke from the job's logic must be created before scheduling // the job logic asynchronously final ICallback callback = new Callback() { @Override protected void internalDone(final Object caller, final IStatus status) { // If the parent callback is not set, pass on the status to a registered status handler if (parentCallback == null) { // If we have a non-OK or CANCEL status, forward to the status handler if (status != null && status.getSeverity() != IStatus.CANCEL && status.getSeverity() != IStatus.OK) { IStatusHandler[] handler = StatusHandlerManager.getInstance().getHandler(AbstractJob.this); if (handler.length > 0) { IPropertiesContainer data = new PropertiesContainer(); data.setProperty(IStatusHandlerConstants.PROPERTY_TITLE, getProperty(IStatusHandlerConstants.PROPERTY_TITLE) != null ? getProperty(IStatusHandlerConstants.PROPERTY_TITLE) : Messages.AbstractJob_error_dialogTitle); data.setProperty(IStatusHandlerConstants.PROPERTY_CONTEXT_HELP_ID, getProperty(IStatusHandlerConstants.PROPERTY_CONTEXT_HELP_ID) != null ? getProperty(IStatusHandlerConstants.PROPERTY_CONTEXT_HELP_ID) : IContextHelpIds.MESSAGE_OPERATION_FAILED); data.setProperty(IStatusHandlerConstants.PROPERTY_CALLER, AbstractJob.this); handler[0].handleStatus(status, data, null); } } } else { // Pass on the caller and the status to the parent callback // We call the parent callback asynchronously in the TCF // event dispatch thread. The job termination happens in // a separate runnable. Protocol.invokeLater(new Runnable() { @Override public void run() { parentCallback.done(caller, status); } }); } // Job termination must must happen in the TCF event dispatch thread as // this is the thread we've set before as the termination thread. Runnable runnable = new Runnable() { @Override public void run() { // Mark the job as done IStatus result = finMonitor.isCanceled() ? Status.CANCEL_STATUS : Status.OK_STATUS; AbstractJob.this.done(result); } }; if (Protocol.isDispatchThread()) runnable.run(); else Protocol.invokeLater(runnable); } }; // Run the job logic Protocol.invokeLater(new Runnable() { @Override public void run() { AbstractJob.this.run(monitor, callback); } }); return ASYNC_FINISH; } /** * Executes the job logic. * * @param monitor The progress monitor. Must not be <code>null</code>. * @param callback The callback to invoke. Must not be <code>null</code>. */ public abstract void run(IProgressMonitor monitor, ICallback callback); }