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