/******************************************************************************* * Copyright (c) 2013, 2015 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.runtime.stepper.job; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import org.eclipse.core.runtime.Assert; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.ProgressMonitorWrapper; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.jobs.IJobChangeEvent; import org.eclipse.core.runtime.jobs.IJobChangeListener; import org.eclipse.core.runtime.jobs.ISchedulingRule; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.core.runtime.jobs.JobChangeAdapter; import org.eclipse.tcf.te.runtime.interfaces.callback.ICallback; import org.eclipse.tcf.te.runtime.interfaces.properties.IPropertiesContainer; import org.eclipse.tcf.te.runtime.services.ServiceManager; import org.eclipse.tcf.te.runtime.services.interfaces.IPropertiesAccessService; import org.eclipse.tcf.te.runtime.statushandler.StatusHandlerManager; import org.eclipse.tcf.te.runtime.statushandler.interfaces.IStatusHandler; import org.eclipse.tcf.te.runtime.stepper.StepperAttributeUtil; import org.eclipse.tcf.te.runtime.stepper.interfaces.IFullQualifiedId; import org.eclipse.tcf.te.runtime.stepper.interfaces.IStepAttributes; import org.eclipse.tcf.te.runtime.stepper.interfaces.IStepContext; import org.eclipse.tcf.te.runtime.stepper.interfaces.IStepper; import org.eclipse.tcf.te.runtime.stepper.interfaces.IStepperService; import org.eclipse.tcf.te.runtime.stepper.stepper.Stepper; /** * Stepper job implementation. */ public final class StepperJob extends Job { final private IStepContext stepContext; final private IPropertiesContainer data; final private String stepGroupId; final protected String operation; private final boolean handleStatus; private final boolean isCancelable; private ICallback jobCallback = null; private boolean isFinished = false; private boolean isCanceled = false; private boolean statusHandled = false; /** * A progress monitor wrapper which blocks any access to a JobMonitor * after the job has finished. This prevents creation of stale progress * items in the ProgressManager. */ private static class StepperProgressMonitor extends ProgressMonitorWrapper { private final boolean cancelable; private volatile boolean jobDone; private volatile boolean canceled; public StepperProgressMonitor(IProgressMonitor monitor, boolean isCancelable) { super(monitor); this.cancelable = isCancelable; } void jobDone(boolean canceled) { // Job has finished - block any access to the JobMonitor jobDone = true; this.canceled = canceled; } @Override public void beginTask(String name, int totalWork) { if (!jobDone) super.beginTask(name, totalWork); } @Override public void done() { if (!jobDone) super.done(); } @Override public void internalWorked(double work) { if (!jobDone) super.internalWorked(work); } @Override public boolean isCanceled() { if (cancelable && !jobDone) return super.isCanceled(); return canceled; } @Override public void setCanceled(boolean value) { canceled = cancelable && value; if (!jobDone) super.setCanceled(canceled); } @Override public void setTaskName(String name) { if (!jobDone) super.setTaskName(name); } @Override public void subTask(String name) { if (!jobDone) super.subTask(name); } @Override public void worked(int work) { if (!jobDone) super.worked(work); } } private class JobChangeListener extends JobChangeAdapter { private final StepperProgressMonitor jobMonitor; /** * Constructor. */ public JobChangeListener(StepperProgressMonitor monitor) { this.jobMonitor = monitor; } @Override public void done(IJobChangeEvent event) { jobMonitor.jobDone(isCanceled()); handleStatus(event.getResult()); removeJobChangeListener(this); } } /** * Constructor. * * @param name The job name. * @param stepContext The step context. * @param data The stepper data. * @param stepGroupId The step group id to execute. * @param operation The operation to execute. * @param cancelable <code>true</code> if the job can be canceled. * @param handleStatus <code>true</code> if the job should handle the status itself and return always <code>Status.OK_STATUS</code>. */ public StepperJob(String name, IStepContext stepContext, IPropertiesContainer data, String stepGroupId, String operation, boolean isCancelable, boolean handleStatus) { super(name); setPriority(Job.INTERACTIVE); Assert.isNotNull(stepContext); Assert.isNotNull(data); Assert.isNotNull(stepGroupId); Assert.isNotNull(operation); this.stepContext = stepContext; this.data = data; this.stepGroupId = stepGroupId; this.operation = operation; this.isCancelable = isCancelable; this.handleStatus = handleStatus; ISchedulingRule rule = null; IStepperService service = ServiceManager.getInstance().getService(stepContext.getContextObject(), IStepperService.class, true); if (service != null) { rule = service.getSchedulingRule(stepContext.getContextObject(), operation); } else if (stepContext.getContextObject() instanceof ISchedulingRule) { rule = (ISchedulingRule)stepContext.getContextObject(); } setRule(rule); Map<String,List<Job>> jobs = getJobs(stepContext.getContextObject()); addJob(jobs, this, operation); setJobs(jobs, stepContext.getContextObject()); } @SuppressWarnings("unchecked") public static Map<String,List<Job>> getJobs(Object context) { IPropertiesAccessService service = ServiceManager.getInstance().getService(context, IPropertiesAccessService.class); Map<String,List<Job>> jobs = null; if (service == null && context instanceof IPropertiesContainer) jobs = (Map<String,List<Job>>)((IPropertiesContainer)context).getProperty(StepperJob.class.getName()); else jobs = service != null ? (Map<String,List<Job>>)service.getProperty(context, StepperJob.class.getName()) : null; if (jobs == null) jobs = new HashMap<String, List<Job>>(); return jobs; } public static List<Job> getJobsForOperation(Object context, String operation) { Map<String, List<Job>> jobs = getJobs(context); synchronized (jobs) { List<Job> jobsForOperation = jobs.get(operation); if (jobsForOperation == null) return Collections.emptyList(); return new ArrayList<Job>(jobsForOperation); } } public static void addJob(Map<String,List<Job>> jobs, Job job, String operation) { synchronized (jobs) { List<Job> jobsForOperation = jobs.get(operation); if (jobsForOperation == null) { jobsForOperation = new ArrayList<Job>(); jobs.put(operation, jobsForOperation); } jobsForOperation.add(job); } } public static void removeJob(Map<String,List<Job>> jobs, Job job, String operation) { synchronized (jobs) { List<Job> jobsForOperation = jobs.get(operation); if (jobsForOperation != null) { jobsForOperation.remove(job); } } } public static void setJobs(Map<String,List<Job>> jobs, Object context) { IPropertiesAccessService service = ServiceManager.getInstance().getService(context, IPropertiesAccessService.class); if (service != null) service.setProperty(context, StepperJob.class.getName(), jobs); else if (context instanceof IPropertiesContainer) ((IPropertiesContainer)context).setProperty(StepperJob.class.getName(), jobs); } /** * Set the callback for the job. * @param callback The callback. */ public final void setJobCallback(ICallback callback) { jobCallback = callback; } /** * Return the job callback. */ public final ICallback getJobCallback() { return jobCallback; } /** * Return <code>true</code> if thsi job is cancelable. */ public boolean isCancelable() { return isCancelable; } /* (non-Javadoc) * @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor) */ @Override public final IStatus run(IProgressMonitor monitor) { StepperProgressMonitor stepperMonitor = new StepperProgressMonitor(monitor, isCancelable); monitor = stepperMonitor; IJobChangeListener listener = new JobChangeListener(stepperMonitor); addJobChangeListener(listener); // The stepper instance to be used IStepper stepper = new Stepper(getName()) { /* (non-Javadoc) * @see org.eclipse.tcf.te.runtime.stepper.stepper.Stepper#onInitialize(org.eclipse.tcf.te.runtime.interfaces.properties.IPropertiesContainer, org.eclipse.tcf.te.runtime.stepper.interfaces.IFullQualifiedId, org.eclipse.core.runtime.IProgressMonitor) */ @Override protected void onInitialize(IPropertiesContainer data, IFullQualifiedId fullQualifiedId, IProgressMonitor monitor) { super.onInitialize(data, fullQualifiedId, monitor); StepperAttributeUtil.setProperty(IStepAttributes.ATTR_STEPPER_JOB, fullQualifiedId, data, StepperJob.this); StepperAttributeUtil.setProperty(IStepAttributes.ATTR_STEPPER_JOB_OPERATION, fullQualifiedId, data, operation); } }; IStatus status = Status.OK_STATUS; try { // Initialize stepper stepper.initialize(stepContext, stepGroupId, data, monitor); // Execute stepper stepper.execute(); } catch (CoreException e) { status = e.getStatus(); } finally { // Cleanup the stepper stepper.cleanup(); Map<String,List<Job>> jobs = getJobs(stepContext.getContextObject()); removeJob(jobs, this, operation); setJobs(jobs, stepContext.getContextObject()); } if (jobCallback != null) jobCallback.done(this, status); isFinished = true; if (handleStatus) { handleStatus(status); } return statusHandled ? Status.OK_STATUS : status; } protected void handleStatus(IStatus status) { if (!statusHandled && status != null && status.matches(IStatus.ERROR|IStatus.WARNING|IStatus.INFO)) { IStatusHandler[] handler = StatusHandlerManager.getInstance().getHandler(StepperJob.this); if (handler != null && handler.length > 0) { handler[0].handleStatus(status, null, null); } } markStatusHandled(); } public void markStatusHandled() { statusHandled = true; } public boolean isFinished() { return isFinished; } public boolean isCanceled() { return isCanceled && isCancelable; } /* (non-Javadoc) * @see org.eclipse.core.runtime.jobs.Job#canceling() */ @Override protected void canceling() { if (isCancelable) { super.canceling(); isCanceled = true; } } /* (non-Javadoc) * @see org.eclipse.core.runtime.jobs.Job#belongsTo(java.lang.Object) */ @Override public boolean belongsTo(Object family) { return StepperJob.class.getName().equals(family); } }