diff --git a/org.spotter.eclipse.ui/src/org/spotter/eclipse/ui/jobs/DynamicSpotterRunJob.java b/org.spotter.eclipse.ui/src/org/spotter/eclipse/ui/jobs/DynamicSpotterRunJob.java index 0460d55..796e9aa 100644 --- a/org.spotter.eclipse.ui/src/org/spotter/eclipse/ui/jobs/DynamicSpotterRunJob.java +++ b/org.spotter.eclipse.ui/src/org/spotter/eclipse/ui/jobs/DynamicSpotterRunJob.java @@ -62,7 +62,6 @@ public class DynamicSpotterRunJob extends Job { private final IProject project; private final long jobId; - private final long timestamp; private final Set processedProblems; private Map.Entry currentProblem; @@ -81,7 +80,6 @@ public DynamicSpotterRunJob(IProject project, long jobId, long timestamp) { this.project = project; this.jobId = jobId; - this.timestamp = timestamp; this.processedProblems = new HashSet<>(); ImageDescriptor imageDescriptor = AbstractUIPlugin.imageDescriptorFromPlugin(Activator.PLUGIN_ID, ICON_PATH); @@ -95,7 +93,7 @@ public DynamicSpotterRunJob(IProject project, long jobId, long timestamp) { setPriority(LONG); setUser(true); - if (!JobsContainer.registerJobId(project, jobId)) { + if (!JobsContainer.registerJobId(project, jobId, timestamp)) { DialogUtils.openError(RunHandler.DIALOG_TITLE, "There was an error when saving the job id. You may not access the results of the diagnosis run."); } diff --git a/org.spotter.eclipse.ui/src/org/spotter/eclipse/ui/jobs/JobsContainer.java b/org.spotter.eclipse.ui/src/org/spotter/eclipse/ui/jobs/JobsContainer.java index 39a81cd..bd7d189 100644 --- a/org.spotter.eclipse.ui/src/org/spotter/eclipse/ui/jobs/JobsContainer.java +++ b/org.spotter.eclipse.ui/src/org/spotter/eclipse/ui/jobs/JobsContainer.java @@ -18,7 +18,9 @@ import java.io.File; import java.io.IOException; import java.io.Serializable; +import java.util.HashMap; import java.util.HashSet; +import java.util.Map; import java.util.Set; import org.eclipse.core.resources.IProject; @@ -42,6 +44,7 @@ public class JobsContainer implements Serializable { private static final long serialVersionUID = 2437447304864394397L; private final Set jobIds = new HashSet<>(); + private final Map timestamps = new HashMap<>(); /** * Returns true if the id is included, otherwise @@ -56,6 +59,20 @@ 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. * @@ -79,9 +96,12 @@ public int count() { * * @param jobId * the id to add + * @param timestamp + * the corresponding timestamp */ - public void addJobId(Long jobId) { + public void addJobId(Long jobId, Long timestamp) { jobIds.add(jobId); + timestamps.put(jobId, timestamp); } /** @@ -91,6 +111,7 @@ public void addJobId(Long jobId) { * the id to remove */ public void removeJobId(Long jobId) { + timestamps.remove(jobId); jobIds.remove(jobId); } @@ -98,6 +119,7 @@ public void removeJobId(Long jobId) { * Clears all job ids. */ public void reset() { + timestamps.clear(); jobIds.clear(); } @@ -108,33 +130,49 @@ public void reset() { * the project the id belongs to * @param jobId * the id to register + * @param timestamp + * the corresponding timestamp * @return true on success, otherwise false */ - public static boolean registerJobId(IProject project, long jobId) { + public static boolean registerJobId(IProject project, long jobId, long timestamp) { boolean success = false; synchronized (JobsContainer.jobMonitor) { JobsContainer jobsContainer = readJobsContainer(project); - jobsContainer.addJobId(jobId); + jobsContainer.addJobId(jobId, timestamp); success = writeJobsContainer(project, jobsContainer); } return success; } /** - * Returns the job ids that belong to the given project. + * Removes the given job id for the project. * * @param project - * the project whose job ids should be returned - * @return array of related job ids + * the project the id belongs to + * @param jobId + * the id to remove + * @return true on success, otherwise false */ - public static Long[] getJobIds(IProject project) { + public static boolean removeJobId(IProject project, long jobId) { + boolean success = false; synchronized (JobsContainer.jobMonitor) { JobsContainer jobsContainer = readJobsContainer(project); - return jobsContainer.getJobIds(); + jobsContainer.removeJobId(jobId); + success = writeJobsContainer(project, jobsContainer); } + return success; } - private static JobsContainer readJobsContainer(IProject project) { + /** + * 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(); diff --git a/org.spotter.eclipse.ui/src/org/spotter/eclipse/ui/navigator/FixedOrderViewerComparator.java b/org.spotter.eclipse.ui/src/org/spotter/eclipse/ui/navigator/FixedOrderViewerComparator.java index 8e2cb2d..15810a4 100644 --- a/org.spotter.eclipse.ui/src/org/spotter/eclipse/ui/navigator/FixedOrderViewerComparator.java +++ b/org.spotter.eclipse.ui/src/org/spotter/eclipse/ui/navigator/FixedOrderViewerComparator.java @@ -75,8 +75,6 @@ private String getLabel(Viewer viewer, Object e1) { IBaseLabelProvider prov = ((ContentViewer) viewer).getLabelProvider(); if (prov instanceof ILabelProvider) { ILabelProvider lprov = (ILabelProvider) prov; - // TODO: convert between timestamp and nice date representation - // for SpotterProjectRunResult elements name1 = lprov.getText(e1); } else { name1 = e1.toString(); diff --git a/org.spotter.eclipse.ui/src/org/spotter/eclipse/ui/navigator/SpotterProjectResults.java b/org.spotter.eclipse.ui/src/org/spotter/eclipse/ui/navigator/SpotterProjectResults.java index aa5f405..f5b2453 100644 --- a/org.spotter.eclipse.ui/src/org/spotter/eclipse/ui/navigator/SpotterProjectResults.java +++ b/org.spotter.eclipse.ui/src/org/spotter/eclipse/ui/navigator/SpotterProjectResults.java @@ -16,7 +16,10 @@ package org.spotter.eclipse.ui.navigator; import java.io.File; +import java.io.IOException; +import java.text.SimpleDateFormat; import java.util.ArrayList; +import java.util.Date; import java.util.List; import org.eclipse.core.resources.IFolder; @@ -24,10 +27,16 @@ import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.CoreException; import org.eclipse.swt.graphics.Image; +import org.lpe.common.util.LpeFileUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.spotter.eclipse.ui.Activator; +import org.spotter.eclipse.ui.ServiceClientWrapper; +import org.spotter.eclipse.ui.jobs.JobsContainer; +import org.spotter.eclipse.ui.util.DialogUtils; import org.spotter.shared.configuration.FileManager; +import org.spotter.shared.result.ResultsLocationConstants; +import org.spotter.shared.result.model.ResultsContainer; /** * An element that represents the results node. @@ -125,39 +134,85 @@ public void refreshChildren() { } private ISpotterProjectElement[] initializeChildren(IProject iProject) { - String defaultResultsDir = FileManager.DEFAULT_RESULTS_DIR_NAME; - IFolder resDir = iProject.getFolder(defaultResultsDir); + IFolder resDir = iProject.getFolder(FileManager.DEFAULT_RESULTS_DIR_NAME); if (!resDir.isSynchronized(IResource.DEPTH_INFINITE)) { try { resDir.refreshLocal(IResource.DEPTH_INFINITE, null); } catch (CoreException e) { - LOGGER.warn("Failed to refresh results directory"); + String msg = "Failed to refresh results directory."; + LOGGER.warn(msg); + DialogUtils.openWarning(msg); return SpotterProjectParent.NO_CHILDREN; } } - File res = new File(resDir.getLocation().toString()); - List runFolders = new ArrayList<>(); - ISpotterProjectElement[] elements = SpotterProjectParent.NO_CHILDREN; + return synchronizeRunResults(iProject, resDir.getLocation().toString()); + } + + private ISpotterProjectElement[] synchronizeRunResults(IProject iProject, String resultsLocation) { + File res = new File(resultsLocation); + List elements = new ArrayList<>(); + ServiceClientWrapper client = Activator.getDefault().getClient(iProject.getName()); + + if (!res.exists() || !res.isDirectory()) { + DialogUtils.openWarning("The project's results folder is missing or corrupted!"); + } else { + boolean connected = client.testConnection(false); + if (!connected) { + DialogUtils.openAsyncWarning("No connection to DS service! New results cannot be fetched from the server."); + } + JobsContainer jobsContainer = JobsContainer.readJobsContainer(iProject); - if (res.exists() && res.isDirectory()) { - File[] files = res.listFiles(); - for (File file : files) { - if (file.isDirectory()) { - runFolders.add(file); + for (Long jobId : jobsContainer.getJobIds()) { + SpotterProjectRunResult runResult = processJobId(jobId, connected, client, jobsContainer, + resultsLocation, iProject); + if (runResult != null) { + elements.add(runResult); } } + } + + return elements.toArray(new ISpotterProjectElement[elements.size()]); + } + + private SpotterProjectRunResult processJobId(Long jobId, boolean connected, ServiceClientWrapper client, + JobsContainer jobsContainer, String resultsLocation, IProject iProject) { + if (connected && client.isRunning(true) && jobId.equals(client.getCurrentJobId())) { + LOGGER.debug("Ignore job " + jobId + " because it is currently running"); + return null; + } - elements = new ISpotterProjectElement[runFolders.size()]; - int i = 0; - for (File runFolder : runFolders) { - IFolder runResultFolder = iProject.getFolder(defaultResultsDir + File.separator + runFolder.getName()); - elements[i++] = new SpotterProjectRunResult(this, runFolder.getName(), runResultFolder); + Long timestamp = jobsContainer.getTimestamp(jobId); + SimpleDateFormat dateFormat = new SimpleDateFormat("yy-MM-dd_HH-mm-ss-SSS"); + String formattedTimestamp = dateFormat.format(new Date(timestamp)); + + String fileName = resultsLocation + "/" + formattedTimestamp; + File file = new File(fileName); + boolean success = file.exists(); + if (!success && connected) { + // try to fetch data from server + ResultsContainer resultsContainer = client.requestResults(jobId.toString()); + if (resultsContainer != null && file.mkdir()) { + String resultsFile = fileName + "/" + ResultsLocationConstants.RESULTS_SERIALIZATION_FILE_NAME; + try { + LpeFileUtils.writeObject(resultsFile, resultsContainer); + success = true; + } catch (IOException e) { + String msg = "Error while saving fetched results for job " + jobId + "!"; + DialogUtils.openError(msg); + LOGGER.error(msg + " Cause: {}", e.toString()); + } } } - return elements; + if (success) { + IFolder runResultFolder = iProject.getFolder(FileManager.DEFAULT_RESULTS_DIR_NAME + "/" + + formattedTimestamp); + return new SpotterProjectRunResult(this, jobId, timestamp, runResultFolder); + } else { + return null; + } } } diff --git a/org.spotter.eclipse.ui/src/org/spotter/eclipse/ui/navigator/SpotterProjectRunResult.java b/org.spotter.eclipse.ui/src/org/spotter/eclipse/ui/navigator/SpotterProjectRunResult.java index 01fcf04..f94402f 100644 --- a/org.spotter.eclipse.ui/src/org/spotter/eclipse/ui/navigator/SpotterProjectRunResult.java +++ b/org.spotter.eclipse.ui/src/org/spotter/eclipse/ui/navigator/SpotterProjectRunResult.java @@ -28,6 +28,7 @@ import org.eclipse.ui.PartInitException; import org.eclipse.ui.PlatformUI; import org.spotter.eclipse.ui.Activator; +import org.spotter.eclipse.ui.jobs.JobsContainer; import org.spotter.eclipse.ui.util.DialogUtils; import org.spotter.eclipse.ui.view.ResultsView; @@ -47,6 +48,8 @@ public class SpotterProjectRunResult implements IOpenableProjectElement, IDeleta private final ISpotterProjectElement parent; private final IFolder resultFolder; private Image image; + private final long jobId; + private final long timestamp; private final String elementName; /** @@ -54,24 +57,26 @@ public class SpotterProjectRunResult implements IOpenableProjectElement, IDeleta * * @param parent * the parent element - * @param elementName - * the name of the element + * @param jobId + * the corresponding job id of this run result + * @param timestamp + * the corresponding timestamp of this run result * @param resultFolder * the result folder that is represented by this node */ - public SpotterProjectRunResult(ISpotterProjectElement parent, String elementName, IFolder resultFolder) { + public SpotterProjectRunResult(ISpotterProjectElement parent, long jobId, long timestamp, IFolder resultFolder) { this.parent = parent; - this.elementName = elementName; + this.jobId = jobId; + this.timestamp = timestamp; + + SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd (HH:mm:ss)"); + this.elementName = dateFormat.format(new Date(timestamp)); + this.resultFolder = resultFolder; } @Override public String getText() { - long timestamp = System.currentTimeMillis(); // TODO: replaced later by saved timestamp - Date date = new Date(timestamp); - SimpleDateFormat dateFormat = new SimpleDateFormat("yy-MM-dd (HH:mm:ss)"); - String readableTimestamp = dateFormat.format(date); - //return readableTimestamp; return elementName; } @@ -84,6 +89,13 @@ public Image getImage() { return image; } + /** + * @return the corresponding timestamp of this run result + */ + public long getTimestamp() { + return timestamp; + } + /** * @return the result folder this element is linked to */ @@ -156,6 +168,11 @@ public void delete() { ResultsView.reset(resultFolder); resultFolder.delete(true, null); + // clear job id + if (!JobsContainer.removeJobId(getProject(), jobId)) { + DialogUtils + .openError("There was an error while updating the project's job ids. The results of the corresponding id will be fetched again."); + } // update navigator viewer ((SpotterProjectResults) getParent()).refreshChildren(); TreeViewer viewer = Activator.getDefault().getNavigatorViewer(); diff --git a/org.spotter.eclipse.ui/src/org/spotter/eclipse/ui/util/DialogUtils.java b/org.spotter.eclipse.ui/src/org/spotter/eclipse/ui/util/DialogUtils.java index 45c0e27..4644d55 100644 --- a/org.spotter.eclipse.ui/src/org/spotter/eclipse/ui/util/DialogUtils.java +++ b/org.spotter.eclipse.ui/src/org/spotter/eclipse/ui/util/DialogUtils.java @@ -205,6 +205,24 @@ public void run() { } } + /** + * Opens asynchronously a warning dialog with the given title and message. + * It is ensured that the dialog is opened on the UI thread. + * + * @param title + * The title of the dialog + * @param message + * The message of the dialog + */ + public static void openAsyncWarning(final String title, final String message) { + getDisplay().asyncExec(new Runnable() { + @Override + public void run() { + MessageDialog.openWarning(getShell(), title, message); + } + }); + } + /** * Opens a warning dialog with the given message and a default title. It is * ensured that the dialog is opened on the UI thread. @@ -216,6 +234,17 @@ public static void openWarning(final String message) { openWarning(DEFAULT_DLG_TITLE, message); } + /** + * Opens asynchronously a warning dialog with the given message and a + * default title. It is ensured that the dialog is opened on the UI thread. + * + * @param message + * The message of the dialog + */ + public static void openAsyncWarning(final String message) { + openAsyncWarning(DEFAULT_DLG_TITLE, message); + } + /** * Opens an error dialog with the given title and message. It is ensured * that the dialog is opened on the UI thread. diff --git a/org.spotter.eclipse.ui/src/org/spotter/eclipse/ui/view/ResultsView.java b/org.spotter.eclipse.ui/src/org/spotter/eclipse/ui/view/ResultsView.java index 5675a9a..d8c88ea 100644 --- a/org.spotter.eclipse.ui/src/org/spotter/eclipse/ui/view/ResultsView.java +++ b/org.spotter.eclipse.ui/src/org/spotter/eclipse/ui/view/ResultsView.java @@ -615,9 +615,14 @@ private void resetReport() { } private void updateResultsContainer() { - String filename = FileManager.DEFAULT_RESULTS_DIR_NAME + File.separator + runResultItem.getText() - + File.separator + ResultsLocationConstants.RESULTS_SERIALIZATION_FILE_NAME; - IFile file = runResultItem.getProject().getFile(filename); +// String filename = FileManager.DEFAULT_RESULTS_DIR_NAME + File.separator + runResultItem.getText() +// + File.separator + ResultsLocationConstants.RESULTS_SERIALIZATION_FILE_NAME; +// String filename = FileManager.DEFAULT_RESULTS_DIR_NAME + File.separator + runResultItem.getText() +// + File.separator + ResultsLocationConstants.RESULTS_SERIALIZATION_FILE_NAME; + + IFile file = runResultItem.getResultFolder().getFile(ResultsLocationConstants.RESULTS_SERIALIZATION_FILE_NAME); + + //IFile file = runResultItem.getProject().getFile(filename); resultsContainer = null; try { if (!file.isSynchronized(IResource.DEPTH_ZERO)) { @@ -627,12 +632,12 @@ private void updateResultsContainer() { resultsContainer = (ResultsContainer) LpeFileUtils.readObject(containerFile); } catch (CoreException e) { resultsContainer = null; - String text = ERR_MSG_MISSING_SER_FILE + " (" + filename + ")"; + String text = ERR_MSG_MISSING_SER_FILE + " (" + file.getLocation() + ")"; LOGGER.error(text + (e.getMessage() != null ? " (" + e.getMessage() + ")" : "")); DialogUtils.openWarning(RESULTS_VIEW_TITLE, text); } catch (IOException | ClassNotFoundException e) { resultsContainer = null; - String text = String.format(ERR_MSG_IO_ERROR, filename); + String text = String.format(ERR_MSG_IO_ERROR, file.getLocation()); LOGGER.error(text + (e.getMessage() != null ? " (" + e.getMessage() + ")" : "")); DialogUtils.openWarning(RESULTS_VIEW_TITLE, text); }