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 178a1188d957a6b34bc64a358ab3f99695b385ca..da75e0d5ecdfa566f20e5298f99dfa66cae226f9 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 @@ -21,6 +21,7 @@ package kieker.webgui.beans.view; import java.io.IOException; +import java.lang.annotation.Annotation; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; @@ -46,6 +47,7 @@ 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.ClassAndMethodContainer; import kieker.webgui.common.IProjectManagerFacade; import kieker.webgui.common.ProjectManagerFacade; import kieker.webgui.common.exception.NewerProjectException; @@ -93,6 +95,8 @@ public class CurrentCockpitEditorBean { @ManagedProperty(value = "#{projectsBean}") private ProjectsBean projectsBean; private final IProjectManagerFacade projectManagerFacade = ProjectManagerFacade.getInstance(); + private ClassAndMethodContainer classAndMethodContainer; + private ClassLoader classLoader; /** * Creates a new instance of this class. <b>Do not call this constructor manually. It will only be accessed by JSF.</b> @@ -134,6 +138,9 @@ public class CurrentCockpitEditorBean { if (this.project != null) { // Remember the current time! This is important for the later comparison of the time stamps. this.resetTimeStamp(); + // Update the class loader and the specific classes used within various methods in this bean + this.reloadClassLoader(); + this.reloadClassesAndMethods(); } } } catch (final ProjectLoadException ex) { @@ -143,6 +150,39 @@ public class CurrentCockpitEditorBean { } } + /** + * This method reloads the field containing the methods and classes, using the current class loader. + * + * @throws ProjectLoadException + * If one or more of the classes and methods necessary for {@link ClassAndMethodContainer} could not be found using the given class loader. + */ + private void reloadClassesAndMethods() throws ProjectLoadException { + synchronized (this) { + this.classAndMethodContainer = new ClassAndMethodContainer(this.classLoader); + } + } + + /** + * This method reloads the class loader. In other words: The class loader will always be able to load classes from the jar-files within the lib-folder of the + * project. + * + * @throws ProjectLoadException + * If something went wrong. + */ + private void reloadClassLoader() throws ProjectLoadException { + synchronized (this) { + try { + this.classLoader = this.projectManagerFacade.getClassLoader(this.projectName); // NOPMD (ClassLoader) + } 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); + } + } + } + /** * This method delivers the project stored in this bean. * @@ -177,6 +217,39 @@ public class CurrentCockpitEditorBean { } } + /** + * This method can be used to get the description of a {@link Display}. The description is read via the annotation using the java reflection API. + * + * @param display + * The display whose description should be extracted. + * @return The description for the display or a substitute if none is available. This is in either case human readable. + */ + public String getDescription(final MIDisplay display) { + try { + final String classname = display.getParent().getClassname(); + final String displayName = display.getName(); + // Try to get the class + final Class<?> clazz = this.classLoader.loadClass(classname); + // Run through all methods of the class + for (final Method method : clazz.getMethods()) { + // Get the potential annotation + final Annotation annotationDisplay = method.getAnnotation(this.classAndMethodContainer.getDisplayAnnotationClass()); + if (annotationDisplay != null) { + final String potDisplayName = (String) ClassAndMethodContainer.invokeMethod(this.classAndMethodContainer.getDisplayNameMethod(), + annotationDisplay, + ""); + if (potDisplayName.equals(displayName)) { + return (String) ClassAndMethodContainer.invokeMethod(this.classAndMethodContainer.getDisplayDescriptionMethod(), annotationDisplay, + "No description available"); + } + } + } + } catch (final ClassNotFoundException ex) { + CurrentCockpitEditorBean.LOG.warn("Class not found.", ex); + } + return "No description available"; + } + /** * This method tries to save the current project and informs the user about success or fail. * diff --git a/Kieker.WebGUI/src/main/java/kieker/webgui/common/ClassAndMethodContainer.java b/Kieker.WebGUI/src/main/java/kieker/webgui/common/ClassAndMethodContainer.java index d1c0410cb3b5c429cef8e9b62f76cb1c8f419aea..ed6d3840495e6b1d5337c89e4c41198b278e0332 100644 --- a/Kieker.WebGUI/src/main/java/kieker/webgui/common/ClassAndMethodContainer.java +++ b/Kieker.WebGUI/src/main/java/kieker/webgui/common/ClassAndMethodContainer.java @@ -223,6 +223,7 @@ public final class ClassAndMethodContainer { * This is the loadFromFile(File)-method of the class equivalence of {@link AnalysisController}. */ private final Method analysisControllerLoadFromFile; + private Method displayDescriptionMethod; /** * This is the constructor for {@link AnalysisControllerThread}, which gets an instance of {@link AnalysisController}. */ @@ -278,6 +279,7 @@ public final class ClassAndMethodContainer { this.analysisControllerThreadTerminate = this.analysisControllerThreadClass.getMethod("terminate", new Class<?>[0]); this.analysisControllerGetState = this.analysisControllerClass.getMethod("getState", new Class<?>[0]); this.propertyDescriptionMethod = this.propertyAnnotationClass.getMethod("description", new Class<?>[0]); + this.displayDescriptionMethod = this.displayAnnotationClass.getMethod("description", new Class<?>[0]); // This is a special case as we need to load some additional classes to search for the correct method final Class<?> miProjectClass = classLoader.loadClass(MIProject.class.getName()); @@ -671,6 +673,15 @@ public final class ClassAndMethodContainer { return this.analysisControllerLoadFromFile; } + /** + * The getter-method for the field {@link ClassAndMethodContainer#displayDescriptionMethod}. + * + * @return The current value for the field. + */ + public Method getDisplayDescriptionMethod() { + return this.displayDescriptionMethod; + } + /** * This method can be used to invoke a given method with given parameters, without having to mind about the exceptions. If an exception occurs, the given default * value will be returned. diff --git a/Kieker.WebGUI/src/main/webapp/CockpitEditor.xhtml b/Kieker.WebGUI/src/main/webapp/CockpitEditor.xhtml index e365638583c7af9a6025eed104fbfe3064afb67e..d79976f52b4ba8b6960cb5e9b8af161ba606b221 100644 --- a/Kieker.WebGUI/src/main/webapp/CockpitEditor.xhtml +++ b/Kieker.WebGUI/src/main/webapp/CockpitEditor.xhtml @@ -7,11 +7,11 @@ xmlns:p="http://primefaces.org/ui" xmlns:c="http://java.sun.com/jsp/jstl/core"> - <f:metadata> - <f:viewParam name="projectName" value="#{currentCockpitEditorBean.projectName}"/> - <f:event type="preRenderView" listener="#{currentCockpitEditorBean.initalize()}" /> + <f:metadata> + <f:viewParam name="projectName" value="#{currentCockpitEditorBean.projectName}"/> + <f:event type="preRenderView" listener="#{currentCockpitEditorBean.initalize()}" /> </f:metadata> - + <h:head> <title>Kieker.WebGUI</title> <link rel="stylesheet" type="text/css" href="../css/Common.css" /> @@ -70,12 +70,27 @@ </h:form> </p:layoutUnit> - <p:layoutUnit size="300" position="west"> - <h:form rendered="#{not empty currentCockpitEditorBean.project}"> - <p:accordionPanel activeIndex="-1" value="#{currentCockpitEditorBean.project.plugins}" var="plugin"> - <p:tab title="#{plugin.name}"> - <ui:repeat value="#{plugin.displays}" var="display"> - <p:commandLink id="displayLink" value="#{display.name}" action="#{currentCockpitEditorBean.addDisplayToView(display)}" update=":viewsForm"/> + <p:layoutUnit position="west" resizable="true" size="300" collapsible="true" header="Available Views"> + <h:form id="availableViewsForm"> + <p:dataTable value="#{currentCockpitEditorBean.project.views}" var="viewElem"> + <p:column headerText="View Name" style="font-weight: #{currentCockpitEditorBean.activeView == viewElem ? 'bold' : 'normal'}"> + <p:commandLink value="#{viewElem.name}" action="#{currentCockpitEditorBean.setActiveView(viewElem)}" update=":availableViewsForm :centerForm"/> + </p:column> + <p:column headerText="# Displays"> + #{viewElem.displayConnectors.size()} + </p:column> + </p:dataTable> + </h:form> + </p:layoutUnit> + + <p:layoutUnit position="east" resizable="true" size="300" collapsible="true" header="Available Displays"> + <h:form rendered="#{not empty currentCockpitEditorBean.project}"> + <p:accordionPanel value="#{currentCockpitEditorBean.project.plugins}" multiple="true" activeIndex="" var="plugin"> + <p:tab title="#{plugin.name}" titletip="#{plugin.classname}"> + <h:outputText value="No Displays Available" rendered="#{empty plugin.displays}"/> + <ui:repeat value="#{plugin.displays}" var="display"> + <p:commandLink id="displayLink" value="#{display.name}" action="#{currentCockpitEditorBean.addDisplayToView(display)}" update=":availableViewsForm :centerForm"/><br/> + <p:tooltip for="displayLink" value="#{currentCockpitEditorBean.getDescription(display)}"/> </ui:repeat> </p:tab> </p:accordionPanel> @@ -83,33 +98,32 @@ </p:layoutUnit> <p:layoutUnit position="center"> - <h:form id="viewsForm"> - <p:accordionPanel value="#{currentCockpitEditorBean.project.views}" var="viewComp" activeIndex="-1"> - <p:tab title="#{viewComp.name}" closable="true"> - <h:outputText value="Description: " /> + <h:form id="centerForm"> + <h:outputText value="Number of Columns: "/><br/> + <h:outputText value="Description: " /> + <p:inplace id="basic" editor="true"> + <p:inputText value="#{currentCockpitEditorBean.activeView.description}" /> + </p:inplace> + <br/> + <hr/> + <p:dataTable value="#{currentCockpitEditorBean.activeView.displayConnectors}" var="dispConn"> + <p:column headerText="Plugin" style="text-align: center"> + #{dispConn.display.parent.name} + </p:column> + <p:column headerText="Display Name" style="text-align: center"> + #{dispConn.display.name} + </p:column> + <p:column headerText="Name" style="text-align: center"> <p:inplace id="basic" editor="true"> - <p:inputText value="#{viewComp.description}" /> + <p:inputText value="#{dispConn.name}" validator="#{currentCockpitEditorBean.validateDisplayConnectorName}" /> </p:inplace> - <hr/> - <p:dataTable value="#{viewComp.displayConnectors}" var="dispConn"> - <p:column headerText="Plugin" style="text-align: center"> - #{dispConn.display.parent.name} - </p:column> - <p:column headerText="Display Name" style="text-align: center"> - #{dispConn.display.name} - </p:column> - <p:column headerText="Name" style="text-align: center"> - <p:inplace id="basic" editor="true"> - <p:inputText value="#{dispConn.name}" validator="#{currentCockpitEditorBean.validateDisplayConnectorName}" /> - </p:inplace> - </p:column> - <p:column style="text-align: center; width: 50px" > - <p:commandButton icon="ui-icon-trash" disabled="true"/> - </p:column> - </p:dataTable> - </p:tab> - <p:ajax event="tabChange" listener="#{currentCockpitEditorBean.onChange}" /> - </p:accordionPanel> + </p:column> + <p:column headerText="Column" style="text-align: center"> + </p:column> + <p:column style="text-align: center; width: 50px" > + <p:commandButton icon="ui-icon-trash" disabled="true"/> + </p:column> + </p:dataTable> </h:form> </p:layoutUnit> </p:layout> diff --git a/Kieker.WebGUI/src/main/webapp/dialogs/viewDialogs.xhtml b/Kieker.WebGUI/src/main/webapp/dialogs/viewDialogs.xhtml index ac6c1095e30b1f5c4c6b0dddef1e4ca92bd8e656..7cf24bef1e50d41aa3c24ed556e632ea30f79d2f 100644 --- a/Kieker.WebGUI/src/main/webapp/dialogs/viewDialogs.xhtml +++ b/Kieker.WebGUI/src/main/webapp/dialogs/viewDialogs.xhtml @@ -20,7 +20,7 @@ <hr/> <div style="text-align: right"> - <p:commandButton value="Ok" action="#{currentCockpitEditorBean.addView(stringBean.string)}" update=":messages :viewsForm" oncomplete="newViewDialog.hide()" /> + <p:commandButton value="Ok" action="#{currentCockpitEditorBean.addView(stringBean.string)}" update=":messages :availableViewsForm" oncomplete="newViewDialog.hide()" /> <p:spacer width="10px" height="10" /> <p:commandButton value="Cancel" onclick="newViewDialog.hide()" /> </div>