/** * Copyright 2014 SAP AG * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.spotter.eclipse.ui.jobs; import java.io.File; import java.io.IOException; import java.io.Serializable; import java.net.ConnectException; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.eclipse.core.resources.IProject; import org.eclipse.core.runtime.jobs.IJobChangeEvent; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.core.runtime.jobs.JobChangeAdapter; import org.lpe.common.util.LpeFileUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.spotter.eclipse.ui.ServiceClientWrapper; import org.spotter.shared.configuration.FileManager; /** * A container for all requested jobs of a DS project. * * @author Denis Knoepfle * */ public class JobsContainer implements Serializable { private static final long serialVersionUID = 2080735292582582198L; private static final Object jobMonitor = new Object(); private static final Map<Long, DynamicSpotterRunJob> runningJobs = new HashMap<>(); private static final Logger LOGGER = LoggerFactory.getLogger(JobsContainer.class); private final Set<Long> jobIds = new HashSet<>(); private final Map<Long, Long> timestamps = new HashMap<>(); /** * Returns <code>true</code> if the id is included, otherwise * <code>false</code>. * * @param jobId * the id to lookup * @return <code>true</code> if the id is included, <code>false</code> * otherwise */ public boolean hasJobId(Long jobId) { return jobIds.contains(jobId); } /** * Returns the timestamp that corresponds to the given job id. * * @param jobId * the job id the timestamp shall be returned for * @return the corresponding timestamp */ public Long getTimestamp(Long jobId) { if (!jobIds.contains(jobId)) { throw new IllegalArgumentException("The given job id is not registered"); } return timestamps.get(jobId); } /** * Returns an array of all stored job ids. * * @return an array of all stored job ids */ public Long[] getJobIds() { return jobIds.toArray(new Long[count()]); } /** * Returns the amount of stored job ids. * * @return the amount of stored job ids */ public int count() { return jobIds.size(); } /** * Adds the given job id. * * @param jobId * the id to add * @param timestamp * the corresponding timestamp */ public void addJobId(Long jobId, Long timestamp) { jobIds.add(jobId); timestamps.put(jobId, timestamp); } /** * Removes the given job id. * * @param jobId * the id to remove */ public void removeJobId(Long jobId) { timestamps.remove(jobId); jobIds.remove(jobId); } /** * Clears all job ids. */ public void reset() { timestamps.clear(); jobIds.clear(); } /** * Registers the given job id for the project. * * @param project * the project the id belongs to * @param jobId * the id to register * @param timestamp * the corresponding timestamp * @return <code>true</code> on success, otherwise <code>false</code> */ public static boolean registerJobId(IProject project, long jobId, long timestamp) { boolean success = false; synchronized (JobsContainer.jobMonitor) { JobsContainer jobsContainer = readJobsContainer(project); jobsContainer.addJobId(jobId, timestamp); success = writeJobsContainer(project, jobsContainer); } return success; } /** * Removes the given job id for the project. * * @param project * the project the id belongs to * @param jobId * the id to remove * @return <code>true</code> on success, otherwise <code>false</code> */ public static boolean removeJobId(IProject project, long jobId) { boolean success = false; synchronized (JobsContainer.jobMonitor) { JobsContainer jobsContainer = readJobsContainer(project); jobsContainer.removeJobId(jobId); success = writeJobsContainer(project, jobsContainer); } return success; } /** * Removes all given job ids for the project at once. Only requires one read * and one write call. * * @param project * the project the ids belong to * @param jobIds * the ids to remove * @return <code>true</code> on success, otherwise <code>false</code> */ public static boolean removeJobIds(IProject project, List<Long> jobIds) { boolean success = false; synchronized (JobsContainer.jobMonitor) { JobsContainer jobsContainer = readJobsContainer(project); for (Long jobId : jobIds) { jobsContainer.removeJobId(jobId); } success = writeJobsContainer(project, jobsContainer); } return success; } /** * Retrieves the current job container for the given project. In case the * file does not exist or an error occurs while reading it an empty * container is returned. * * @param project * the project the container should be retrieved for * @return the corresponding job container or an empty one */ public static JobsContainer readJobsContainer(IProject project) { String fileName = project.getFile(FileManager.JOBS_CONTAINER_FILENAME).getLocation().toString(); File file = new File(fileName); JobsContainer jobsContainer = new JobsContainer(); if (file.exists()) { try { jobsContainer = (JobsContainer) LpeFileUtils.readObject(file); } catch (ClassNotFoundException | IOException e) { LOGGER.warn("JobsContainer {} corrupted, ignoring file contents. Error: {}", fileName, e.getMessage()); } } return jobsContainer; } /** * Tries to read the current job from the jobs container associated with the * given project. Returns the job id or <code>null</code> if not found. * * @param client * the client to use for the lookup * @param project * the corresponding project * @return job id or <code>null</code> * @throws ConnectException * if an connection error occurs during lookup */ public static Long readCurrentJob(ServiceClientWrapper client, IProject project) throws ConnectException { JobsContainer container = readJobsContainer(project); for (Long jobId : container.getJobIds()) { boolean isRunning = client.isRunning(true); if (isRunning && jobId.equals(client.getCurrentJobId())) { return jobId; } else if (!isRunning && client.isConnectionIssue()) { throw new ConnectException("Lost connection during lookup of job ids."); } } return null; } /** * Convenience method to add running jobs that will be automatically * cancelled silently if the plug-in is shutting down and they are still * running. * * @param jobId * the job id of the job * @param job * the job to add */ public static void addRunningJob(final long jobId, DynamicSpotterRunJob job) { // automatically remove the job when it's done job.addJobChangeListener(new JobChangeAdapter() { @Override public void done(IJobChangeEvent event) { synchronized (jobMonitor) { runningJobs.remove(jobId); } } }); synchronized (jobMonitor) { runningJobs.put(jobId, job); } } /** * Convenience method to cancel all jobs silently that has been previously * added with {@link #addRunningJob}, e.g. when shutting down the plug-in. * But regardless if a job of the diagnosis family had been added all of * these will be cancelled anyway. */ public static void cancelAllRunningJobsSilently() { synchronized (jobMonitor) { for (DynamicSpotterRunJob job : runningJobs.values()) { job.setSilentCancel(true); } Job.getJobManager().cancel(DynamicSpotterRunJob.DS_RUN_JOB_FAMILY); } } private static boolean writeJobsContainer(IProject project, JobsContainer jobsContainer) { String fileName = project.getFile(FileManager.JOBS_CONTAINER_FILENAME).getLocation().toString(); try { LpeFileUtils.writeObject(fileName, jobsContainer); return true; } catch (IOException e) { LOGGER.error("Error while writing JobsContainer.", e); } return false; } }