From cd20989280873c1d3cd787a158343c3372faa9b5 Mon Sep 17 00:00:00 2001 From: Nils Christian Ehmke <nie@informatik.uni-kiel.de> Date: Thu, 13 Sep 2012 13:25:34 +0200 Subject: [PATCH] Replaced ACManager and FSManager with the ProjectManagerFacade. --- .../.settings/org.eclipse.jdt.ui.prefs | 2 +- .../beans/application/ProjectsBean.java | 40 +- .../beans/view/CurrentAnalysisEditorBean.java | 67 +- .../webgui/beans/view/CurrentCockpitBean.java | 167 ++-- .../beans/view/CurrentCockpitEditorBean.java | 14 +- .../beans/view/CurrentControllerBean.java | 82 +- .../java/kieker/webgui/common/ACManager.java | 201 ++--- .../java/kieker/webgui/common/FSManager.java | 766 ++++++++---------- .../webgui/common/IProjectManagerFacade.java | 18 +- .../webgui/common/ProjectManagerFacade.java | 180 +++- .../ProjectNotExistingException.java | 5 + .../dialogs/manageLibrariesDialog.xhtml | 4 +- 12 files changed, 829 insertions(+), 717 deletions(-) diff --git a/Kieker.WebGUI/.settings/org.eclipse.jdt.ui.prefs b/Kieker.WebGUI/.settings/org.eclipse.jdt.ui.prefs index e3a46d30..a7294084 100644 --- a/Kieker.WebGUI/.settings/org.eclipse.jdt.ui.prefs +++ b/Kieker.WebGUI/.settings/org.eclipse.jdt.ui.prefs @@ -55,7 +55,7 @@ eclipse.preferences.version=1 editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true formatter_profile=_Kieker - Profile formatter_settings_version=12 -org.eclipse.jdt.ui.exception.name=e +org.eclipse.jdt.ui.exception.name=ex org.eclipse.jdt.ui.gettersetter.use.is=true org.eclipse.jdt.ui.ignorelowercasenames=true org.eclipse.jdt.ui.importorder=java;javax;junit;org;com;kieker;org.primefaces;org.eclipse; diff --git a/Kieker.WebGUI/src/main/java/kieker/webgui/beans/application/ProjectsBean.java b/Kieker.WebGUI/src/main/java/kieker/webgui/beans/application/ProjectsBean.java index fafdd177..78e85044 100644 --- a/Kieker.WebGUI/src/main/java/kieker/webgui/beans/application/ProjectsBean.java +++ b/Kieker.WebGUI/src/main/java/kieker/webgui/beans/application/ProjectsBean.java @@ -37,10 +37,11 @@ import kieker.analysis.model.analysisMetaModel.MIProject; import kieker.common.logging.Log; import kieker.common.logging.LogFactory; import kieker.webgui.beans.view.CurrentProjectOverviewBean; -import kieker.webgui.common.ACManager; -import kieker.webgui.common.FSManager; +import kieker.webgui.common.IProjectManagerFacade; +import kieker.webgui.common.ProjectManagerFacade; import kieker.webgui.common.exception.ProjectAlreadyExistingException; import kieker.webgui.common.exception.ProjectLoadException; +import kieker.webgui.common.exception.ProjectNotExistingException; /** * The {@link ProjectsBean} is a JSF managed bean to manage a list with all application wide available projects. It provides methods to receive this list as well as @@ -64,6 +65,7 @@ public final class ProjectsBean { * This list contains all available projects by name. It is synchronized as a simple synchronized list, as the fine grained synchronization isn't necessary here. */ private final List<String> projects = Collections.synchronizedList(new ArrayList<String>()); + private final IProjectManagerFacade projectManagerFacade = ProjectManagerFacade.getInstance(); /** * Default constructor. <b>Do not use this constructor. This bean is JSF managed.</b> @@ -78,7 +80,7 @@ public final class ProjectsBean { @PostConstruct protected void init() { // Load a list with all available projects on the FS - this.projects.addAll(FSManager.getInstance().getAllProjects()); + this.projects.addAll(this.projectManagerFacade.listAllProjects()); } /** @@ -95,7 +97,7 @@ public final class ProjectsBean { public void addProject(final String project) { try { // Try and use the FS-Manager to create the project atomically. - FSManager.getInstance().addProject(project); + this.projectManagerFacade.addProject(project); // If there were no exception, everything went well. We can add the project to our list. this.projects.add(project); // Inform the user @@ -126,7 +128,7 @@ public final class ProjectsBean { public void copyProject(final String sourceProject, final String destinationProject) { try { // Try and use the FS-Manager to copy the project atomically. - FSManager.getInstance().copyProject(sourceProject, destinationProject); + this.projectManagerFacade.copyProject(sourceProject, destinationProject); // If there were no exception, everything went well. We can add the project to our list. this.projects.add(destinationProject); // Inform the user @@ -140,6 +142,9 @@ public final class ProjectsBean { } catch (final ProjectAlreadyExistingException ex) { ProjectsBean.LOG.info("A project with the same name exists already.", ex); ProjectsBean.showMessage(FacesMessage.SEVERITY_WARN, "A project with the same name exists already."); + } catch (final ProjectNotExistingException ex) { + ProjectsBean.LOG.info("A project with the given name does not exist.", ex); + ProjectsBean.showMessage(FacesMessage.SEVERITY_WARN, "A project with the given name does not exist."); } } @@ -153,10 +158,13 @@ public final class ProjectsBean { */ public MIProject openProject(final String project) throws ProjectLoadException { try { - return FSManager.getInstance().openProject(project); + return this.projectManagerFacade.openProject(project); } catch (final IOException ex) { ProjectsBean.LOG.error("An error occured while loading the project.", ex); throw new ProjectLoadException("An error occured while loading the project.", ex); + } catch (final ProjectNotExistingException ex) { + ProjectsBean.LOG.info("A project with the given name does not exist.", ex); + throw new ProjectLoadException("A project with the given name does not exist.", ex); } } @@ -169,15 +177,16 @@ public final class ProjectsBean { * @return The human readable time stamp of the project. */ public String getCurrTimeStamp(final String project) { - // Get the current time stamp of the project - final long timeStamp = FSManager.getInstance().getCurrTimeStamp(project); - - if (timeStamp == 0) { + try { + // Get the current time stamp of the project + final long timeStamp = this.projectManagerFacade.getCurrTimeStamp(project); + // Convert the stamp into a human readable string. + return new Date(timeStamp).toString(); + } catch (final ProjectNotExistingException ex) { + ProjectsBean.LOG.info("A project with the given name does not exist.", ex); // We can assume that something went wrong return "N/A"; } - // Convert the stamp into a human readable string. - return new Date(timeStamp).toString(); } @@ -201,7 +210,12 @@ public final class ProjectsBean { * @return The current state of the corresponding AnalysisController as defined by {@link ACManager#getAnalysisControllerStateString(String)}. */ public String getAnalysisControllerState(final String project) { - return ACManager.getInstance().getAnalysisControllerStateString(project); + try { + return this.projectManagerFacade.getCurrentState(project).toString(); + } catch (final ProjectNotExistingException ex) { + ProjectsBean.LOG.info("A project with the given name does not exist.", ex); + return ""; + } } /** diff --git a/Kieker.WebGUI/src/main/java/kieker/webgui/beans/view/CurrentAnalysisEditorBean.java b/Kieker.WebGUI/src/main/java/kieker/webgui/beans/view/CurrentAnalysisEditorBean.java index 8ea833af..5130acd2 100644 --- a/Kieker.WebGUI/src/main/java/kieker/webgui/beans/view/CurrentAnalysisEditorBean.java +++ b/Kieker.WebGUI/src/main/java/kieker/webgui/beans/view/CurrentAnalysisEditorBean.java @@ -60,12 +60,13 @@ import kieker.common.logging.Log; import kieker.common.logging.LogFactory; import kieker.webgui.beans.application.ProjectsBean; import kieker.webgui.common.ClassAndMethodContainer; -import kieker.webgui.common.FSManager; -import kieker.webgui.common.Pair; +import kieker.webgui.common.IProjectManagerFacade; import kieker.webgui.common.PluginFinder; +import kieker.webgui.common.ProjectManagerFacade; import kieker.webgui.common.exception.LibraryAlreadyExistingException; import kieker.webgui.common.exception.NewerProjectException; import kieker.webgui.common.exception.ProjectLoadException; +import kieker.webgui.common.exception.ProjectNotExistingException; import org.primefaces.context.RequestContext; import org.primefaces.event.FileUploadEvent; @@ -151,7 +152,7 @@ public final class CurrentAnalysisEditorBean { * This field contains the currently selected component (this can either be a plugin ({@link MIPlugin}) or a repository ({@link MIRepository})). */ private EObject selectedComponent; - + private final IProjectManagerFacade projectManagerFacade = ProjectManagerFacade.getInstance(); @ManagedProperty(value = "#{projectsBean}") private ProjectsBean projectsBean; @@ -284,10 +285,10 @@ public final class CurrentAnalysisEditorBean { // Run through all libraries and add their content to our lists for (final MIDependency lib : this.project.getDependencies()) { - this.addContentsToToolPalette(FSManager.getInstance().getURL(lib, this.projectName)); + this.addContentsToToolPalette(this.projectManagerFacade.getURL(lib, this.projectName)); } // Run also through the kieker library - this.addContentsToToolPalette(FSManager.getInstance().getKiekerURL()); + this.addContentsToToolPalette(this.projectManagerFacade.getKiekerURL()); } catch (final MalformedURLException ex) { CurrentAnalysisEditorBean.LOG.error("A library could not be loaded correctly.", ex); throw new ProjectLoadException("A library could not be loaded correctly.", ex); @@ -333,14 +334,25 @@ public final class CurrentAnalysisEditorBean { /** * This method takes all libraries from the lib-folder and adds them to the in-memory-model. + * + * @throws ProjectLoadException */ - private void initializeModelLibraries() { + private void initializeModelLibraries() throws ProjectLoadException { synchronized (this) { - final List<MIDependency> libs = FSManager.getInstance().getModelLibraries(this.projectName); - // Add them, but remove all existing dependencies so far to avoid double entries. This also makes sure that the model - after it has been opened - points - // just to valid dependencies (and to all of them). - this.project.getDependencies().clear(); - this.project.getDependencies().addAll(libs); + try { + final List<String> libs = this.projectManagerFacade.listAllLibraries(this.projectName); + // Add them, but remove all existing dependencies so far to avoid double entries. This also makes sure that the model - after it has been opened - + // points + // just to valid dependencies (and to all of them). + this.project.getDependencies().clear(); + for (final String lib : libs) { + final MIDependency dep = this.factory.createDependency(); + dep.setFilePath(lib); + this.project.getDependencies().add(dep); + } + } catch (final ProjectNotExistingException ex) { + throw new ProjectLoadException("The project does not exist.", ex); + } } } @@ -353,13 +365,17 @@ public final class CurrentAnalysisEditorBean { */ private void reloadClassLoader() throws ProjectLoadException { synchronized (this) { - this.classLoader = FSManager.getInstance().getClassLoader(this.projectName); try { + this.classLoader = this.projectManagerFacade.getClassLoader(this.projectName); this.pluginFinder = new PluginFinder(this.classLoader); } catch (final ClassNotFoundException ex) { throw new ProjectLoadException("Could not load classes.", ex); } catch (final NullPointerException ex) { throw new ProjectLoadException("Invalid class loader.", ex); + } catch (final IOException ex) { + throw new ProjectLoadException("Could not load classes.", ex); + } catch (final ProjectNotExistingException ex) { + throw new ProjectLoadException("Project does not exist.", ex); } } } @@ -585,15 +601,17 @@ public final class CurrentAnalysisEditorBean { // Use the file system manager to upload the new file final MIDependency lib; synchronized (this) { - lib = FSManager.getInstance().uploadLibrary(file, this.projectName); + this.projectManagerFacade.uploadLibrary(file, this.projectName); CurrentAnalysisEditorBean.showMessage(FacesMessage.SEVERITY_INFO, "Libary uploaded."); // As it seem to have worked, we can add the library to our model. + lib = this.factory.createDependency(); + lib.setFilePath(file.getFileName()); this.project.getDependencies().add(lib); } // Update our class loader and the available plugins & repositories this.reloadClassLoader(); this.reloadClassesAndMethods(); - this.addContentsToToolPalette(FSManager.getInstance().getURL(lib, this.projectName)); + this.addContentsToToolPalette(this.projectManagerFacade.getURL(lib, this.projectName)); } catch (final LibraryAlreadyExistingException ex) { CurrentAnalysisEditorBean.LOG.info("A library with the same name exists already.", ex); CurrentAnalysisEditorBean.showMessage(FacesMessage.SEVERITY_WARN, "A library with the same name exists already."); @@ -603,6 +621,9 @@ public final class CurrentAnalysisEditorBean { } catch (final ProjectLoadException ex) { CurrentAnalysisEditorBean.LOG.error("An error occured while uploading the library.", ex); CurrentAnalysisEditorBean.showMessage(FacesMessage.SEVERITY_ERROR, "An error occured while uploading the library."); + } catch (final ProjectNotExistingException ex) { + CurrentAnalysisEditorBean.LOG.error("Project does not exist.", ex); + CurrentAnalysisEditorBean.showMessage(FacesMessage.SEVERITY_ERROR, "Project does not exist."); } } @@ -612,12 +633,17 @@ public final class CurrentAnalysisEditorBean { * * @return The available libraries. */ - public List<Pair<String, String>> getLibraries() { + public List<String> getLibraries() { synchronized (this) { - final List<Pair<String, String>> result = FSManager.getInstance().getLibraries(this.projectName); - result.add(0, new Pair<String, String>("Kieker", "N/A")); + try { + final List<String> result = this.projectManagerFacade.listAllLibraries(this.projectName); + result.add(0, "Kieker"); - return result; + return result; + } catch (final ProjectNotExistingException ex) { + CurrentAnalysisEditorBean.LOG.error("The project does not exist.", ex); + return new ArrayList<String>(); + } } } @@ -657,7 +683,7 @@ public final class CurrentAnalysisEditorBean { public void saveProject(final boolean overwriteNewerProject) { synchronized (this) { try { - FSManager.getInstance().saveProject(this.projectName, this.project, this.timeStamp, overwriteNewerProject); + this.projectManagerFacade.saveProject(this.projectName, this.project, this.timeStamp, overwriteNewerProject); CurrentAnalysisEditorBean.showMessage(FacesMessage.SEVERITY_INFO, "Project saved."); // Update the time stamp! this.resetTimeStamp(); @@ -670,6 +696,9 @@ public final class CurrentAnalysisEditorBean { CurrentAnalysisEditorBean.showMessage(FacesMessage.SEVERITY_WARN, "The project has been modified externally in the meanwhile."); // Give the user the possibility to force-save the project RequestContext.getCurrentInstance().execute("forceSaveDlg.show()"); + } catch (final ProjectNotExistingException ex) { + CurrentAnalysisEditorBean.LOG.error("The project does not exist.", ex); + CurrentAnalysisEditorBean.showMessage(FacesMessage.SEVERITY_ERROR, "The project does not exist."); } } } diff --git a/Kieker.WebGUI/src/main/java/kieker/webgui/beans/view/CurrentCockpitBean.java b/Kieker.WebGUI/src/main/java/kieker/webgui/beans/view/CurrentCockpitBean.java index f9039d87..59f43761 100644 --- a/Kieker.WebGUI/src/main/java/kieker/webgui/beans/view/CurrentCockpitBean.java +++ b/Kieker.WebGUI/src/main/java/kieker/webgui/beans/view/CurrentCockpitBean.java @@ -20,6 +20,7 @@ package kieker.webgui.beans.view; +import java.io.IOException; import java.lang.reflect.InvocationTargetException; import javax.faces.bean.ManagedBean; @@ -32,11 +33,12 @@ import kieker.analysis.display.Image; import kieker.analysis.model.analysisMetaModel.MIProject; import kieker.analysis.model.analysisMetaModel.MIView; import kieker.webgui.beans.application.ProjectsBean; -import kieker.webgui.common.ACManager; import kieker.webgui.common.ClassAndMethodContainer; -import kieker.webgui.common.FSManager; import kieker.webgui.common.Global; +import kieker.webgui.common.IProjectManagerFacade; +import kieker.webgui.common.ProjectManagerFacade; import kieker.webgui.common.exception.ProjectLoadException; +import kieker.webgui.common.exception.ProjectNotExistingException; /** * The {@link CurrentCockpitBean} contains the necessary data behind an instance of the cockpit. It provides methods to read the state of the currently @@ -66,7 +68,7 @@ public class CurrentCockpitBean { @ManagedProperty(value = "#{projectsBean}") private ProjectsBean projectsBean; - + private final IProjectManagerFacade projectManagerFacade = ProjectManagerFacade.getInstance(); private ClassAndMethodContainer classAndMethodContainer; /** @@ -130,16 +132,22 @@ public class CurrentCockpitBean { * @throws ProjectLoadException */ public void initalize() throws ProjectLoadException { - synchronized (this) { - // Make sure that the initialization will only be done for the init request. - if (!FacesContext.getCurrentInstance().isPostback() && (this.projectsBean != null)) { - // Remember the given parameters - this.project = this.projectsBean.openProject(this.projectName); + try { + synchronized (this) { + // Make sure that the initialization will only be done for the init request. + if (!FacesContext.getCurrentInstance().isPostback() && (this.projectsBean != null)) { + // Remember the given parameters + this.project = this.projectsBean.openProject(this.projectName); - if (this.project != null) { - this.classAndMethodContainer = new ClassAndMethodContainer(FSManager.getInstance().getClassLoader(this.projectName)); + if (this.project != null) { + this.classAndMethodContainer = new ClassAndMethodContainer(this.projectManagerFacade.getClassLoader(this.projectName)); + } } } + } catch (final ProjectNotExistingException ex) { + // TODO + } catch (final IOException ex) { + // TODO } } @@ -163,38 +171,43 @@ public class CurrentCockpitBean { * does exist, but the return content is not plain text, null will be returned. */ public String updatePlainTextDisplay(final String displayName) { - String result; + try { + String result; - synchronized (this) { - if ((this.activeView != null) && (this.projectName != null)) { - final Object displayObj = ACManager.getInstance().getDisplay(this.projectName, this.activeView.getName(), displayName); - if (displayObj == null) { - // The display does not exist - result = "N/A"; - } else { - if (this.classAndMethodContainer.getPlainTextClass().isAssignableFrom(displayObj.getClass())) { - // The display exists and is valid - try { - result = (String) (this.classAndMethodContainer.getPlainTextgetTextMethod().invoke(displayObj, new Object[0])); - } catch (final IllegalAccessException e) { - result = "Error"; - } catch (final IllegalArgumentException e) { - result = "Error"; - } catch (final InvocationTargetException e) { - result = "Error"; - } + synchronized (this) { + if ((this.activeView != null) && (this.projectName != null)) { + final Object displayObj = this.projectManagerFacade.getDisplay(this.projectName, this.activeView.getName(), displayName); + if (displayObj == null) { + // The display does not exist + result = "N/A"; } else { - // The display exists, but is not valid - result = null; + if (this.classAndMethodContainer.getPlainTextClass().isAssignableFrom(displayObj.getClass())) { + // The display exists and is valid + try { + result = (String) (this.classAndMethodContainer.getPlainTextgetTextMethod().invoke(displayObj, new Object[0])); + } catch (final IllegalAccessException e) { + result = "Error"; + } catch (final IllegalArgumentException e) { + result = "Error"; + } catch (final InvocationTargetException e) { + result = "Error"; + } + } else { + // The display exists, but is not valid + result = null; + } } + } else { + // The active view is not set + result = "N/A"; } - } else { - // The active view is not set - result = "N/A"; } - } - return result; + return result; + } catch (final Exception ex) { + return ""; + // TODO + } } /** @@ -206,30 +219,35 @@ public class CurrentCockpitBean { * does exist, but the return content is not html text, null will be returned. */ public String updateHtmlTextDisplay(final String displayName) { - final String result; + try { + final String result; - synchronized (this) { - if ((this.activeView != null) && (this.projectName != null)) { - final Object displayObj = ACManager.getInstance().getDisplay(this.projectName, this.activeView.getName(), displayName); - if (displayObj == null) { - // The display does not exist - result = "N/A"; - } else { - if (displayObj instanceof HtmlText) { - // The display exists and is valid - result = ((HtmlText) displayObj).toString(); + synchronized (this) { + if ((this.activeView != null) && (this.projectName != null)) { + final Object displayObj = this.projectManagerFacade.getDisplay(this.projectName, this.activeView.getName(), displayName); + if (displayObj == null) { + // The display does not exist + result = "N/A"; } else { - // The display exists, but is not valid - result = null; + if (displayObj instanceof HtmlText) { + // The display exists and is valid + result = ((HtmlText) displayObj).toString(); + } else { + // The display exists, but is not valid + result = null; + } } + } else { + // The active view is not set + result = "N/A"; } - } else { - // The active view is not set - result = "N/A"; } - } - return result; + return result; + } catch (final Exception ex) { + return ""; + // TODO + } } /** @@ -241,30 +259,35 @@ public class CurrentCockpitBean { * does exist, but the return content is not an image, null will be returned. */ public String updateImageDisplay(final String displayName) { - final String result; + try { + final String result; - synchronized (this) { - if ((this.activeView != null) && (this.projectName != null)) { - final Object displayObj = ACManager.getInstance().getDisplay(this.projectName, this.activeView.getName(), displayName); - if (displayObj == null) { - // The display does not exist - result = "N/A"; - } else { - if (displayObj instanceof Image) { - // The display exists and is valid - result = ((Image) displayObj).toString(); + synchronized (this) { + if ((this.activeView != null) && (this.projectName != null)) { + final Object displayObj = this.projectManagerFacade.getDisplay(this.projectName, this.activeView.getName(), displayName); + if (displayObj == null) { + // The display does not exist + result = "N/A"; } else { - // The display exists, but is not valid - result = null; + if (displayObj instanceof Image) { + // The display exists and is valid + result = ((Image) displayObj).toString(); + } else { + // The display exists, but is not valid + result = null; + } } + } else { + // The active view is not set + result = "N/A"; } - } else { - // The active view is not set - result = "N/A"; } - } - return result; + return result; + } catch (final Exception ex) { + return ""; + // TODO + } } /** diff --git a/Kieker.WebGUI/src/main/java/kieker/webgui/beans/view/CurrentCockpitEditorBean.java b/Kieker.WebGUI/src/main/java/kieker/webgui/beans/view/CurrentCockpitEditorBean.java index c624674d..19a35c2c 100644 --- a/Kieker.WebGUI/src/main/java/kieker/webgui/beans/view/CurrentCockpitEditorBean.java +++ b/Kieker.WebGUI/src/main/java/kieker/webgui/beans/view/CurrentCockpitEditorBean.java @@ -46,10 +46,12 @@ import kieker.analysis.plugin.AbstractPlugin; import kieker.common.logging.Log; import kieker.common.logging.LogFactory; import kieker.webgui.beans.application.ProjectsBean; -import kieker.webgui.common.FSManager; import kieker.webgui.common.Global; +import kieker.webgui.common.IProjectManagerFacade; +import kieker.webgui.common.ProjectManagerFacade; import kieker.webgui.common.exception.NewerProjectException; import kieker.webgui.common.exception.ProjectLoadException; +import kieker.webgui.common.exception.ProjectNotExistingException; import org.primefaces.context.RequestContext; import org.primefaces.event.TabChangeEvent; @@ -91,6 +93,7 @@ public class CurrentCockpitEditorBean { private MIView activeView; @ManagedProperty(value = "#{projectsBean}") private ProjectsBean projectsBean; + private final IProjectManagerFacade projectManagerFacade = ProjectManagerFacade.getInstance(); /** * Creates a new instance of this class. @@ -200,18 +203,21 @@ public class CurrentCockpitEditorBean { public void saveProject(final boolean overwriteNewerProject) { synchronized (this) { try { - FSManager.getInstance().saveProject(this.projectName, this.project, this.timeStamp, overwriteNewerProject); + this.projectManagerFacade.saveProject(this.projectName, this.project, this.timeStamp, overwriteNewerProject); CurrentCockpitEditorBean.showMessage(FacesMessage.SEVERITY_INFO, "Project saved."); // Update the time stamp! this.resetTimeStamp(); } catch (final IOException ex) { - CurrentCockpitEditorBean.LOG.error("An error occured while saving the projct.", ex); - CurrentCockpitEditorBean.showMessage(FacesMessage.SEVERITY_ERROR, "An error occured while saving the projct."); + CurrentCockpitEditorBean.LOG.error("An error occured while saving the projet.", ex); + CurrentCockpitEditorBean.showMessage(FacesMessage.SEVERITY_ERROR, "An error occured while saving the project."); } catch (final NewerProjectException ex) { CurrentCockpitEditorBean.LOG.info("The project has been modified externally in the meanwhile.", ex); CurrentCockpitEditorBean.showMessage(FacesMessage.SEVERITY_WARN, "The project has been modified externally in the meanwhile."); // Give the user the possibility to force-save the project RequestContext.getCurrentInstance().execute("forceSaveDlg.show()"); + } catch (final ProjectNotExistingException ex) { + CurrentCockpitEditorBean.LOG.error("A project with the given name does not exist.", ex); + CurrentCockpitEditorBean.showMessage(FacesMessage.SEVERITY_ERROR, "A project with the given name does not exist."); } } } diff --git a/Kieker.WebGUI/src/main/java/kieker/webgui/beans/view/CurrentControllerBean.java b/Kieker.WebGUI/src/main/java/kieker/webgui/beans/view/CurrentControllerBean.java index 9e84b72e..55bb5f8c 100644 --- a/Kieker.WebGUI/src/main/java/kieker/webgui/beans/view/CurrentControllerBean.java +++ b/Kieker.WebGUI/src/main/java/kieker/webgui/beans/view/CurrentControllerBean.java @@ -20,6 +20,8 @@ package kieker.webgui.beans.view; +import java.io.IOException; + import javax.faces.application.FacesMessage; import javax.faces.application.FacesMessage.Severity; import javax.faces.bean.ManagedBean; @@ -32,10 +34,12 @@ import kieker.analysis.model.analysisMetaModel.MIProject; import kieker.common.logging.Log; import kieker.common.logging.LogFactory; import kieker.webgui.beans.application.ProjectsBean; -import kieker.webgui.common.ACManager; import kieker.webgui.common.Global; +import kieker.webgui.common.IProjectManagerFacade; +import kieker.webgui.common.ProjectManagerFacade; import kieker.webgui.common.exception.AnalysisStateException; import kieker.webgui.common.exception.ProjectLoadException; +import kieker.webgui.common.exception.ProjectNotExistingException; /** * /** @@ -63,6 +67,7 @@ public class CurrentControllerBean { private MIProject project; @ManagedProperty(value = "#{projectsBean}") private ProjectsBean projectsBean; + private final IProjectManagerFacade projectManagerFacade = ProjectManagerFacade.getInstance(); /** * Creates a new instance of this class. @@ -164,11 +169,14 @@ public class CurrentControllerBean { public void startAnalysis() { try { synchronized (this) { - ACManager.getInstance().startAnalysisController(this.projectName); + this.projectManagerFacade.startAnalysis(this.projectName); } } catch (final AnalysisStateException ex) { CurrentControllerBean.LOG.info("The analysis has already been started.", ex); CurrentControllerBean.showMessage(FacesMessage.SEVERITY_WARN, "The analysis has already been started."); + } catch (final ProjectNotExistingException ex) { + CurrentControllerBean.LOG.info("The project does not exist.", ex); + CurrentControllerBean.showMessage(FacesMessage.SEVERITY_WARN, "The project does not exist."); } } @@ -178,11 +186,14 @@ public class CurrentControllerBean { public void stopAnalysis() { try { synchronized (this) { - ACManager.getInstance().stopAnalysisController(this.projectName); + this.projectManagerFacade.stopAnalysis(this.projectName); } } catch (final AnalysisStateException ex) { CurrentControllerBean.LOG.info("The analysis has not been started yet.", ex); CurrentControllerBean.showMessage(FacesMessage.SEVERITY_WARN, "The analysis has not been started yet."); + } catch (final ProjectNotExistingException ex) { + CurrentControllerBean.LOG.info("The project does not exist.", ex); + CurrentControllerBean.showMessage(FacesMessage.SEVERITY_WARN, "The project does not exist."); } } @@ -192,11 +203,17 @@ public class CurrentControllerBean { public void instantiateAnalysis() { try { synchronized (this) { - ACManager.getInstance().instantiateAnalysisController(this.projectName); + this.projectManagerFacade.initializeAnalysis(this.projectName, this.projectManagerFacade.getClassLoader(this.projectName)); } } catch (final AnalysisStateException ex) { CurrentControllerBean.LOG.error("The analysis has already been instantiated.", ex); CurrentControllerBean.showMessage(FacesMessage.SEVERITY_ERROR, "The analysis has already been instantiated."); + } catch (final ProjectNotExistingException ex) { + CurrentControllerBean.LOG.info("The project does not exist.", ex); + CurrentControllerBean.showMessage(FacesMessage.SEVERITY_WARN, "The project does not exist."); + } catch (final IOException ex) { + CurrentControllerBean.LOG.info("An error occured during the initialization.", ex); + CurrentControllerBean.showMessage(FacesMessage.SEVERITY_WARN, "An error occured during the initialization."); } } @@ -204,7 +221,17 @@ public class CurrentControllerBean { * This method cleans the current analysis instance. */ public void cleanAnalysis() { - ACManager.getInstance().cleanAnalysisController(this.projectName); + synchronized (this) { + try { + this.projectManagerFacade.cleanAnalysis(this.projectName); + } catch (final ProjectNotExistingException ex) { + CurrentControllerBean.LOG.info("The project does not exist.", ex); + CurrentControllerBean.showMessage(FacesMessage.SEVERITY_WARN, "The project does not exist."); + } catch (final AnalysisStateException ex) { + CurrentControllerBean.LOG.error("The analysis has not been instantiated yet.", ex); + CurrentControllerBean.showMessage(FacesMessage.SEVERITY_ERROR, "The analysis has not been instantiated yet."); + } + } } /** @@ -213,7 +240,14 @@ public class CurrentControllerBean { * @return true if and only if the analysis is running. */ public boolean isAnalysisRunning() { - return ACManager.getInstance().getAnalysisControllerState(this.projectName) == AnalysisController.STATE.RUNNING; + synchronized (this) { + try { + return this.projectManagerFacade.getCurrentState(this.projectName) == AnalysisController.STATE.RUNNING; + } catch (final ProjectNotExistingException ex) { + CurrentControllerBean.LOG.info("The project does not exist.", ex); + return false; + } + } } /** @@ -222,7 +256,14 @@ public class CurrentControllerBean { * @return true if and only if the analysis is ready to be started. */ public boolean isAnalysisReady() { - return ACManager.getInstance().getAnalysisControllerState(this.projectName) == AnalysisController.STATE.READY; + synchronized (this) { + try { + return this.projectManagerFacade.getCurrentState(this.projectName) == AnalysisController.STATE.READY; + } catch (final ProjectNotExistingException ex) { + CurrentControllerBean.LOG.info("The project does not exist.", ex); + return false; + } + } } /** @@ -231,7 +272,14 @@ public class CurrentControllerBean { * @return true if and only if the analysis is <b>not</b> available. */ public boolean isAnalysisNotAvailable() { - return ACManager.getInstance().getAnalysisControllerState(this.projectName) == null; + synchronized (this) { + try { + return this.projectManagerFacade.getCurrentState(this.projectName) == null; + } catch (final ProjectNotExistingException ex) { + CurrentControllerBean.LOG.info("The project does not exist.", ex); + return true; + } + } } /** @@ -240,7 +288,14 @@ public class CurrentControllerBean { * @return true if and only if the analysis has been terminated. */ public boolean isAnalysisTerminated() { - return ACManager.getInstance().getAnalysisControllerState(this.projectName) == AnalysisController.STATE.TERMINATED; + synchronized (this) { + try { + return this.projectManagerFacade.getCurrentState(this.projectName) == AnalysisController.STATE.TERMINATED; + } catch (final ProjectNotExistingException ex) { + CurrentControllerBean.LOG.info("The project does not exist.", ex); + return false; + } + } } /** @@ -249,7 +304,14 @@ public class CurrentControllerBean { * @return true if and only if the analysis has failed. */ public boolean isAnalysisFailed() { - return ACManager.getInstance().getAnalysisControllerState(this.projectName) == AnalysisController.STATE.FAILED; + synchronized (this) { + try { + return this.projectManagerFacade.getCurrentState(this.projectName) == AnalysisController.STATE.FAILED; + } catch (final ProjectNotExistingException ex) { + CurrentControllerBean.LOG.info("The project does not exist.", ex); + return false; + } + } } /** diff --git a/Kieker.WebGUI/src/main/java/kieker/webgui/common/ACManager.java b/Kieker.WebGUI/src/main/java/kieker/webgui/common/ACManager.java index 1b996cfd..2b66f0d9 100644 --- a/Kieker.WebGUI/src/main/java/kieker/webgui/common/ACManager.java +++ b/Kieker.WebGUI/src/main/java/kieker/webgui/common/ACManager.java @@ -20,13 +20,12 @@ package kieker.webgui.common; -import java.io.IOException; import java.util.concurrent.ConcurrentHashMap; -import kieker.analysis.AnalysisController; -import kieker.common.logging.Log; -import kieker.common.logging.LogFactory; +import kieker.analysis.AnalysisController.STATE; import kieker.webgui.common.exception.AnalysisStateException; +import kieker.webgui.common.exception.DisplayNotFoundException; +import kieker.webgui.common.exception.ProjectNotExistingException; /** * This manager is responsible for the currently used and running instances of {@code AnalysisController}. It supplies methods to check the states of analysis, @@ -35,12 +34,7 @@ import kieker.webgui.common.exception.AnalysisStateException; * @author Nils Christian Ehmke * @version 1.0 */ -// TODO Make this class threadsafe!! -public final class ACManager { - /** - * This is the log for errors, exceptions etc. - */ - private static final Log LOG = LogFactory.getLog(ACManager.class); +final class ACManager { /** * This is the singleton instance of this class. */ @@ -58,145 +52,104 @@ public final class ACManager { // No code necessary. } - /** - * Delivers the singleton instance of this class. - * - * @return The only instance of {@link FSManager}. - */ - public static ACManager getInstance() { - return ACManager.INSTANCE; + public void initializeAnalysis(final String projectName, final ClassLoader classLoader) + throws ProjectNotExistingException, AnalysisStateException { + // The analysis for the given project must not exist! + if (this.analyses.containsKey(projectName)) { + throw new AnalysisStateException("The analysis has not been cleaned yet."); + } + + final Analysis analysis = new Analysis(classLoader, FSManager.getInstance().getProjectFile(projectName)); + this.analyses.put(projectName, analysis); } - /** - * This method tries to stop a running analysis. - * - * @param project - * The project to be stopped. - * @throws AnalysisNotStartedException - * If the analysis is not running or has not yet been initialized. - * @throws InterruptedException - * If the current thread has somehow been interrupted while waiting for the existing analysis thread. - */ - public void stopAnalysisController(final String project) throws AnalysisStateException { - final Analysis analysis = this.analyses.get(project); + public void cleanAnalysis(final String projectName) throws AnalysisStateException { + // The analysis for the given project must exist! + if (!this.analyses.containsKey(projectName)) { + // Nothing to do + return; + } + final Analysis analysis = this.analyses.get(projectName); - if (analysis != null) { - analysis.stop(); - } else { - throw new AnalysisStateException("The analysis has not been started yet."); + // The analysis for the given project must not run + if (STATE.RUNNING.toString().equals(analysis.getCurrentState().toString())) { + throw new AnalysisStateException("The analysis is still running."); } - } - /** - * This method tries to start the analysis for the given project. It has already to be initialized. - * - * @param project - * The project to be started. - * @throws ProjectAlreadyStartedException - * If the project is already running. - * @throws AnalysisNotInstantiatedException - * If the analysis has not yet been initialized. - */ - public void startAnalysisController(final String project) throws AnalysisStateException { - final Analysis analysis = this.analyses.get(project); + this.analyses.remove(projectName); + } - if (analysis != null) { - analysis.start(); - } else { + public void startAnalysis(final String projectName) throws ProjectNotExistingException, AnalysisStateException { + // The analysis for the given project must exist! + if (!this.analyses.containsKey(projectName)) { throw new AnalysisStateException("The analysis has not been initialized yet."); } + final Analysis analysis = this.analyses.get(projectName); + + // The analysis for the given project must be ready + if (!STATE.READY.toString().equals(analysis.getCurrentState().toString())) { + throw new AnalysisStateException("The analysis is not ready."); + } + + analysis.start(); } - /** - * This method cleans the analysis controller of the given project. In other words: The potential running instance is stopped and removed. After a call to this - * method, the project can be restarted. Potential exceptions will be ignored - * - * @param project - * The name of the project to be cleaned. - */ - public void cleanAnalysisController(final String project) { - final Analysis analysis = this.analyses.get(project); + public void stopAnalysis(final String projectName) throws ProjectNotExistingException, AnalysisStateException { + // The analysis for the given project must exist! + if (!this.analyses.containsKey(projectName)) { + throw new AnalysisStateException("The analysis has not been initialized yet."); + } + final Analysis analysis = this.analyses.get(projectName); - if (analysis != null) { - analysis.stop(); - this.analyses.remove(project); + // The analysis for the given project must be running + if (!STATE.RUNNING.toString().equals(analysis.getCurrentState().toString())) { + throw new AnalysisStateException("The analysis is not running."); } + + analysis.stop(); } - /** - * This method tries to instantiate the analysis controller of the given project using the data available on the file system. - * - * @param project - * The project whose analysis controller should be instantiated. - * @throws AnalysisInstantiationException - */ - public void instantiateAnalysisController(final String project) throws AnalysisStateException { - try { - if (!this.analyses.containsKey(project)) { - FSManager.getInstance().openProject(project); - FSManager.getInstance().getClassLoader(project); - - // TODO - // this.analyses.put(project, new Analysis(classLoader, modelProject)); - } else { - throw new AnalysisStateException("The analysis has already been initialized."); - } - } catch (final IOException ex) { - throw new AnalysisStateException("An IO-error occured while instantiating the analysis.", ex); + public Object getDisplay(final String projectName, final String viewName, final String displayName) throws ProjectNotExistingException, DisplayNotFoundException { + // The analysis for the given project must exist! + if (!this.analyses.containsKey(projectName)) { + throw new ProjectNotExistingException("The analysis has not been initialized yet."); } - } - /** - * This method can be used to deliver the state of the analysis controller of the given project as a human readable string. - * - * @param project - * The project whose state should be delivered. - * @return The current state of the corresponding {@link AnalysisController} if it exists, '' otherwise. - */ - public String getAnalysisControllerStateString(final String project) { - final Enum<?> state = this.getAnalysisControllerState(project); + return this.analyses.get(projectName).getDisplay(viewName, displayName); + } - if (state != null) { - return state.toString(); - } else { - return null; + public STATE getCurrentState(final String projectName) throws ProjectNotExistingException { + // The analysis for the given project must exist! + if (!this.analyses.containsKey(projectName)) { + throw new ProjectNotExistingException("The analysis has not been initialized yet."); } - } - /** - * This method can be used to deliver the state of the analysis controller of the given project as a human readable string. - * - * @param project - * The project whose state should be delivered. - * @return The current state of the corresponding {@link AnalysisController} if it exists, null otherwise. - */ - public Enum<?> getAnalysisControllerState(final String project) { - final Analysis analysis = this.analyses.get(project); + final Analysis analysis = this.analyses.get(projectName); + final Enum<?> state = analysis.getCurrentState(); - if (analysis != null) { - return analysis.getCurrentState(); - } else { - return null; + if (STATE.FAILED.toString().equals(state.toString())) { + return STATE.FAILED; + } + if (STATE.READY.toString().equals(state.toString())) { + return STATE.READY; } + if (STATE.RUNNING.toString().equals(state.toString())) { + return STATE.RUNNING; + } + if (STATE.TERMINATED.toString().equals(state.toString())) { + return STATE.TERMINATED; + } + + throw new ProjectNotExistingException("Unknown state."); } /** - * Delivers the display object for the given display in the given view in the given project. + * Delivers the singleton instance of this class. * - * @param project - * The name of the project. - * @param viewName - * The name of the view. - * @param displayName - * The name of the display. - * @return The display object if it exists, null otherwise. + * @return The only instance of {@link FSManager}. */ - public Object getDisplay(final String project, final String viewName, final String displayName) { - final Analysis analysis = this.analyses.get(project); - if (analysis != null) { - return analysis.getDisplay(viewName, displayName); - } else { - return null; - } + public static ACManager getInstance() { + return ACManager.INSTANCE; } + } diff --git a/Kieker.WebGUI/src/main/java/kieker/webgui/common/FSManager.java b/Kieker.WebGUI/src/main/java/kieker/webgui/common/FSManager.java index 58c79d60..7aae4fee 100644 --- a/Kieker.WebGUI/src/main/java/kieker/webgui/common/FSManager.java +++ b/Kieker.WebGUI/src/main/java/kieker/webgui/common/FSManager.java @@ -24,9 +24,9 @@ import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; +import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; -import java.math.RoundingMode; import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; @@ -34,12 +34,11 @@ import java.nio.channels.ByteChannel; import java.nio.channels.FileChannel; import java.security.AccessController; import java.security.PrivilegedAction; -import java.text.DecimalFormat; -import java.text.NumberFormat; import java.util.ArrayList; import java.util.Collection; import java.util.List; -import java.util.concurrent.ConcurrentHashMap; + +import javax.annotation.PostConstruct; import kieker.analysis.AnalysisController; import kieker.analysis.model.analysisMetaModel.MIAnalysisMetaModelFactory; @@ -51,19 +50,19 @@ import kieker.common.logging.LogFactory; import kieker.webgui.common.exception.LibraryAlreadyExistingException; import kieker.webgui.common.exception.NewerProjectException; import kieker.webgui.common.exception.ProjectAlreadyExistingException; +import kieker.webgui.common.exception.ProjectNotExistingException; import org.primefaces.model.UploadedFile; /** * This is a singleton class for the access to the file system. It makes sure that the necessary directories for the execution of the application exist. <b>Do * not</b> remove directories created from this manager during runtime! Directories are created during first access to the class.<br> - * This class uses also a fine grained synchronization to handle the access to the projects. + * This class <b>does not</b> use any kind of synchronization to handle the access to the projects. * * @author Nils Christian Ehmke + * @version 1.0 */ -// TODO Projects have to check their lock once they entered the synchronized block in order to make sure that they have still the valid one (and not a removed one -// from an older project) -public final class FSManager { // NOCS (Class Data Abstraction Coupling, Class Fan-Out Complexity) +final class FSManager { /** * This is the log object used to log messages, warnings etc. */ @@ -85,10 +84,9 @@ public final class FSManager { // NOCS (Class Data Abstraction Coupling, Class F */ private static final String ROOT_DIRECTORY = "data"; /** - * The library for kieker which is contained in the war-file as a ressource. + * The library for kieker which is contained in the war-file as a resource. */ private static final String KIEKER_LIB = "kieker-1.6-SNAPSHOT_emf.jar"; - /** * This is the buffer (in bytes) used to copy and upload files. */ @@ -101,15 +99,16 @@ public final class FSManager { // NOCS (Class Data Abstraction Coupling, Class F * This is the factory we will use to create the elements for the projects. */ private final MIAnalysisMetaModelFactory factory = new MAnalysisMetaModelFactory(); - /** - * This map contains one lock-object for every project (by name). This works at every project name may exist only once. - */ - private final ConcurrentHashMap<String, Object> projectLocksMap = new ConcurrentHashMap<String, Object>(); /** * Creates a new instance of this class. */ private FSManager() { + // No code necessary + } + + @PostConstruct + protected void initialize() { // Check for our root-directory and create it if necessary final File rootDir = new File(FSManager.ROOT_DIRECTORY); if (!rootDir.exists()) { @@ -130,254 +129,381 @@ public final class FSManager { // NOCS (Class Data Abstraction Coupling, Class F } /** - * This method adds a new project to the application. If the method fails due to an {@link IOException}, it will make sure that the project-directories will be - * removed as if the method would never have been called. + * This method adds a new project to the application. It creates an empty, but nevertheless valid kax-file to the file system. If the method fails due to an + * {@link IOException}, it will make sure that the project-directories will be removed as if the method would never have been called. * - * @param project - * The project to be added. - * @throws IOException - * If a write-error occurred during the creation. + * @param projectName + * The name of the new project. * @throws ProjectAlreadyExistingException * If a project with the same name exists already. + * @throws IOException + * If something went wrong during the creation of the project. */ - public void addProject(final String project) throws IOException, ProjectAlreadyExistingException { - // Get the lock for the new project - final Object lock = this.getLock(project); - - // Assemble the paths to the directory and to the files for the given project - final File projectDir = new File(FSManager.ROOT_DIRECTORY + File.separator + project); - final File projectFile = new File(FSManager.ROOT_DIRECTORY + File.separator + project + File.separator + project + "." + FSManager.KAX_EXTENSION); - final File libDir = new File(FSManager.ROOT_DIRECTORY + File.separator + project + File.separator + FSManager.LIB_DIRECTORY); + public void addProject(final String projectName) throws ProjectAlreadyExistingException, IOException { + // Assemble all necessary paths and files for the given project + final File projectDir = this.assembleProjectDir(projectName); + final File projectFile = this.assembleKaxFile(projectName); + final File libDir = this.assembleLibDir(projectName); // We need an "empty" project in order to save it. - final MIProject emptyProject = this.factory.createProject(); - // Create the directories and the empty file atomically - synchronized (lock) { - // Make sure that the project doesn't exist already - if (projectDir.exists()) { - throw new ProjectAlreadyExistingException("The project with the name '" + project + "' exists already."); + // Make sure that the project doesn't exist already + if (projectDir.exists()) { + throw new ProjectAlreadyExistingException("The project with the name '" + projectName + "' exists already."); + } + + try { + // Try to create the directories + if (projectDir.mkdir() && libDir.mkdir()) { + // Try to save the file + AnalysisController.saveToFile(projectFile, emptyProject); + } else { + // The directories could not be created + throw new IOException("Project-Directories could not be created."); } + } catch (final IOException ex) { + // Something went wrong. Remove the directories and files! + final boolean libDirDeleted = libDir.delete(); + // Keep in mind that the potential remains of the file have to be deleted before the directory. + final boolean projectFileDeleted = projectFile.delete(); + final boolean projectDeleted = projectDir.delete(); - // Create the directories - final boolean projDirCreated = projectDir.mkdir(); - final boolean libDirCreated = libDir.mkdir(); // The following part is only necessary to calm FindBugs... @SuppressWarnings("unused") - final boolean createResults = projDirCreated && libDirCreated; + final boolean deleteResults = libDirDeleted && projectFileDeleted && projectDeleted; - // Now the empty project file - try { - AnalysisController.saveToFile(projectFile, emptyProject); - } catch (final IOException ex) { // NOPMD (Rethrow to clean the directories) - // Something went wrong. Remove the directories and files! - final boolean libDirDeleted = libDir.delete(); - // Keep in mind that the potential remains of the file have to be deleted before the directory. - final boolean projectFileDeleted = projectFile.delete(); - final boolean projectDeleted = projectDir.delete(); - - // The following part is only necessary to calm FindBugs... - @SuppressWarnings("unused") - final boolean deleteResults = libDirDeleted && projectFileDeleted && projectDeleted; - // Rethrow the exception in order to inform the caller of this method - throw ex; + // Rethrow the exception in order to inform the caller of this method + throw ex; + } + } + + /** + * This method makes a copy of a project and saves it under another name. If the method fails due to an {@link IOException}, it will make sure that the + * project-directories of the destination-project will be removed as if the method would never have been called. + * + * @param originalProjectName + * The name of the source project. + * @param newProjectName + * The name of the target project. + * @throws ProjectNotExistingException + * If a project with the given (source) name doesn't exist. + * @throws ProjectAlreadyExistingException + * If a project with the same (target) name exists already. + * @throws IOException + * If something went wrong during the creation of the target-project or during the loading of the source-project. + */ + public void copyProject(final String originalProjectName, final String newProjectName) throws ProjectNotExistingException, ProjectAlreadyExistingException, + IOException { + // Get the necessary paths + final File dstProjDir = this.assembleProjectDir(newProjectName); + final File srcLibDir = this.assembleLibDir(originalProjectName); + final File dstLibDir = this.assembleLibDir(newProjectName); + + // Check whether the project exists already! + if (this.projectExists(newProjectName)) { + throw new ProjectAlreadyExistingException("A project with the name '" + newProjectName + "' exists already."); + } + + final File srcKaxFile = this.assembleKaxFile(originalProjectName); + final File dstKaxFile = this.assembleKaxFile(newProjectName); + + try { + if (dstProjDir.mkdir() && dstLibDir.mkdir()) { + // Copy the kax file + this.copyFile(srcKaxFile, dstKaxFile); + + // Copy the libs + for (final File lib : srcLibDir.listFiles()) { + final File dstLibFile = new File(dstLibDir, lib.getName()); + this.copyFile(lib, dstLibFile); + } + } else { + throw new IOException("Could not create directories."); } + } catch (final IOException ex) { + // Something went wrong. Remove the directories and files! + final boolean libDirDeleted = dstLibDir.delete(); + // Keep in mind that the potential remains of the file have to be deleted before the directory. + final boolean projectFileDeleted = dstKaxFile.delete(); + final boolean projectDeleted = dstProjDir.delete(); + + // The following part is only necessary to calm FindBugs... + @SuppressWarnings("unused") + final boolean deleteResults = libDirDeleted && projectFileDeleted && projectDeleted; + + // Rethrow the exception in order to inform the caller of this method + throw ex; } } /** - * This method opens a project atomically, using the current version on the FS. + * This method loads the kax-file for the given project name and delivers an initializes instance of {@link MIProject}. * - * @param project - * The project to be loaded. - * @return An instance of {@link MIProject} containing all informations from the project. + * @param projectName + * The name of the project to be loaded. + * @return The model instance as defined by the corresponding kax-file. + * @throws ProjectNotExistingException + * If a project with the given (source) name doesn't exist. * @throws IOException - * If something went wrong during opening the file. + * If something went wrong during the opening of the project. */ - public MIProject openProject(final String project) throws IOException { - if (project == null) { + public MIProject openProject(final String projectName) throws ProjectNotExistingException, IOException { + if (projectName == null) { throw new IOException("Project is null"); } - // Get the lock for the given project - final Object lock = this.getLock(project); - // Assemble the path to the KAX-file - final File kaxFile = new File(FSManager.ROOT_DIRECTORY + File.separator + project + File.separator + project + "." + FSManager.KAX_EXTENSION); - - // Load it atomically - synchronized (lock) { - return AnalysisController.loadFromFile(kaxFile.getAbsoluteFile()); // /kaxFile); + try { + // Load the project + return AnalysisController.loadFromFile(this.assembleKaxFile(projectName).getAbsoluteFile()); + } catch (final FileNotFoundException ex) { + throw new ProjectNotExistingException("A project with the name '" + projectName + "' does not exist.", ex); } } - public Object openProject(final String project, final ClassAndMethodContainer classAndMethodContainer) { - // TODO Correct and synchronize - // return ClassAndMethodContainer.invokeClassMethod(this.classAndMethodContainer.getAnalysisControllerLoadFromFile(), null, projectFile); - return null; + /** + * This method loads the kax-file for the given project name and delivers an initializes instance of {@link MIProject} - but instead of using the "normal" class + * loader, it uses the methods and classes stored in the given instance of {@link ClassAndMethodContainer}. This means that this method <b>does</b> return an + * instance of {@link MIProject}, but the one defined in the container. This is also the reason why this method has to return an {@link Object}-instance. + * + * @param projectName + * The name of the project to be loaded. + * @param classAndMethodContainer + * The container, which will be used to load the project instance. + * @return The model instance as defined by the corresponding kax-file. + * @throws ProjectNotExistingException + * If a project with the given (source) name doesn't exist. + * @throws IOException + * If something went wrong during the opening of the project. This can also mean that the given {@link ClassAndMethodContainer} is somehow invalid. + */ + public Object openProject(final String projectName, final ClassAndMethodContainer classAndMethodContainer) throws ProjectNotExistingException, IOException { + if (projectName == null) { + throw new IOException("Project is null"); + } + + // Load the project + final Object project = ClassAndMethodContainer.invokeClassMethod(classAndMethodContainer.getAnalysisControllerLoadFromFile(), null, + this.assembleKaxFile(projectName)); + + if (project == null) { + throw new IOException("Project could not be loaded."); + } else { + return project; + } } /** - * This method tries to save the given project. + * This method tries to save the given model instance for the given project. The given time stamp will be compared (if the corresponding flag says so) with the + * current time stamp of the project. If the project on the file system has been modified in the meantime, a {@link NewerProjectException} will be thrown. If + * something goes wrong during the storage, it is <b>not</b> guaranteed that the resulting file will be valid. * * @param projectName - * The name of the project to be saved. + * The name of the project which has to be saved. * @param project - * The project to be saved. + * The model instance to be stored in the corresponding kax-file. * @param timeStamp - * The time stamp, the given project has been opened (necessary to check for a newer version). + * The time stamp which has to be compared with the "real" time stamp of the project. * @param overwriteNewerProject - * This flag determines whether a newer project should be overwritten. + * Determines whether a newer project file will be overwritten without further warning or not- + * @throws ProjectNotExistingException + * If a project with the given name does not exist. * @throws IOException - * If something went wrong during saving the project. + * If something went wrong during the storage of the model instance. * @throws NewerProjectException - * This exception is raised if the time stamp of the project on the file system is newer than the given one <b>and</b> if the flag - * {@code overwriteNewerProject} is false. If it is true, a newer version will be overwritten. + * If the project on the file system is newer and the overwriteNewerProject-flag has not been set. */ - public void saveProject(final String projectName, final MIProject project, final long timeStamp, final boolean overwriteNewerProject) throws IOException, - NewerProjectException { - // Get the lock - final Object lock = this.getLock(projectName); - synchronized (lock) { - // Check for a newer version first - final long currTimeStamp = this.getCurrTimeStamp(projectName); - if (!overwriteNewerProject && (currTimeStamp > timeStamp)) { - throw new NewerProjectException("The project with the name '" + projectName + "' has a newer version on the FS."); - } + public void saveProject(final String projectName, final MIProject project, final long timeStamp, final boolean overwriteNewerProject) throws + ProjectNotExistingException, IOException, NewerProjectException { + // Check whether the project exists + if (!this.projectExists(projectName)) { + throw new ProjectNotExistingException("A project with the name '" + projectName + "' does not exist."); + } + + // Check for a newer version first + final long currTimeStamp = this.getCurrTimeStamp(projectName); + if (!overwriteNewerProject && (currTimeStamp > timeStamp)) { + throw new NewerProjectException("The project with the name '" + projectName + "' has a newer version on the FS."); + } - // Everything seems to be okay (or we should overwrite the current file). Try to save it. Assemble the path to the KAX-file for this purpose. - final File kaxFile = new File(FSManager.ROOT_DIRECTORY + File.separator + projectName + File.separator + projectName + "." + FSManager.KAX_EXTENSION); + // Try to save it. + AnalysisController.saveToFile(this.assembleKaxFile(projectName), project); + } - // Try to save it. - AnalysisController.saveToFile(kaxFile, project); + /** + * Delivers the current time stamp of the given project. + * + * @param projectName + * The name of the project whose time stamp will be delivered. + * @return The current time stamp. + * @throws ProjectNotExistingException + * If a project with the given name does not exist. + */ + public long getCurrTimeStamp(final String projectName) throws ProjectNotExistingException { + // Check whether the project exists + if (!this.projectExists(projectName)) { + throw new ProjectNotExistingException("A project with the name '" + projectName + "' does not exist."); } + return this.assembleKaxFile(projectName).lastModified(); } /** - * This method renames a project. In other words: The whole project will be copied and the old one removed (but atomically in respect to both project names). + * This method tries to upload a dependency to the given project. * + * @param file + * The file to be uploaded to the project. * @param projectName - * The name of the old project. - * @param newName - * The new name of the project. + * The name of the project. + * @throws ProjectNotExistingException + * If a project with the given name does not exist. * @throws IOException - * If something goes wrong during the rename-procedure. - * @throws ProjectAlreadyExistingException - * If a project with the given new name exists already. + * If something went wrong during the uploading. */ - public void renameProject(final String projectName, final String newName) throws IOException, ProjectAlreadyExistingException { - if (projectName.equals(newName)) { - throw new ProjectAlreadyExistingException("A project with the name '" + newName + "' exists already."); + public void uploadLibrary(final UploadedFile file, final String projectName) throws ProjectNotExistingException, IOException, LibraryAlreadyExistingException { + // Check whether the project exists + if (!this.projectExists(projectName)) { + throw new ProjectNotExistingException("A project with the name '" + projectName + "' does not exist."); } - // Get both locks - final Object lockOld = this.getLock(projectName); - final Object lockNew = this.getLock(newName); - - // We cannot risk just using two synchronized-blocks as such a thing could block the application. We therefore have to sort the locks by name in order to - // make sure that two methods would use the same order to access (if project A has to be renamed to B and B has to be renamed to A, we would always - // synchronize on A before synchronizing on B). - final Object lockFst; - final Object lockSnd; - if (projectName.compareTo(newName) < 0) { - lockFst = lockOld; - lockSnd = lockNew; - } else { - lockFst = lockNew; - lockSnd = lockOld; + + // Prepare the files + final File libDir = this.assembleLibDir(projectName); + final File dstFile = new File(libDir, file.getFileName()); + + // Now copy the file - if it doesn't already exist + if (dstFile.exists()) { + throw new LibraryAlreadyExistingException("The library with the name '" + file.getFileName() + "' exists already."); } - // Access the projects atomically - synchronized (lockFst) { - synchronized (lockSnd) { - // Check whether the project exists already! - if (this.projectExists(newName)) { - throw new ProjectAlreadyExistingException("A project with the name '" + newName + "' exists already."); + BufferedInputStream in = null; + BufferedOutputStream out = null; + IOException occException = null; + try { + // Get the streams. + in = new BufferedInputStream(file.getInputstream()); + out = new BufferedOutputStream(new FileOutputStream(dstFile)); + final byte[] buf = new byte[FSManager.BUF_SIZE_BYTES]; + int count; + + // Transfer the file. + count = in.read(buf); + while (count != -1) { + out.write(buf, 0, count); + count = in.read(buf); + } + } finally { + // Try to make sure that the streams will be closed. + try { + if (in != null) { + in.close(); } + } catch (final IOException ex) { + occException = ex; + } - // Otherwise copy the project and delete the old one - this.copyProject(projectName, newName); - this.deleteProject(projectName); + try { + if (out != null) { + out.close(); + } + } catch (final IOException ex) { + occException = ex; } } + // If something went wrong, rethrow the exception + if (occException != null) { + throw occException; + } } /** - * This method deletes the project with the given name. + * This method delivers a class loader containing the currently available libraries of the given project. * * @param projectName - * The name of the project to be removed. + * The name of the project. + * @return A class loader for the given project. + * @throws ProjectNotExistingException + * If a project with the given name does not exist. + * @throws IOException + * If something went wrong during the initialization of the class loader. */ - public void deleteProject(final String projectName) { - // TODO Implement + public ClassLoader getClassLoader(final String projectName) throws ProjectNotExistingException, IOException { + // Check whether the project exists + if (!this.projectExists(projectName)) { + throw new ProjectNotExistingException("A project with the name '" + projectName + "' does not exist."); + } + + final List<URL> libs = new ArrayList<URL>(); + + // Collect all libraries of the project + + // Run through the libs and put them into our list. + final File libDir = new File(FSManager.ROOT_DIRECTORY + File.separator + projectName + File.separator + FSManager.LIB_DIRECTORY); + final File[] files = libDir.listFiles(); + if (files != null) { + for (final File file : files) { + if (file.getName().endsWith("." + FSManager.LIB_EXTENSION)) { + try { + libs.add(file.toURL()); + } catch (final MalformedURLException ex) { + ex.printStackTrace(); + } + } + } + } + + // Add the kieker lib! + libs.add(this.getKiekerURL()); + + // Now assemble the URL class loader + final PrivilegedClassLoaderAction action = new PrivilegedClassLoaderAction(libs); + return AccessController.doPrivileged(action); } /** - * This method copies the given project and saves it under the given name. + * This method lists all available libraries of the given project. * * @param projectName - * The name of the project to be copied. - * @param newName - * The name of the new project. - * @throws ProjectAlreadyExistingException - * If a project with the given name exists already. - * @throws IOException - * If something goes wrong during the copy procedure. + * The name of the project whose libraries have to be delivered. + * @return A list containing all available library-names of the project. + * @throws ProjectNotExistingException + * If a project with the given name does not exist. */ - public void copyProject(final String projectName, final String newName) throws ProjectAlreadyExistingException, IOException { - if (projectName.equals(newName)) { - throw new ProjectAlreadyExistingException("A project with the name '" + newName + "' exists already."); - } - // Get both locks - final Object lockOld = this.getLock(projectName); - final Object lockNew = this.getLock(newName); - - // We cannot risk just using two synchronized-blocks as such a thing could block the application. We therefore have to sort the locks by name in order to - // make sure that two methods would use the same order to access. - final Object lockFst; - final Object lockSnd; - if (projectName.compareTo(newName) < 0) { - lockFst = lockOld; - lockSnd = lockNew; - } else { - lockFst = lockNew; - lockSnd = lockOld; + public List<String> listAllLibraries(final String projectName) throws ProjectNotExistingException { + // Check whether the project exists + if (!this.projectExists(projectName)) { + throw new ProjectNotExistingException("A project with the name '" + projectName + "' does not exist."); } - // Access the projects atomically - synchronized (lockFst) { - synchronized (lockSnd) { - // Check whether the project exists already! - if (this.projectExists(newName)) { - throw new ProjectAlreadyExistingException("A project with the name '" + newName + "' exists already."); - } - - // Get the necessary paths - final File dstProjDir = new File(FSManager.ROOT_DIRECTORY + File.separator + newName); - final File srcLibDir = new File(FSManager.ROOT_DIRECTORY + File.separator + projectName + File.separator + FSManager.LIB_DIRECTORY); - final File dstLibDir = new File(FSManager.ROOT_DIRECTORY + File.separator + newName + File.separator + FSManager.LIB_DIRECTORY); - - final File srcKaxFile = new File(FSManager.ROOT_DIRECTORY + File.separator + projectName + File.separator + projectName + "." - + FSManager.KAX_EXTENSION); - final File dstKaxFile = new File(FSManager.ROOT_DIRECTORY + File.separator + newName + File.separator + newName + "." + FSManager.KAX_EXTENSION); + final List<String> result = new ArrayList<String>(); - // Create the directories - final boolean projDirCreated = dstProjDir.mkdir(); - final boolean libDirCreated = dstLibDir.mkdir(); - // The following part is only necessary to calm FindBugs... - @SuppressWarnings("unused") - final boolean createResults = projDirCreated && libDirCreated; + // Run through the libs and put them into our list. + final File[] files = this.assembleLibDir(projectName).listFiles(); + if (files != null) { + for (final File file : files) { + if (file.getName().endsWith("." + FSManager.LIB_EXTENSION)) { + result.add(file.getName()); + } + } + } - // Copy the kax file - this.copyFile(srcKaxFile, dstKaxFile); + return result; + } - // Copy the libs - for (final File lib : srcLibDir.listFiles()) { - final File dstLibFile = new File(dstLibDir, lib.getName()); - this.copyFile(lib, dstLibFile); - } + /** + * This method lists all available projects on the file system. + * + * @return A list containing all available project names. + */ + public Collection<String> listAllProjects() { + final List<String> result = new ArrayList<String>(); + // Get all directories within our root-dir + final File[] files = new File(FSManager.ROOT_DIRECTORY).listFiles(); + for (final File file : files) { + if (file.isDirectory()) { + result.add(file.getName()); } } + + return result; } /** @@ -464,222 +590,8 @@ public final class FSManager { // NOCS (Class Data Abstraction Coupling, Class F * The name of the project. * @return true if and only if a directory with the name of the project exists in the root dir. */ - private boolean projectExists(final String project) { - // Assemble the path to the given project - final String fileName = FSManager.ROOT_DIRECTORY + File.separator + project; - final File file = new File(fileName); - - return file.exists(); - } - - /** - * Returns a list containing all available projects on the FS as a string. - * - * @return A list of string containing all available project. - */ - public Collection<? extends String> getAllProjects() { - final List<String> result = new ArrayList<String>(); - - // Get all directories within our root-dir - final File[] files = new File(FSManager.ROOT_DIRECTORY).listFiles(); - for (final File file : files) { - if (file.isDirectory()) { - result.add(file.getName()); - } - } - - return result; - } - - /** - * This method returns the current time stamp of the given project. The time stamp is the last modification time of the KAX-file. - * - * @param project - * The project whose time stamp should be delivered. - * @return The time stamp of the last modification. - */ - public long getCurrTimeStamp(final String project) { - // Assemble the path to the kax-file of the given project - final String fileName = FSManager.ROOT_DIRECTORY + File.separator + project + File.separator + project + "." + FSManager.KAX_EXTENSION; - - // Try to find this file - final File file = new File(fileName); - - // Get the time stamp atomically - final Object lock = this.getLock(project); - - final long ts; - synchronized (lock) { - ts = file.lastModified(); - } - - // Return the time stamp - return ts; - } - - /** - * This method can be used to get the lock for a given project. If the lock doesn't exist already, a new one will be created. - * - * @param project - * The project whose lock should be delivered. - * @return A lock object for the project. - */ - private Object getLock(final String project) { - final Object newLock = new Object(); - final Object existLock = this.projectLocksMap.putIfAbsent(project, newLock); - - final Object lock; - if (existLock != null) { - lock = existLock; - } else { - lock = newLock; - } - return lock; - } - - /** - * This method can be used to upload a new library. - * - * @param srcFile - * The file to be used as a new library. - * @param project - * The name of the project for the new lib. - * @return An instance of {@link MIDependency} if everything went well. - * @throws LibraryAlreadyExistingException - * If a library with the same name for the project exists already. - * @throws IOException - * If something went wrong during writing the file. - */ - public MIDependency uploadLibrary(final UploadedFile srcFile, final String project) throws LibraryAlreadyExistingException, IOException { - // Get the lock for this project - final Object lock = this.getLock(project); - - // Prepare the files - final File libDir = new File(FSManager.ROOT_DIRECTORY + File.separator + project + File.separator + FSManager.LIB_DIRECTORY); - final File dstFile = new File(libDir, srcFile.getFileName()); - - // Now copy the file - if it doesn't already exist - synchronized (lock) { - if (dstFile.exists()) { - throw new LibraryAlreadyExistingException("The library with the name '" + srcFile.getFileName() + "' exists already."); - } - - BufferedInputStream in = null; - BufferedOutputStream out = null; - IOException occException = null; - try { - // Get the streams. - in = new BufferedInputStream(srcFile.getInputstream()); - out = new BufferedOutputStream(new FileOutputStream(dstFile)); - final byte[] buf = new byte[FSManager.BUF_SIZE_BYTES]; - int count; - - // Transfer the file. - count = in.read(buf); - while (count != -1) { - out.write(buf, 0, count); - count = in.read(buf); - } - } finally { - // Try to make sure that the streams will be closed. - try { - if (in != null) { - in.close(); - } - } catch (final IOException ex) { - occException = ex; - } - - try { - if (out != null) { - out.close(); - } - } catch (final IOException ex) { - occException = ex; - } - } - // If something went wrong, rethrow the exception - if (occException != null) { - throw occException; - } - } - - final MIDependency dependency = this.factory.createDependency(); - dependency.setFilePath(dstFile.getName()); - return dependency; - } - - /** - * This method delivers all available libraries for the given project as a pair of strings. The first element is the name of the library, the second one the size - * of the lib in MiBytes and as a human readable string. If the given name is null, an empty list will be returned. - * - * @param projectName - * The name of the project. - * @return The libraries of the given project. - */ - public List<Pair<String, String>> getLibraries(final String projectName) { - final List<Pair<String, String>> result = new ArrayList<Pair<String, String>>(); - if (projectName != null) { - // Get the lock for the project - final Object lock = this.getLock(projectName); - - synchronized (lock) { - // Run through the libs and put them into our list. - final File libDir = new File(FSManager.ROOT_DIRECTORY + File.separator + projectName + File.separator + FSManager.LIB_DIRECTORY); - final File[] files = libDir.listFiles(); - if (files != null) { - for (final File file : files) { - if (file.getName().endsWith("." + FSManager.LIB_EXTENSION)) { - final float len = file.length() / 1024.0f / 1024.0f; - final NumberFormat numberFormat = new DecimalFormat("0.00"); - numberFormat.setRoundingMode(RoundingMode.DOWN); - final String lenStr = numberFormat.format(len); - result.add(new Pair<String, String>(file.getName(), lenStr)); - } - } - } - } - } - - return result; - } - - /** - * This method can be used to deliver the class loader for the given project. The class loader is able to load from all libraries within the given project. - * - * @param projectName - * The name of the project. - * @return An instance of {@link URLClassLoader} with all available libraries. - */ - public ClassLoader getClassLoader(final String projectName) { - // Get the lock for the project - final Object lock = this.getLock(projectName); - final List<URL> libs = new ArrayList<URL>(); - - // Collect all libraries of the project - synchronized (lock) { - // Run through the libs and put them into our list. - final File libDir = new File(FSManager.ROOT_DIRECTORY + File.separator + projectName + File.separator + FSManager.LIB_DIRECTORY); - final File[] files = libDir.listFiles(); - if (files != null) { - for (final File file : files) { - if (file.getName().endsWith("." + FSManager.LIB_EXTENSION)) { - try { - libs.add(file.toURL()); - } catch (final MalformedURLException ex) { - ex.printStackTrace(); - } - } - } - } - - // Add the kieker lib! - libs.add(this.getKiekerURL()); - - // Now assemble the URL class loader - final PrivilegedClassLoaderAction action = new PrivilegedClassLoaderAction(libs); - return AccessController.doPrivileged(action); - } + private boolean projectExists(final String projectName) { + return this.assembleKaxFile(projectName).exists(); } /** @@ -699,34 +611,24 @@ public final class FSManager { // NOCS (Class Data Abstraction Coupling, Class F return file.toURL(); } - /** - * This method delivers all available libraries of the given project as a list of {@link MIDependency}. - * - * @param projectName - * The name of the project whose libraries should be delivered. - * @return A list with all available libraries. - */ - public List<MIDependency> getModelLibraries(final String projectName) { - // Get the lock for the project - final Object lock = this.getLock(projectName); - final List<MIDependency> libs = new ArrayList<MIDependency>(); + private File assembleProjectDir(final String projectName) { + return new File(FSManager.ROOT_DIRECTORY + File.separator + projectName); + } - // Collect all libraries of the project - synchronized (lock) { - // Run through the libs and put them into our list. - final File libDir = new File(FSManager.ROOT_DIRECTORY + File.separator + projectName + File.separator + FSManager.LIB_DIRECTORY); - final File[] files = libDir.listFiles(); - if (files != null) { - for (final File file : files) { - if (file.getName().endsWith("." + FSManager.LIB_EXTENSION)) { - final MIDependency dep = this.factory.createDependency(); - dep.setFilePath(file.getName()); - libs.add(dep); - } - } - } - return libs; - } + private File assembleKaxFile(final String projectName) { + return new File(FSManager.ROOT_DIRECTORY + File.separator + projectName + File.separator + projectName + "." + FSManager.KAX_EXTENSION); + } + + private File assembleLibDir(final String projectName) { + return new File(FSManager.ROOT_DIRECTORY + File.separator + projectName + File.separator + FSManager.LIB_DIRECTORY); + } + + public URL getKiekerURL() { + return Thread.currentThread().getContextClassLoader().getResource(FSManager.KIEKER_LIB); + } + + public File getProjectFile(final String projectName) { + return this.assembleKaxFile(projectName); } /** @@ -763,8 +665,4 @@ public final class FSManager { // NOCS (Class Data Abstraction Coupling, Class F } } - public URL getKiekerURL() { - return Thread.currentThread().getContextClassLoader().getResource(FSManager.KIEKER_LIB); - } - } diff --git a/Kieker.WebGUI/src/main/java/kieker/webgui/common/IProjectManagerFacade.java b/Kieker.WebGUI/src/main/java/kieker/webgui/common/IProjectManagerFacade.java index a4c4d33c..40ca7376 100644 --- a/Kieker.WebGUI/src/main/java/kieker/webgui/common/IProjectManagerFacade.java +++ b/Kieker.WebGUI/src/main/java/kieker/webgui/common/IProjectManagerFacade.java @@ -21,12 +21,17 @@ package kieker.webgui.common; import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; import java.util.Collection; +import java.util.List; import kieker.analysis.AnalysisController.STATE; +import kieker.analysis.model.analysisMetaModel.MIDependency; import kieker.analysis.model.analysisMetaModel.MIProject; import kieker.webgui.common.exception.AnalysisStateException; import kieker.webgui.common.exception.DisplayNotFoundException; +import kieker.webgui.common.exception.LibraryAlreadyExistingException; import kieker.webgui.common.exception.NewerProjectException; import kieker.webgui.common.exception.ProjectAlreadyExistingException; import kieker.webgui.common.exception.ProjectNotExistingException; @@ -149,8 +154,10 @@ public interface IProjectManagerFacade { * If a project with the given name does not exist. * @throws IOException * If something went wrong during the uploading. + * @throws LibraryAlreadyExistingException + * If a library with the same name exists already. */ - public void uploadLibrary(final UploadedFile file, final String projectName) throws ProjectNotExistingException, IOException; + public void uploadLibrary(final UploadedFile file, final String projectName) throws ProjectNotExistingException, IOException, LibraryAlreadyExistingException; /** * This method delivers a class loader containing the currently available libraries of the given project. @@ -174,7 +181,11 @@ public interface IProjectManagerFacade { * @throws ProjectNotExistingException * If a project with the given name does not exist. */ - public Collection<String> listAllLibraries(final String projectName) throws ProjectNotExistingException; + public List<String> listAllLibraries(final String projectName) throws ProjectNotExistingException; + + public URL getURL(final MIDependency lib, final String project) throws MalformedURLException; + + public URL getKiekerURL(); /** * This method lists all available projects on the file system. @@ -193,7 +204,7 @@ public interface IProjectManagerFacade { * @throws AnalysisStateException * If the analysis of the given project is in the wrong state to be initialized. This means that it has not been cleaned yet. */ - public void initializeAnalysis(final String projectName) throws ProjectNotExistingException, AnalysisStateException; + public void initializeAnalysis(final String projectName, final ClassLoader classLoader) throws ProjectNotExistingException, AnalysisStateException; /** * This method cleans the analysis of the given project. @@ -259,4 +270,5 @@ public interface IProjectManagerFacade { * If a project with the given name does not exist. */ public STATE getCurrentState(final String projectName) throws ProjectNotExistingException; + } diff --git a/Kieker.WebGUI/src/main/java/kieker/webgui/common/ProjectManagerFacade.java b/Kieker.WebGUI/src/main/java/kieker/webgui/common/ProjectManagerFacade.java index 9913a4de..b8391efd 100644 --- a/Kieker.WebGUI/src/main/java/kieker/webgui/common/ProjectManagerFacade.java +++ b/Kieker.WebGUI/src/main/java/kieker/webgui/common/ProjectManagerFacade.java @@ -21,13 +21,18 @@ package kieker.webgui.common; import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; import java.util.Collection; -import java.util.HashMap; +import java.util.List; +import java.util.concurrent.ConcurrentHashMap; import kieker.analysis.AnalysisController.STATE; +import kieker.analysis.model.analysisMetaModel.MIDependency; import kieker.analysis.model.analysisMetaModel.MIProject; import kieker.webgui.common.exception.AnalysisStateException; import kieker.webgui.common.exception.DisplayNotFoundException; +import kieker.webgui.common.exception.LibraryAlreadyExistingException; import kieker.webgui.common.exception.NewerProjectException; import kieker.webgui.common.exception.ProjectAlreadyExistingException; import kieker.webgui.common.exception.ProjectNotExistingException; @@ -42,8 +47,9 @@ import org.primefaces.model.UploadedFile; */ public final class ProjectManagerFacade implements IProjectManagerFacade { - private final HashMap<String, Object> fileSystemLocks = new HashMap<String, Object>(); - private final HashMap<String, Object> analysesLocks = new HashMap<String, Object>(); + private static final ProjectManagerFacade INSTANCE = new ProjectManagerFacade(); + private final ConcurrentHashMap<String, Object> fileSystemLocks = new ConcurrentHashMap<String, Object>(); + private final ConcurrentHashMap<String, Object> analysesLocks = new ConcurrentHashMap<String, Object>(); /** * This is the private constructor for this class, as this class is a singleton. @@ -52,106 +58,210 @@ public final class ProjectManagerFacade implements IProjectManagerFacade { // No code necessary. } + /** + * This method delivers the one and only singleton instance of this class. + * + * @return The singleton instance. + */ public static ProjectManagerFacade getInstance() { - return null; + return ProjectManagerFacade.INSTANCE; } @Override public void addProject(final String projectName) throws ProjectAlreadyExistingException, IOException { - // TODO Auto-generated method stub + final Object projectLock = this.getLock(projectName, this.fileSystemLocks); + synchronized (projectLock) { + FSManager.getInstance().addProject(projectName); + } } @Override public void copyProject(final String originalProjectName, final String newProjectName) throws ProjectNotExistingException, ProjectAlreadyExistingException, IOException { - // TODO Auto-generated method stub - + // Get the file system locks for both projects + final Object srcProjectLock = this.getLock(originalProjectName, this.fileSystemLocks); + final Object dstProjectLock = this.getLock(newProjectName, this.fileSystemLocks); + + // We cannot risk just using two synchronized-blocks as such a thing could block the application. We therefore have to sort the locks by name in order to + // make sure that two methods would use the same order to access (if project A has to be renamed to B and B has to be renamed to A, we would always + // synchronize on A before synchronizing on B). + final Object lockFst; + final Object lockSnd; + if (originalProjectName.compareTo(newProjectName) < 0) { + lockFst = srcProjectLock; + lockSnd = dstProjectLock; + } else { + lockFst = dstProjectLock; + lockSnd = srcProjectLock; + } + + synchronized (lockFst) { + synchronized (lockSnd) { + FSManager.getInstance().copyProject(originalProjectName, newProjectName); + } + } } @Override public MIProject openProject(final String projectName) throws ProjectNotExistingException, IOException { - // TODO Auto-generated method stub - return null; + final Object projectLock = this.getLock(projectName, this.fileSystemLocks); + + synchronized (projectLock) { + return FSManager.getInstance().openProject(projectName); + } } @Override public Object openProject(final String projectName, final ClassAndMethodContainer classAndMethodContainer) throws ProjectNotExistingException, IOException { - // TODO Auto-generated method stub - return null; + final Object projectLock = this.getLock(projectName, this.fileSystemLocks); + + synchronized (projectLock) { + return FSManager.getInstance().openProject(projectName, classAndMethodContainer); + } } @Override - public void saveProject(final String projectName, final MIProject project, final long timeStamp, final boolean overwriteNewerProject) - throws ProjectNotExistingException, IOException, NewerProjectException { - // TODO Auto-generated method stub + public void saveProject(final String projectName, final MIProject project, final long timeStamp, final boolean overwriteNewerProject) throws + ProjectNotExistingException, IOException, NewerProjectException { + final Object projectLock = this.getLock(projectName, this.fileSystemLocks); + synchronized (projectLock) { + FSManager.getInstance().saveProject(projectName, project, timeStamp, overwriteNewerProject); + } } @Override public long getCurrTimeStamp(final String projectName) throws ProjectNotExistingException { - // TODO Auto-generated method stub - return 0; + final Object projectLock = this.getLock(projectName, this.fileSystemLocks); + + synchronized (projectLock) { + return FSManager.getInstance().getCurrTimeStamp(projectName); + } } @Override - public void uploadLibrary(final UploadedFile file, final String projectName) throws ProjectNotExistingException, IOException { - // TODO Auto-generated method stub + public void uploadLibrary(final UploadedFile file, final String projectName) throws ProjectNotExistingException, IOException, LibraryAlreadyExistingException { + final Object projectLock = this.getLock(projectName, this.fileSystemLocks); + synchronized (projectLock) { + FSManager.getInstance().uploadLibrary(file, projectName); + } } @Override public ClassLoader getClassLoader(final String projectName) throws ProjectNotExistingException, IOException { - // TODO Auto-generated method stub - return null; + final Object projectLock = this.getLock(projectName, this.fileSystemLocks); + + synchronized (projectLock) { + return FSManager.getInstance().getClassLoader(projectName); + } } @Override - public Collection<String> listAllLibraries(final String projectName) throws ProjectNotExistingException { - // TODO Auto-generated method stub - return null; + public URL getURL(final MIDependency lib, final String project) throws MalformedURLException { + return FSManager.getInstance().getURL(lib, project); } @Override - public Collection<String> listAllProjects() { - // TODO Auto-generated method stub - return null; + public URL getKiekerURL() { + return FSManager.getInstance().getKiekerURL(); + } + + @Override + public List<String> listAllLibraries(final String projectName) throws ProjectNotExistingException { + final Object projectLock = this.getLock(projectName, this.fileSystemLocks); + + synchronized (projectLock) { + return FSManager.getInstance().listAllLibraries(projectName); + } } @Override - public void initializeAnalysis(final String projectName) throws ProjectNotExistingException, AnalysisStateException { - // TODO Auto-generated method stub + public Collection<String> listAllProjects() { + return FSManager.getInstance().listAllProjects(); + } + @Override + public void initializeAnalysis(final String projectName, final ClassLoader classLoader) throws ProjectNotExistingException, AnalysisStateException { + // We have to lock both - the project and the analysis, as a file has to be loaded + final Object projectLock = this.getLock(projectName, this.fileSystemLocks); + final Object analysisLock = this.getLock(projectName, this.analysesLocks); + + synchronized (projectLock) { + synchronized (analysisLock) { + ACManager.getInstance().initializeAnalysis(projectName, classLoader); + } + } } @Override public void cleanAnalysis(final String projectName) throws ProjectNotExistingException, AnalysisStateException { - // TODO Auto-generated method stub + final Object analysisLock = this.getLock(projectName, this.analysesLocks); + synchronized (analysisLock) { + ACManager.getInstance().cleanAnalysis(projectName); + } } @Override public void startAnalysis(final String projectName) throws ProjectNotExistingException, AnalysisStateException { - // TODO Auto-generated method stub + final Object analysisLock = this.getLock(projectName, this.analysesLocks); + synchronized (analysisLock) { + ACManager.getInstance().startAnalysis(projectName); + } } @Override public void stopAnalysis(final String projectName) throws ProjectNotExistingException, AnalysisStateException { - // TODO Auto-generated method stub + final Object analysisLock = this.getLock(projectName, this.analysesLocks); + synchronized (analysisLock) { + ACManager.getInstance().stopAnalysis(projectName); + } } @Override public Object getDisplay(final String projectName, final String viewName, final String displayName) throws ProjectNotExistingException, DisplayNotFoundException { - // TODO Auto-generated method stub - return null; + final Object analysisLock = this.getLock(projectName, this.analysesLocks); + + synchronized (analysisLock) { + return ACManager.getInstance().getDisplay(projectName, viewName, displayName); + } } @Override public STATE getCurrentState(final String projectName) throws ProjectNotExistingException { - // TODO Auto-generated method stub - return null; + final Object analysisLock = this.getLock(projectName, this.analysesLocks); + + synchronized (analysisLock) { + return ACManager.getInstance().getCurrentState(projectName); + } } + /** + * This method can be used to get the lock for a given project. If the lock doesn't exist already, a new one will be created. + * + * @param projectName + * The project whose lock should be delivered. + * @param lockMap + * The map containing the locks. + * @return A lock object for the project. + */ + private Object getLock(final String projectName, final ConcurrentHashMap<String, Object> lockMap) { + // Create a new lock and put it into the map - if it doesn't already contain the key + final Object newLock = new Object(); + final Object existLock = lockMap.putIfAbsent(projectName, newLock); + + // Find out which of the both locks is the correct one + final Object lock; + if (existLock != null) { + lock = existLock; + } else { + lock = newLock; + } + + return lock; + } } diff --git a/Kieker.WebGUI/src/main/java/kieker/webgui/common/exception/ProjectNotExistingException.java b/Kieker.WebGUI/src/main/java/kieker/webgui/common/exception/ProjectNotExistingException.java index 6a600fcb..8b65b105 100644 --- a/Kieker.WebGUI/src/main/java/kieker/webgui/common/exception/ProjectNotExistingException.java +++ b/Kieker.WebGUI/src/main/java/kieker/webgui/common/exception/ProjectNotExistingException.java @@ -20,6 +20,7 @@ package kieker.webgui.common.exception; + /** * @author Nils Christian Ehmke * @version 1.0 @@ -46,4 +47,8 @@ public class ProjectNotExistingException extends Exception { public ProjectNotExistingException(final String msg) { super(msg); } + + public ProjectNotExistingException(final String msg, final Throwable cause) { + super(msg, cause); + } } diff --git a/Kieker.WebGUI/src/main/webapp/dialogs/manageLibrariesDialog.xhtml b/Kieker.WebGUI/src/main/webapp/dialogs/manageLibrariesDialog.xhtml index bd4c979f..541a3e2f 100644 --- a/Kieker.WebGUI/src/main/webapp/dialogs/manageLibrariesDialog.xhtml +++ b/Kieker.WebGUI/src/main/webapp/dialogs/manageLibrariesDialog.xhtml @@ -12,11 +12,11 @@ <p:dataTable id="currentDependencies" value="#{currentAnalysisEditorBean.libraries}" var="dependency" paginator="true" rows="10" paginatorTemplate="{CurrentPageReport} {FirstPageLink} {PreviousPageLink} {PageLinks} {NextPageLink} {LastPageLink} {RowsPerPageDropdown}" > <p:column headerText="Filename"> - <h:outputText value="#{dependency.fst}"/> + <h:outputText value="#{dependency}"/> </p:column> <p:column headerText="Size" style="text-align: center"> - <h:outputText value="#{dependency.snd} [MiByte]"/> + <h:outputText value="N/A [MiByte]"/> </p:column> <p:column headerText="Options" style="text-align: center; width:40px"> -- GitLab