From b000f7a5afc32a0d9a0ec4bcdbd89eb27050ced1 Mon Sep 17 00:00:00 2001 From: Nils Christian Ehmke <nie@informatik.uni-kiel.de> Date: Sat, 26 May 2012 13:31:21 +0200 Subject: [PATCH] Added dialogs for renaming, copying and deleting projects; Fixed some bugs; Added code to get the available views; Plugins can now be renamed within a project --- .../beans/application/ProjectsBean.java | 10 +++ .../CurrentAnalysisCockpitProjectBean.java | 31 +++++++ .../view/CurrentProjectOverviewBean.java | 48 +++++++++++ .../java/kieker/webgui/common/ACManager.java | 6 +- .../java/kieker/webgui/common/FSManager.java | 86 ++++++++++++++++++- .../src/main/webapp/AnalysisCockpit.xhtml | 14 ++- .../src/main/webapp/ProjectOverview.xhtml | 12 +-- .../src/main/webapp/ProjectWorkSpace.xhtml | 4 +- .../main/webapp/dialogs/projectDialogs.xhtml | 54 ++++++++++++ 9 files changed, 253 insertions(+), 12 deletions(-) create mode 100644 Kieker.WebGUI/src/main/java/kieker/webgui/beans/view/CurrentProjectOverviewBean.java 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 cd1bc43c..a4bb6c5b 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 @@ -111,6 +111,16 @@ public final class ProjectsBean { } } + public void renameProject(final String projectName, final String newName) { + try { + FSManager.getInstance().renameProject(projectName, newName); + } catch (final IOException ex) { + ProjectsBean.showMessage(FacesMessage.SEVERITY_ERROR, "An error occured while renaming the project."); + } catch (final ProjectAlreadyExistingException ex) { + ProjectsBean.showMessage(FacesMessage.SEVERITY_WARN, "A project with the same name exists already."); + } + } + /** * This method can be used to get the current time stamp of a given project as a human readable date. If the project doesn't exist or the time stamp would be * invalid, the date of 0 is returned. diff --git a/Kieker.WebGUI/src/main/java/kieker/webgui/beans/session/CurrentAnalysisCockpitProjectBean.java b/Kieker.WebGUI/src/main/java/kieker/webgui/beans/session/CurrentAnalysisCockpitProjectBean.java index dccd1e79..03a616b0 100644 --- a/Kieker.WebGUI/src/main/java/kieker/webgui/beans/session/CurrentAnalysisCockpitProjectBean.java +++ b/Kieker.WebGUI/src/main/java/kieker/webgui/beans/session/CurrentAnalysisCockpitProjectBean.java @@ -20,9 +20,18 @@ package kieker.webgui.beans.session; +import java.util.List; + import javax.faces.bean.ManagedBean; import javax.faces.bean.SessionScoped; +import kieker.webgui.common.FSManager; + +import org.primefaces.model.DashboardColumn; +import org.primefaces.model.DashboardModel; +import org.primefaces.model.DefaultDashboardColumn; +import org.primefaces.model.DefaultDashboardModel; + /** * This bean contains the project of the current (session) user for the analysis cockpit. * @@ -45,12 +54,30 @@ public class CurrentAnalysisCockpitProjectBean { * This is the name of the stored project. It can be used as an identifier within the FS-Manager */ private String projectName; + private DashboardModel model; /** * Creates a new instance of this class. */ public CurrentAnalysisCockpitProjectBean() { // No code necessary + + // This code is just for test purposes + this.model = new DefaultDashboardModel(); + final DashboardColumn column1 = new DefaultDashboardColumn(); + + column1.addWidget("panel1"); + column1.addWidget("panel2"); + + this.model.addColumn(column1); + } + + public DashboardModel getModel() { + return this.model; + } + + public void setModel(final DashboardModel model) { + this.model = model; } /** @@ -85,4 +112,8 @@ public class CurrentAnalysisCockpitProjectBean { return CurrentAnalysisCockpitProjectBean.PAGE_PROJECT_OVERVIEW; } + + public List<String> getViews() { + return FSManager.getInstance().getAllViews(this.projectName); + } } diff --git a/Kieker.WebGUI/src/main/java/kieker/webgui/beans/view/CurrentProjectOverviewBean.java b/Kieker.WebGUI/src/main/java/kieker/webgui/beans/view/CurrentProjectOverviewBean.java new file mode 100644 index 00000000..f05209e2 --- /dev/null +++ b/Kieker.WebGUI/src/main/java/kieker/webgui/beans/view/CurrentProjectOverviewBean.java @@ -0,0 +1,48 @@ +/*************************************************************************** + * Copyright 2012 by + * + Christian-Albrechts-University of Kiel + * + Department of Computer Science + * + Software Engineering Group + * and others. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ + +package kieker.webgui.beans.view; + +import javax.faces.bean.ManagedBean; +import javax.faces.bean.ViewScoped; + +/** + * + * @author Nils Christian Ehmke + */ +@ManagedBean +@ViewScoped +public class CurrentProjectOverviewBean { + + private String projectName; + + public CurrentProjectOverviewBean() { + // No code necessary + } + + public String getProjectName() { + return this.projectName; + } + + public void setProjectName(final String projectName) { + this.projectName = projectName; + } + +} 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 1a2ab36b..73ffcaba 100644 --- a/Kieker.WebGUI/src/main/java/kieker/webgui/common/ACManager.java +++ b/Kieker.WebGUI/src/main/java/kieker/webgui/common/ACManager.java @@ -198,9 +198,13 @@ public class ACManager { * * @param project * The project whose state should be delivered. - * @return The current state of the corresponding {@link AnalysisController} if it exists, 'N/A' otherwise. + * @return The current state of the corresponding {@link AnalysisController} if it exists, null otherwise. */ public STATE getAnalysisControllerState(final String project) { + // If the given project is null, we return a null-state. + if (project == null) { + return null; + } final Pair<AnalysisController, Thread> controller = this.analysisController.get(project); final STATE controllerState; 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 e41dbd36..76d8cc66 100644 --- a/Kieker.WebGUI/src/main/java/kieker/webgui/common/FSManager.java +++ b/Kieker.WebGUI/src/main/java/kieker/webgui/common/FSManager.java @@ -57,6 +57,9 @@ import org.primefaces.model.UploadedFile; * This class uses also a fine grained synchronization to handle the access to the projects. * * @author Nils Christian Ehmke + * + * 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 { @@ -202,7 +205,7 @@ public final class FSManager { // Load it atomically synchronized (lock) { - return AnalysisController.loadFromFile(kaxFile); + return AnalysisController.loadFromFile(kaxFile.getAbsoluteFile());// /kaxFile); } } @@ -242,6 +245,87 @@ public final class FSManager { } } + 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."); + } + // 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 = (projectName.compareTo(newName)) < 0 ? lockOld : lockNew; + final Object lockSnd = (projectName.compareTo(newName)) < 0 ? lockNew : lockOld; + + // 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."); + } + + // Otherwise copy the project and delete the old one + this.copyProject(projectName, newName); + this.deleteProject(projectName); + } + } + } + + private void deleteProject(final String projectName) { + // TODO Delete + } + + private 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 = (projectName.compareTo(newName)) < 0 ? lockOld : lockNew; + final Object lockSnd = (projectName.compareTo(newName)) < 0 ? lockNew : lockOld; + + // 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."); + } + + // TODO Copy all files + } + } + } + + public List<String> getAllViews(final String project) { + final List<String> result = new ArrayList<String>(); + + // Get all files within the view-dir + final File[] files = new File(FSManager.ROOT_DIRECTORY + File.separator + project + File.separator + FSManager.VIEW_DIRECTORY).listFiles(); + for (final File file : files) { + if (file.isFile()) { + result.add(file.getName()); + } + } + + return result; + } + + 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. * diff --git a/Kieker.WebGUI/src/main/webapp/AnalysisCockpit.xhtml b/Kieker.WebGUI/src/main/webapp/AnalysisCockpit.xhtml index 8acf4b47..ab394769 100644 --- a/Kieker.WebGUI/src/main/webapp/AnalysisCockpit.xhtml +++ b/Kieker.WebGUI/src/main/webapp/AnalysisCockpit.xhtml @@ -41,12 +41,22 @@ <p:layoutUnit position="center" id="centerLayout"> + <p:dashboard id="board" model="#{currentAnalysisCockpitProjectBean.model}"> + <p:panel id="panel1" header="N/A"> + <h:outputText value="N/A" /> + </p:panel> + <p:panel id="panel2" header="N/A"> + <h:outputText value="N/A" /> + </p:panel> + </p:dashboard> </p:layoutUnit> <p:layoutUnit position="west" size="300" header="Views" resizable="true" collapsible="true"> <h:form id="viewsForm"> - <p:accordionPanel multiple="true" activeIndex=""> - + <p:accordionPanel multiple="true" activeIndex="" value="#{currentAnalysisCockpitProjectBean.views}" var="currView"> + <p:tab title="#{currView}"> + <h:outputText value="Some description here."/> + </p:tab> </p:accordionPanel> </h:form> </p:layoutUnit> diff --git a/Kieker.WebGUI/src/main/webapp/ProjectOverview.xhtml b/Kieker.WebGUI/src/main/webapp/ProjectOverview.xhtml index 8706ab5e..81dd147e 100644 --- a/Kieker.WebGUI/src/main/webapp/ProjectOverview.xhtml +++ b/Kieker.WebGUI/src/main/webapp/ProjectOverview.xhtml @@ -46,23 +46,23 @@ <h:outputText value="#{project}" /> </p:column> - <p:column headerText="Owner"> + <p:column headerText="Owner" style="text-align: center"> <h:outputText value="N/A" /> </p:column> - <p:column headerText="Last Modification" sortBy="#{projectsBean.getCurrTimeStamp(project)}"> + <p:column headerText="Last Modification" sortBy="#{projectsBean.getCurrTimeStamp(project)}" style="text-align: center"> <h:outputText value="#{projectsBean.getCurrTimeStamp(project)}" /> </p:column> - <p:column headerText="Analysis" style="text-align: center"> + <p:column headerText="Analysis" style="text-align: center" sortBy="#{projectsBean.getAnalysisControllerState(project)}"> <h:outputText value="#{projectsBean.getAnalysisControllerState(project)}"/> </p:column> <p:column headerText="Options" style="text-align: center; width: 280px"> <p:commandButton id="openButton" ajax="false" action="#{currentWorkSpaceProjectBean.setProject(projectsBean.openProject(project), project)}" icon="ui-icon-folder-open"/> - <p:commandButton id="copyButton" icon="ui-icon-copy"/> - <p:commandButton id="renameButton" icon="ui-icon-pencil"/> - <p:commandButton id="deleteButton" icon="ui-icon-trash"/> + <p:commandButton id="copyButton" icon="ui-icon-copy" action="#{currentProjectOverviewBean.setProjectName(project)}" onclick="copyProjectDialog.show()"/> + <p:commandButton id="renameButton" icon="ui-icon-pencil" action="#{currentProjectOverviewBean.setProjectName(project)}" onclick="renameProjectDialog.show()"/> + <p:commandButton id="deleteButton" icon="ui-icon-trash" action="#{currentProjectOverviewBean.setProjectName(project)}" onclick="deleteProjectDialog.show()"/> <p:spacer height="0" width="10"/> <p:commandButton id="controlAnalysis" ajax="false" action="#{currentAnalysisControllerProjectBean.setProject(project)}" icon="ui-icon-wrench"/> <p:commandButton id="editAnalysisViews" icon="ui-icon-pencil"/> diff --git a/Kieker.WebGUI/src/main/webapp/ProjectWorkSpace.xhtml b/Kieker.WebGUI/src/main/webapp/ProjectWorkSpace.xhtml index c6bee0ed..b9b2db04 100644 --- a/Kieker.WebGUI/src/main/webapp/ProjectWorkSpace.xhtml +++ b/Kieker.WebGUI/src/main/webapp/ProjectWorkSpace.xhtml @@ -87,11 +87,11 @@ <p:cellEditor> <f:facet name="output"> <h:outputText value="#{property.value}" rendered="#{not stringBean.checkString(property)}"/> - <h:outputText value="#{selectedPluginBean.plugin.name}" rendered="#{stringBean.checkString(property)}"/> + <h:outputText value="#{currentWorkSpaceProjectBean.selectedPlugin.name}" rendered="#{stringBean.checkString(property)}"/> </f:facet> <f:facet name="input"> <h:inputText value="#{property.value}" rendered="#{not stringBean.checkString(property)}"/> - <h:inputText value="#{selectedPluginBean.plugin.name}" rendered="#{stringBean.checkString(property)}"/> + <h:inputText value="#{currentWorkSpaceProjectBean.selectedPlugin.name}" rendered="#{stringBean.checkString(property)}"/> </f:facet> </p:cellEditor> </p:column> diff --git a/Kieker.WebGUI/src/main/webapp/dialogs/projectDialogs.xhtml b/Kieker.WebGUI/src/main/webapp/dialogs/projectDialogs.xhtml index 77d967d2..424d120e 100644 --- a/Kieker.WebGUI/src/main/webapp/dialogs/projectDialogs.xhtml +++ b/Kieker.WebGUI/src/main/webapp/dialogs/projectDialogs.xhtml @@ -27,4 +27,58 @@ </h:form> </p:dialog> <!-- ******************************************************************************** --> + + <p:dialog id="renameProjectDialog" header="Rename Project" resizable="false" modal="true" widgetVar="renameProjectDialog"> + <!-- Make sure that closing of the dialog also clears the input field. --> + <p:ajax event="close" update="renameProjectDialogForm:renameProjectInputText" /> + + <h:form id="renameProjectDialogForm"> + <div style="text-align: center"> + <h:outputText value="New Name: " /> + <p:inputText id="renameProjectInputText" value="#{stringBean.string}" /> + </div> + + <hr/> + <div style="text-align: right"> + <p:commandButton value="Ok" action="#{projectsBean.renameProject(currentProjectOverviewBean.projectName, stringBean.string)}" update=":projectsListForm :messages" oncomplete="renameProjectDialog.hide()" /> + <p:spacer width="10px" height="10" /> + <p:commandButton value="Cancel" onclick="renameProjectDialog.hide()" /> + </div> + </h:form> + </p:dialog> + + <p:dialog id="deleteProjectDialog" header="Delete Project" resizable="false" modal="true" widgetVar="deleteProjectDialog"> + + <h:form id="deleteProjectDialogForm"> + <div style="text-align: center"> + <h:outputText value="Do you really want to delete the selected project?" /> + </div> + + <hr/> + <div style="text-align: right"> + <p:commandButton value="Ok" action="#{projectsBean.deleteProject(currentProjectOverviewBean.projectName)}" update=":projectsListForm :messages" oncomplete="deleteProjectDialog.hide()" /> + <p:spacer width="10px" height="10" /> + <p:commandButton value="Cancel" onclick="deleteProjectDialog.hide()" /> + </div> + </h:form> + </p:dialog> + + <p:dialog id="copyProjectDialog" header="Copy Project" resizable="false" modal="true" widgetVar="copyProjectDialog"> + <!-- Make sure that closing of the dialog also clears the input field. --> + <p:ajax event="close" update="copyProjectDialogForm:copyProjectDialogInputText" /> + + <h:form id="copyProjectDialogForm"> + <div style="text-align: center"> + <h:outputText value="Name: " /> + <p:inputText id="copyProjectDialogInputText" value="#{stringBean.string}" /> + </div> + + <hr/> + <div style="text-align: right"> + <p:commandButton value="Ok" action="#{projectsBean.copyProject(currentProjectOverviewBean.projectName, stringBean.string)}" update=":projectsListForm :messages" oncomplete="copyProjectDialog.hide()" /> + <p:spacer width="10px" height="10" /> + <p:commandButton value="Cancel" onclick="copyProjectDialog.hide()" /> + </div> + </h:form> + </p:dialog> </ui:composition> \ No newline at end of file -- GitLab