/***************************************************************************** * Copyright (c) 2006-2013, Cloudsmith Inc. * The code, documentation and other materials contained herein have been * licensed under the Eclipse Public License - v 1.0 by the copyright holder * listed above, as the Initial Contributor under such license. The text of * such license is available at www.eclipse.org. *****************************************************************************/ package org.eclipse.buckminster.core.commands; import org.eclipse.buckminster.cmdline.AbstractCommand; import org.eclipse.buckminster.core.CorePlugin; import org.eclipse.buckminster.core.Messages; import org.eclipse.buckminster.core.helpers.JobBlocker; import org.eclipse.buckminster.core.materializer.WorkspaceBindingInstallJob; import org.eclipse.buckminster.runtime.Buckminster; import org.eclipse.buckminster.runtime.Logger; import org.eclipse.buckminster.runtime.MonitorUtils; import org.eclipse.core.internal.resources.DelayedSnapshotJob; import org.eclipse.core.internal.resources.ResourceException; import org.eclipse.core.internal.resources.Workspace; import org.eclipse.core.internal.utils.StringPoolJob; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IWorkspace; import org.eclipse.core.resources.IWorkspaceDescription; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.jobs.IJobManager; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.osgi.util.NLS; /** * The workspace command ensures that the workspace is in good shape when the * command terminates. * * @author Thomas Hallgren * */ @SuppressWarnings("restriction") public abstract class WorkspaceCommand extends AbstractCommand { private static void saveWorkspace(IProgressMonitor monitor) { monitor.beginTask(null, 300); try { IStatus saveStatus = ((Workspace) ResourcesPlugin.getWorkspace()).save(true, true, MonitorUtils.subMonitor(monitor, 100)); if (!(saveStatus == null || saveStatus.isOK())) throw new ResourceException(saveStatus); } catch (Throwable e) { Buckminster.getLogger().error(e, NLS.bind(Messages.Error_while_saving_workspace_0, e.getMessage())); } monitor.done(); } private boolean inWorkspace = false; protected void initWorkspace(IProgressMonitor monitor) throws Exception { IWorkspace ws = ResourcesPlugin.getWorkspace(); IWorkspaceDescription wsDesc = ws.getDescription(); wsDesc.setAutoBuilding(false); wsDesc.setSnapshotInterval(Long.MAX_VALUE); ws.setDescription(wsDesc); saveWorkspace(monitor); } protected abstract int internalRun(IProgressMonitor monitor) throws Exception; public boolean isInWorkspace() { return inWorkspace; } @Override protected final int run(IProgressMonitor monitor) throws Exception { monitor.beginTask(null, 1000); // We block some jobs that are targeted for IDE development but // considered bad during a headless build. // JobBlocker jobBlocker = new JobBlocker(); jobBlocker.addClassBlock("org.eclipse.core.internal.events.AutoBuildJob"); //$NON-NLS-1$ jobBlocker.addClassBlock("org.eclipse.jdt.internal.core.search.processing.JobManager$1$ProgressJob"); //$NON-NLS-1$ try { if (!isInWorkspace()) initWorkspace(MonitorUtils.subMonitor(monitor, 50)); jobBlocker.addClassBlock(DelayedSnapshotJob.class); return internalRun(MonitorUtils.subMonitor(monitor, 900)); } finally { if (isInWorkspace()) jobBlocker.release(); else { final Logger logger = CorePlugin.getLogger(); logger.debug("Doing full workspace refresh"); //$NON-NLS-1$ try { ResourcesPlugin.getWorkspace().getRoot().refreshLocal(IResource.DEPTH_INFINITE, MonitorUtils.subMonitor(monitor, 50)); } catch (Throwable e) { Buckminster.getLogger().error(NLS.bind(Messages.Error_while_refreshing_workspace_0, e.getMessage()), e); } // Suspend the job manager temporarily and wait for all jobs // to drain // final IJobManager jobManager = Job.getJobManager(); // Cancel jobs that are known to run indefinitely // logger.debug("Cancel jobs that are known to run indefinitely..."); //$NON-NLS-1$ WorkspaceBindingInstallJob.stop(); for (Job job : jobManager.find(null)) { if (job instanceof StringPoolJob || job instanceof DelayedSnapshotJob) { job.cancel(); logger.debug("CANCELED JOB: (%s) %s", job.getClass().getName(), job.toString()); //$NON-NLS-1$ } } // We wait for current jobs to end but we use a timeout // since there might be jobs // that run forever. // Thread joinWait = new Thread() { @Override public void run() { try { // jobManager.join(null, new NullProgressMonitor()); for (Job job : jobManager.find(null)) { if (job.getState() == Job.RUNNING && !job.toString().startsWith("ThreadJob")) { //$NON-NLS-1$ logger.debug(" Joining JOB: (%s) %s", job.getClass().getName(), job.toString()); //$NON-NLS-1$ job.join(); } } } catch (InterruptedException e) { // Cancel remaining jobs // for (Job job : jobManager.find(null)) { int state = job.getState(); if (state != Job.NONE) { logger.debug(" JOB: %s is still active", job.toString()); //$NON-NLS-1$ job.cancel(); } } } } }; logger.debug("Waiting for jobs to end"); //$NON-NLS-1$ // Wait at max 60 seconds for all jobs to complete. The // normal case is that // the join returns very quickly. // joinWait.start(); joinWait.join(60000); joinWait.interrupt(); // and resume the job manager. The workspace save will start // new // jobs. // jobBlocker.removeClassBlock(DelayedSnapshotJob.class); saveWorkspace(MonitorUtils.subMonitor(monitor, 50)); monitor.done(); } } } public void setInWorkspace(boolean inWorkspace) { this.inWorkspace = inWorkspace; } }