diff --git a/Kieker.WebGUI/config/quality-config/fb-filter.xml b/Kieker.WebGUI/config/quality-config/fb-filter.xml index a354dc9d0de15f0a11677f6a081f04efd7be820d..8b01646b1cd1a7061ee550c7cebc0becad278fa5 100644 --- a/Kieker.WebGUI/config/quality-config/fb-filter.xml +++ b/Kieker.WebGUI/config/quality-config/fb-filter.xml @@ -22,4 +22,12 @@ <Bug code="RV" /> </Match> + <!-- Ignores the fact that our comparator doesn't implement the Serializable interface. There is no need to. Also it is not possible for this comparator, as it uses a map to compare some values. This "bug" can therefore be ignored. --> + <Match> + <Class name="kieker.webgui.web.utility.CockpitLayout$RowComparator" /> + + <Bug code="Se" /> + </Match> + + </FindBugsFilter> \ No newline at end of file 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 3566d8eb556f9de2977a234dc61750c667d79ebb..4b3a66f5664610727ab15e74c8f2e8f038992f27 100644 --- a/Kieker.WebGUI/src/main/java/kieker/webgui/common/ClassAndMethodContainer.java +++ b/Kieker.WebGUI/src/main/java/kieker/webgui/common/ClassAndMethodContainer.java @@ -23,6 +23,7 @@ import kieker.analysis.AnalysisControllerThread; import kieker.analysis.display.HtmlText; import kieker.analysis.display.Image; import kieker.analysis.display.PlainText; +import kieker.analysis.display.Plot; import kieker.analysis.display.annotation.Display; import kieker.analysis.plugin.AbstractPlugin; import kieker.analysis.plugin.annotation.InputPort; @@ -70,6 +71,7 @@ public class ClassAndMethodContainer { private Class<? extends Annotation> inputPortAnnotationClass; private Class<? extends Annotation> repositoryPortAnnotationClass; private Class<? extends Annotation> displayAnnotationClass; + private Class<?> plotClass; /** * Creates a new instance of this class, using the given class loader. @@ -98,6 +100,7 @@ public class ClassAndMethodContainer { this.htmlTextClass = classLoader.loadClass(HtmlText.class.getName()); this.plainTextClass = classLoader.loadClass(PlainText.class.getName()); this.imageClass = classLoader.loadClass(Image.class.getName()); + this.plotClass = classLoader.loadClass(Plot.class.getName()); // Now we load the more specific annotation classes this.pluginAnnotationClass = (Class<? extends Annotation>) classLoader.loadClass(Plugin.class.getName()); @@ -192,4 +195,8 @@ public class ClassAndMethodContainer { return this.logImplWebguiLoggingClass; } + public Class<?> getPlotClass() { + return this.plotClass; + } + } diff --git a/Kieker.WebGUI/src/main/java/kieker/webgui/domain/DisplayType.java b/Kieker.WebGUI/src/main/java/kieker/webgui/domain/DisplayType.java new file mode 100644 index 0000000000000000000000000000000000000000..ad86a5efbb5d7741ab380444f88067c2b6ee565c --- /dev/null +++ b/Kieker.WebGUI/src/main/java/kieker/webgui/domain/DisplayType.java @@ -0,0 +1,35 @@ +/*************************************************************************** + * Copyright 2013 Kieker Project (http://kieker-monitoring.net) + * + * 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.domain; + +/** + * This enumeration represents the different display types within Kieker. + * + * @author Nils Christian Ehmke + */ +public enum DisplayType { + + /** Represents the plot display type. */ + TYPE_PLOT, + /** Represents the plain text display type. */ + TYPE_PLAIN_TEXT, + /** Represents the Html text display type. */ + TYPE_HTML_TEXT, + /** Represents the image display type. */ + TYPE_IMAGE + +} diff --git a/Kieker.WebGUI/src/main/java/kieker/webgui/service/IProjectService.java b/Kieker.WebGUI/src/main/java/kieker/webgui/service/IProjectService.java index ca72415454d97c18971107eb2bf8a08cbef2ec34..cd5f90971ed07358228f1d399eedec5c67420499 100644 --- a/Kieker.WebGUI/src/main/java/kieker/webgui/service/IProjectService.java +++ b/Kieker.WebGUI/src/main/java/kieker/webgui/service/IProjectService.java @@ -30,6 +30,7 @@ import kieker.webgui.common.exception.NewerProjectException; import kieker.webgui.common.exception.ProjectAlreadyExistingException; import kieker.webgui.common.exception.ProjectNotExistingException; import kieker.webgui.domain.ComponentListContainer; +import kieker.webgui.domain.DisplayType; import org.primefaces.model.UploadedFile; @@ -421,4 +422,18 @@ public interface IProjectService { */ @PreAuthorize("isAuthenticated()") public abstract String getCockpitLayout(String projectName); + + /** + * Delivers the type of the given display connector. + * + * @param projectName + * The name of the project. + * @param viewName + * The name of the view. + * @param displayConnectorName + * The name of the display connector. + * + * @return The type of the display connector. + */ + public DisplayType getDisplayType(final String projectName, final String viewName, final String displayConnectorName); } diff --git a/Kieker.WebGUI/src/main/java/kieker/webgui/service/impl/ProjectServiceImpl.java b/Kieker.WebGUI/src/main/java/kieker/webgui/service/impl/ProjectServiceImpl.java index c13726099c02dee19c48e8e53456eaa8dd44d11e..d38281672400727ce7899a666c6ab94ae60ab7ce 100644 --- a/Kieker.WebGUI/src/main/java/kieker/webgui/service/impl/ProjectServiceImpl.java +++ b/Kieker.WebGUI/src/main/java/kieker/webgui/service/impl/ProjectServiceImpl.java @@ -32,6 +32,7 @@ import kieker.webgui.common.exception.NewerProjectException; import kieker.webgui.common.exception.ProjectAlreadyExistingException; import kieker.webgui.common.exception.ProjectNotExistingException; import kieker.webgui.domain.ComponentListContainer; +import kieker.webgui.domain.DisplayType; import kieker.webgui.persistence.IProjectDAO; import kieker.webgui.service.IProjectService; import kieker.webgui.service.impl.utility.ACManager; @@ -308,6 +309,15 @@ public class ProjectServiceImpl implements IProjectService { } } + @Override + public DisplayType getDisplayType(final String projectName, final String viewName, final String displayConnectorName) { + final Object analysisLock = this.getLock(projectName, this.analysesLocks); + + synchronized (analysisLock) { + return this.acManager.getDisplayType(projectName, viewName, displayConnectorName); + } + } + private Object getFirstLock(final String projectA, final String projectB, final ConcurrentHashMap<String, Object> lockMap) { if (projectA.compareTo(projectB) < 0) { return this.getLock(projectA, lockMap); @@ -349,4 +359,5 @@ public class ProjectServiceImpl implements IProjectService { return lock; } + } diff --git a/Kieker.WebGUI/src/main/java/kieker/webgui/service/impl/utility/ACManager.java b/Kieker.WebGUI/src/main/java/kieker/webgui/service/impl/utility/ACManager.java index a6b89ac2f196c8e1bdf96c03e82e834e392fac7d..6adb67d5cbcc1260cf2e89510882d74e8fa2b56f 100644 --- a/Kieker.WebGUI/src/main/java/kieker/webgui/service/impl/utility/ACManager.java +++ b/Kieker.WebGUI/src/main/java/kieker/webgui/service/impl/utility/ACManager.java @@ -23,6 +23,7 @@ import kieker.analysis.AnalysisController.STATE; import kieker.webgui.common.exception.AnalysisInitializationException; import kieker.webgui.common.exception.DisplayNotFoundException; import kieker.webgui.common.exception.InvalidAnalysisStateException; +import kieker.webgui.domain.DisplayType; import kieker.webgui.persistence.IProjectDAO; import org.springframework.beans.factory.annotation.Autowired; @@ -231,9 +232,29 @@ public class ACManager { * The project to update. */ public void updateDisplays(final String projectName) { - if (this.analyses.containsKey(projectName)) { + if (this.analyses.containsKey(projectName) && (this.getCurrentState(projectName) == STATE.RUNNING)) { this.analyses.get(projectName).updateDisplays(); } } + /** + * Delivers the type of the given display connector. + * + * @param projectName + * The name of the project. + * @param viewName + * The name of the view. + * @param displayConnectorName + * The name of the display connector. + * + * @return The type of the display connector. + */ + public DisplayType getDisplayType(final String projectName, final String viewName, final String displayConnectorName) { + if (this.analyses.containsKey(projectName)) { + return this.analyses.get(projectName).getDisplayType(viewName, displayConnectorName); + } else { + return null; + } + } + } diff --git a/Kieker.WebGUI/src/main/java/kieker/webgui/service/impl/utility/Analysis.java b/Kieker.WebGUI/src/main/java/kieker/webgui/service/impl/utility/Analysis.java index 661e924f93721371d785c3a667cc9618dd5e4b8e..571e277bde859e3a29594af5047ad92235df8edf 100644 --- a/Kieker.WebGUI/src/main/java/kieker/webgui/service/impl/utility/Analysis.java +++ b/Kieker.WebGUI/src/main/java/kieker/webgui/service/impl/utility/Analysis.java @@ -29,6 +29,7 @@ import kieker.webgui.common.ClassAndMethodContainer; import kieker.webgui.common.exception.AnalysisInitializationException; import kieker.webgui.common.exception.InvalidAnalysisStateException; import kieker.webgui.common.exception.ReflectionException; +import kieker.webgui.domain.DisplayType; import net.vidageek.mirror.dsl.Mirror; import net.vidageek.mirror.exception.MirrorException; @@ -203,6 +204,9 @@ public class Analysis { } } + /** + * Updates the display objects within this analysis. + */ public void updateDisplays() { for (final String view : this.displayMethods.keySet()) { for (final String display : this.displayMethods.get(view).keySet()) { @@ -214,4 +218,30 @@ public class Analysis { } } } + + /** + * Delivers the type of the given display connector. + * + * @param viewName + * The name of the view. + * @param displayConnectorName + * The name of the display connector. + * + * @return The type of the display connector. + */ + public DisplayType getDisplayType(final String viewName, final String displayConnectorName) { + final Object parameter = this.displayObjects.get(viewName).get(displayConnectorName); + + if (parameter.getClass() == this.classAndMethodContainer.getImageClass()) { + return DisplayType.TYPE_IMAGE; + } else if (parameter.getClass() == this.classAndMethodContainer.getPlainTextClass()) { + return DisplayType.TYPE_PLAIN_TEXT; + } else if (parameter.getClass() == this.classAndMethodContainer.getHtmlTextClass()) { + return DisplayType.TYPE_HTML_TEXT; + } else if (parameter.getClass() == this.classAndMethodContainer.getPlotClass()) { + return DisplayType.TYPE_PLOT; + } else { + return null; + } + } } diff --git a/Kieker.WebGUI/src/main/java/kieker/webgui/web/beans/view/CurrentCockpitBean.java b/Kieker.WebGUI/src/main/java/kieker/webgui/web/beans/view/CurrentCockpitBean.java index e3bd10d2887d2f851c25ba1676c4fdd6951bf6ee..a5a60fccabaebc625720f0e95d496b9e310842fb 100644 --- a/Kieker.WebGUI/src/main/java/kieker/webgui/web/beans/view/CurrentCockpitBean.java +++ b/Kieker.WebGUI/src/main/java/kieker/webgui/web/beans/view/CurrentCockpitBean.java @@ -35,6 +35,7 @@ import kieker.monitoring.core.registry.Registry; import kieker.webgui.common.exception.DisplayNotFoundException; import kieker.webgui.common.exception.InvalidAnalysisStateException; import kieker.webgui.common.exception.ProjectLoadException; +import kieker.webgui.domain.DisplayType; import kieker.webgui.service.IProjectService; import kieker.webgui.web.beans.application.GlobalPropertiesBean; import kieker.webgui.web.beans.application.ProjectsBean; @@ -58,7 +59,7 @@ import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; /** - * This class is a {@code Spring} managed bean with view scope containing the necessary data behind an instance of the cockpit.<br> + * This class is a {@code Spring} managed bean containing the necessary data behind an instance of the cockpit.<br> * <br/> * * It has view scope to make sure that one user (even in one session) can open multiple projects at a time without causing any problems. @@ -71,24 +72,25 @@ public class CurrentCockpitBean { private static final Log LOG = LogFactory.getLog(CurrentCockpitBean.class); - private static final String ID_PREFIX = "displayConnector_"; - private static final int NUMBER_COLUMNS = 2; + private static final String DASHBOARD_ID = "dashboard"; + private static final String PANEL_ID_PREFIX = "panel_"; + private static final int NUMBER_OF_COCKPIT_COLUMNS = 2; + private final Registry<MIDisplayConnector> displayConnectors = new Registry<MIDisplayConnector>(); + + private CockpitLayout cockpitLayout; + private DashboardModel dashboardModel; + private Dashboard dashboard; private String projectName; private MIProject project; private MIView activeView; - private Dashboard dashboard; - private DashboardModel dashboardModel; + @Autowired + private GlobalPropertiesBean globalPropertiesBean; @Autowired private IProjectService projectService; @Autowired private ProjectsBean projectsBean; - @Autowired - private GlobalPropertiesBean globalPropertiesBean; - - private CockpitLayout cockpitLayout; - private final Registry<MIDisplayConnector> displayConnectors = new Registry<MIDisplayConnector>(); /** * Creates a new instance of this class. <b>Do not call this constructor manually. It will only be accessed by Spring.</b> @@ -105,11 +107,12 @@ public class CurrentCockpitBean { try { // Make sure that the initialization will only be done for the init request. if (!FacesContext.getCurrentInstance().isPostback()) { - // Remember the given parameters - this.project = this.projectsBean.openProject(this.projectName); - this.createDashboard(); + this.loadProject(); - this.cockpitLayout = new CockpitLayout(this.project, this.projectService.getCockpitLayout(this.projectName), NUMBER_COLUMNS); + if (this.project != null) { + this.createDashboardComponent(); + this.loadCockpitLayout(); + } } } catch (final ProjectLoadException ex) { CurrentCockpitBean.LOG.error("An error occured while loading the project.", ex); @@ -122,91 +125,48 @@ public class CurrentCockpitBean { } /** - * Creates the initial dashboard object. + * Sets the active view to a new value and updates the dashboard. + * + * @param activeView + * The new active view. */ - private void createDashboard() { - final FacesContext fc = FacesContext.getCurrentInstance(); - final Application application = fc.getApplication(); - - // Create the primefaces dashboard - this.dashboard = (Dashboard) application.createComponent(fc, "org.primefaces.component.Dashboard", "org.primefaces.component.DashboardRenderer"); - this.dashboard.setId("dashboard"); - - // Create the model and add the columns - this.dashboardModel = new DefaultDashboardModel(); - for (int i = 0; i < CurrentCockpitBean.NUMBER_COLUMNS; i++) { - final DashboardColumn column = new DefaultDashboardColumn(); - this.dashboardModel.addColumn(column); - } - this.dashboard.setModel(this.dashboardModel); + public void setActiveView(final MIView activeView) { + this.activeView = activeView; + + this.fillDashboardWithActiveView(); } /** - * Fills the initial dashboard object. + * Sets the dashboard of this bean. + * + * @param dashboard + * The new dashboard. */ - private void fillDashboard() { - // Dump the old entries - this.clearDashboard(); - - // Now add the entries from the current view - if (this.activeView != null) { - - final List<List<MIDisplayConnector>> layout = this.cockpitLayout.getCurrentLayout(this.activeView); - - for (int col = 0; col < NUMBER_COLUMNS; col++) { - final DashboardColumn column = this.dashboard.getModel().getColumn(col); - - for (final MIDisplayConnector displayConnector : layout.get(col)) { - final Panel panel = this.createPanelFromDisplayConnector(displayConnector); + public void setDashboard(final Dashboard dashboard) { + this.dashboard = dashboard; - this.dashboard.getChildren().add(panel); - column.addWidget(panel.getId()); - } - } - } + // This step is necessary as we lose otherwise the dashboard model + this.dashboard.setModel(this.dashboardModel); } - private Panel createPanelFromDisplayConnector(final MIDisplayConnector connector) { - final FacesContext fc = FacesContext.getCurrentInstance(); - - final Application application = fc.getApplication(); - final Panel panel = (Panel) application.createComponent(fc, "org.primefaces.component.Panel", "org.primefaces.component.PanelRenderer"); - final String id = this.displayConnectorToID(connector); + public MIView getActiveView() { + return this.activeView; + } - // Set the usual properties of the panel - panel.setId(id); - panel.setHeader(connector.getName()); - panel.setClosable(true); - panel.setToggleable(false); + public Dashboard getDashboard() { + return this.dashboard; + } - final HtmlOutputText text = new HtmlOutputText(); - text.setValue("N/A"); - panel.getChildren().add(text); - - /* - * final GraphicImage graphImg = (GraphicImage) application.createComponent(fc, "org.primefaces.component.GraphicImage", - * "org.primefaces.component.GraphicImageRenderer"); - * try { - * final BufferedImage bufferedImg = new BufferedImage(100, 25, BufferedImage.TYPE_INT_RGB); - * final Graphics2D g2 = bufferedImg.createGraphics(); - * g2.drawString("This is a text", 0, 10); - * final ByteArrayOutputStream os = new ByteArrayOutputStream(); - * ImageIO.write(bufferedImg, "png", os); - * final StreamedContent graphicText = new DefaultStreamedContent(new ByteArrayInputStream(os.toByteArray()), "image/png"); - * - * graphImg.setValue(graphicText); - * } catch (final Exception ex) { - * ex.printStackTrace(); - * } - * - * panel.getChildren().add(graphImg); - */ + public MIProject getProject() { + return this.project; + } - return panel; + public void setProjectName(final String newName) { + this.projectName = newName; } - private String displayConnectorToID(final MIDisplayConnector displayConnector) { - return ID_PREFIX + this.displayConnectors.get(displayConnector); + public String getProjectName() { + return this.projectName; } /** @@ -285,162 +245,213 @@ public class CurrentCockpitBean { } /** - * Clears the dashboard and removes all children within it. + * Updates all displays of the currently active view. */ - private void clearDashboard() { - // Run through all columns of the dashboard and remove the items - final List<DashboardColumn> columns = this.dashboard.getModel().getColumns(); - for (final DashboardColumn column : columns) { - column.getWidgets().clear(); + public void updateDisplaysOfActiveView() { + if (this.projectName != null) { + for (final UIComponent component : this.dashboard.getChildren()) { + // Get the correct display type of the current component + final int componentID = Integer.valueOf(component.getId().replace(PANEL_ID_PREFIX, "")); + final MIDisplayConnector displayConnector = this.displayConnectors.get(componentID); + + if (this.projectService.getCurrentState(this.projectName) != null) { + final DisplayType displayType = this.projectService.getDisplayType(this.projectName, this.activeView.getName(), displayConnector.getName()); + + // Update the element depending on the display type + switch (displayType) { + case TYPE_PLAIN_TEXT: + this.updatePlainTextDisplay(component, displayConnector.getName()); + break; + case TYPE_HTML_TEXT: + this.updateHTMLTextDisplay(component, displayConnector.getName()); + break; + case TYPE_IMAGE: + this.updateImageDisplay(component, displayConnector.getName()); + break; + case TYPE_PLOT: + this.updatePlotDisplay(component, displayConnector.getName()); + break; + default: + // Unknown type + this.updateDisplayWithDefaultContent(component); + break; + + } + } else { + // If the analysis has not yet been initialized, we add a simple note to each of the panels + this.updateDisplayWithDefaultContent(component); + } + } } - // Now run through the dashboard itself and remove the items as well - this.dashboard.getChildren().clear(); } - public MIProject getProject() { - return this.project; + private void loadCockpitLayout() { + this.cockpitLayout = new CockpitLayout(this.project, this.projectService.getCockpitLayout(this.projectName), NUMBER_OF_COCKPIT_COLUMNS); } - public void setProjectName(final String newName) { - // Remember the given parameters - this.projectName = newName; - } + private void createDashboardComponent() { + final FacesContext facesContext = FacesContext.getCurrentInstance(); + final Application application = facesContext.getApplication(); - public String getProjectName() { - return this.projectName; - } + // Create the Primefaces dashboard component + this.dashboard = (Dashboard) application.createComponent(facesContext, "org.primefaces.component.Dashboard", "org.primefaces.component.DashboardRenderer"); + this.dashboard.setId(DASHBOARD_ID); - public void updateDisplays() { - for (final UIComponent child : this.dashboard.getChildren()) { - final int id = Integer.valueOf(child.getId().replace(ID_PREFIX, "")); - final MIDisplayConnector conn = this.displayConnectors.get(id); - this.updatePlainTextDisplay(child, conn.getName()); - this.updatePlotDisplay(child, conn.getName()); - } - } + // Create the corresponding model with the correct number of columns + this.dashboard.setModel(new DefaultDashboardModel()); - /** - * @param child - * @param name - */ - private void updatePlotDisplay(final UIComponent child, final String displayName) { - if ((this.activeView != null) && (this.projectName != null)) { - try { - final Object displayObj = this.projectService.getDisplay(this.projectName, this.activeView.getName(), displayName); - final Collection<Long> entries = (Collection<Long>) new Mirror().on(displayObj).invoke().method("getEntries").withoutArgs(); + for (int i = 0; i < NUMBER_OF_COCKPIT_COLUMNS; i++) { + this.dashboard.getModel().addColumn(new DefaultDashboardColumn()); + } - final FacesContext fc = FacesContext.getCurrentInstance(); + // Remember the dashboard model + this.dashboardModel = this.dashboard.getModel(); + } - final Application application = fc.getApplication(); + private void loadProject() throws ProjectLoadException { + this.project = this.projectsBean.openProject(this.projectName); + } - child.getChildren().clear(); + private void fillDashboardWithActiveView() { + // Dump the old entries + this.clearDashboard(); - final LineChart lineChart = (LineChart) application.createComponent(fc, "org.primefaces.component.chart.LineChart", - "org.primefaces.component.chart.LineChartRenderer"); + // Now add the entries from the active view + if (this.activeView != null) { + final List<List<MIDisplayConnector>> layout = this.cockpitLayout.getCurrentLayout(this.activeView); - lineChart.setTitle("Title"); - final CartesianChartModel linearModel = new CartesianChartModel(); + for (int columnIndex = 0; columnIndex < NUMBER_OF_COCKPIT_COLUMNS; columnIndex++) { + final DashboardColumn column = this.dashboard.getModel().getColumn(columnIndex); - final LineChartSeries series = new LineChartSeries(); - series.setLabel("Series"); + for (final MIDisplayConnector displayConnector : layout.get(columnIndex)) { + final Panel panel = this.createPanelFromDisplayConnector(displayConnector); - int i = 0; - for (final Long entry : entries) { - series.set(i, entry); - i++; + // Add the panel to the dashboard and to its model + this.dashboard.getChildren().add(panel); + column.addWidget(panel.getId()); } - - linearModel.addSeries(series); - lineChart.setValue(linearModel); - child.getChildren().add(lineChart); - } catch (final DisplayNotFoundException ex) { - CurrentCockpitBean.LOG.warn("Display not found.", ex); - } catch (final MirrorException ex) { - CurrentCockpitBean.LOG.warn("Reflection exception.", ex); - } catch (final InvalidAnalysisStateException ex) { - CurrentCockpitBean.LOG.info("Project is in invalid state.", ex); } } - } - /** - * Delivers the current content for the given display as plain text. - * - * @param displayName - * The name of the display. - * @return The current content of the display, if it exists. If the display does not exist or the active view is not set 'N/A' will be returned. If an access to - * the display fails, "Error" will be returned. - */ - public void updatePlainTextDisplay(final UIComponent child, final String displayName) { - if ((this.activeView != null) && (this.projectName != null)) { - try { - final FacesContext fc = FacesContext.getCurrentInstance(); - - fc.getApplication(); - child.getChildren().clear(); - final Object displayObj = this.projectService.getDisplay(this.projectName, this.activeView.getName(), displayName); - final String text = (String) new Mirror().on(displayObj).invoke().method("getText").withoutArgs(); - final HtmlOutputText t = new HtmlOutputText(); - t.setValue(text); - child.getChildren().add(t); - } catch (final DisplayNotFoundException ex) { - CurrentCockpitBean.LOG.warn("Display not found.", ex); - } catch (final MirrorException ex) { - CurrentCockpitBean.LOG.warn("Reflection exception.", ex); - } catch (final InvalidAnalysisStateException ex) { - CurrentCockpitBean.LOG.info("Project is in invalid state.", ex); - } + private void clearDashboard() { + // Run through all columns of the dashboard and remove the items + final List<DashboardColumn> columns = this.dashboard.getModel().getColumns(); + for (final DashboardColumn column : columns) { + column.getWidgets().clear(); } + // Now clear the dashboard itself + this.dashboard.getChildren().clear(); } - /** - * Delivers the current content for the given display as html text. - * - * @return Currently this method returns only N/A. - */ - public String updateHtmlTextDisplay() { - return "N/A"; + private Panel createPanelFromDisplayConnector(final MIDisplayConnector connector) { + final FacesContext facesContext = FacesContext.getCurrentInstance(); + final Application application = facesContext.getApplication(); + + // Create the Primefaces panel component + final Panel panel = (Panel) application.createComponent(facesContext, "org.primefaces.component.Panel", "org.primefaces.component.PanelRenderer"); + + // Set the usual properties of the panel + panel.setId(this.displayConnectorToID(connector)); + panel.setHeader(connector.getName()); + panel.setClosable(false); + panel.setToggleable(false); + + return panel; } - /** - * Delivers the current content for the given display as Image link. - * - * @return Currently this method returns only N/A. - */ - public String updateImageDisplay() { - return "N/A"; + private String displayConnectorToID(final MIDisplayConnector displayConnector) { + return PANEL_ID_PREFIX + this.displayConnectors.get(displayConnector); } - public MIView getActiveView() { - return this.activeView; + private void updateDisplayWithDefaultContent(final UIComponent component) { + component.getChildren().clear(); + + final HtmlOutputText htmlOutputText = new HtmlOutputText(); + htmlOutputText.setValue("N/A"); + + component.getChildren().add(htmlOutputText); } - /** - * Sets the active view to a new value. - * - * @param activeView - * The new active view. - */ - public void setActiveView(final MIView activeView) { - this.activeView = activeView; + private void updateHTMLTextDisplay(final UIComponent component, final String displayConnectorName) { // NOPMD (Not implemented yet) + // Not implemented yet + this.updateDisplayWithDefaultContent(component); + } - this.fillDashboard(); + private void updateImageDisplay(final UIComponent component, final String displayConnectorName) { // NOPMD (Not implemented yet) + // Not implemented yet + this.updateDisplayWithDefaultContent(component); } - public Dashboard getDashboard() { - return this.dashboard; + private void updatePlainTextDisplay(final UIComponent component, final String displayConnectorName) { + try { + component.getChildren().clear(); + + final Object displayObject = this.projectService.getDisplay(this.projectName, this.activeView.getName(), displayConnectorName); + final String text = (String) new Mirror().on(displayObject).invoke().method("getText").withoutArgs(); + final HtmlOutputText htmlOutputText = new HtmlOutputText(); + htmlOutputText.setValue(text); + + component.getChildren().add(htmlOutputText); + } catch (final DisplayNotFoundException ex) { + CurrentCockpitBean.LOG.warn("Display not found.", ex); + this.updateDisplayWithDefaultContent(component); + } catch (final MirrorException ex) { + CurrentCockpitBean.LOG.warn("Reflection exception.", ex); + this.updateDisplayWithDefaultContent(component); + } catch (final InvalidAnalysisStateException ex) { + CurrentCockpitBean.LOG.info("Project is in invalid state.", ex); + this.updateDisplayWithDefaultContent(component); + } } - /** - * Setter for the property {@link CurrentCockpitEditorBean#dashboard}. - * - * @param dashboard - * The new value for the property. - */ - public void setDashboard(final Dashboard dashboard) { - this.dashboard = dashboard; - this.dashboard.setModel(this.dashboardModel); + @SuppressWarnings("unchecked") + private void updatePlotDisplay(final UIComponent component, final String displayConnectorName) { + try { + component.getChildren().clear(); + + final FacesContext facesContext = FacesContext.getCurrentInstance(); + final Application application = facesContext.getApplication(); + + final Object displayObj = this.projectService.getDisplay(this.projectName, this.activeView.getName(), displayConnectorName); + final Collection<Long> entries = (Collection<Long>) new Mirror().on(displayObj).invoke().method("getEntries").withoutArgs(); + + // Create the Primefaces chart component + final LineChart lineChart = (LineChart) application.createComponent(facesContext, "org.primefaces.component.chart.LineChart", + "org.primefaces.component.chart.LineChartRenderer"); + lineChart.setTitle(displayConnectorName); + + // Add the corresponding model + final CartesianChartModel linearModel = new CartesianChartModel(); + final LineChartSeries series = new LineChartSeries(); + + series.setLabel("Series"); + + if (entries.isEmpty()) { + series.set(0, 0); + } else { + int x = 0; + for (final Long entry : entries) { + series.set(x++, entry); + } + } + + linearModel.addSeries(series); + lineChart.setValue(linearModel); + + component.getChildren().add(lineChart); + } catch (final DisplayNotFoundException ex) { + CurrentCockpitBean.LOG.warn("Display not found.", ex); + this.updateDisplayWithDefaultContent(component); + } catch (final MirrorException ex) { + CurrentCockpitBean.LOG.warn("Reflection exception.", ex); + this.updateDisplayWithDefaultContent(component); + } catch (final InvalidAnalysisStateException ex) { + CurrentCockpitBean.LOG.info("Project is in invalid state.", ex); + this.updateDisplayWithDefaultContent(component); + } + } + } diff --git a/Kieker.WebGUI/src/main/java/kieker/webgui/web/beans/view/CurrentCockpitEditorBean.java b/Kieker.WebGUI/src/main/java/kieker/webgui/web/beans/view/CurrentCockpitEditorBean.java index e7d3098311f489e0c3c44518cfa4b5185fa960b4..2c5ea8c8eee74bb76a43ee060dc87c05b2822237 100644 --- a/Kieker.WebGUI/src/main/java/kieker/webgui/web/beans/view/CurrentCockpitEditorBean.java +++ b/Kieker.WebGUI/src/main/java/kieker/webgui/web/beans/view/CurrentCockpitEditorBean.java @@ -61,7 +61,6 @@ import org.primefaces.component.dashboard.Dashboard; import org.primefaces.component.panel.Panel; import org.primefaces.context.RequestContext; import org.primefaces.event.DashboardReorderEvent; -import org.primefaces.event.TabChangeEvent; import org.primefaces.model.DashboardColumn; import org.primefaces.model.DashboardModel; import org.primefaces.model.DefaultDashboardColumn; @@ -72,7 +71,9 @@ import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; /** - * The {@link CurrentCockpitEditorBean} contains the necessary data behind an instance of the cockpit editor. + * The {@link CurrentCockpitEditorBean} contains the necessary data behind an instance of the cockpit editor.<br> + * </br> + * * The class is a Spring managed bean with view scope to make sure that one user (even in one session) can open multiple projects at a time without causing any * problems. * @@ -84,34 +85,35 @@ public class CurrentCockpitEditorBean { private static final Log LOG = LogFactory.getLog(CurrentCockpitEditorBean.class); - private static final String ID_PREFIX = "displayConnector_"; - private static final int NUMBER_COLUMNS = 2; + private static final String DASHBOARD_ID = "dashboard"; + private static final String PANEL_ID_PREFIX = "displayConnector_"; + private static final int NUMBER_OF_COCKPIT_COLUMNS = 2; + private final Registry<MIDisplayConnector> displayConnectors = new Registry<MIDisplayConnector>(); private final MIAnalysisMetaModelFactory factory = MAnalysisMetaModelFactory.eINSTANCE; - @Autowired - private IProjectService projectService; private ComponentListContainer availableComponents; - private long currId; - private long timeStamp; + private CockpitLayout cockpitLayout; + private boolean unsavedModifications; private String projectName; private MIProject project; - private boolean unsavedModifications; + private long timeStampSinceLastSaving; + + private MIDisplayConnector selectedNode; private MIView activeView; - private Dashboard dashboard; + private DashboardModel dashboardModel; - @Autowired - private ProjectsBean projectsBean; + private Dashboard dashboard; + @Autowired private GlobalPropertiesBean globalPropertiesBean; @Autowired + private IProjectService projectService; + @Autowired + private ProjectsBean projectsBean; + @Autowired private UserBean userBean; - private MIDisplayConnector selectedNode = null; - - private final Registry<MIDisplayConnector> displayConnectors = new Registry<MIDisplayConnector>(); - private CockpitLayout cockpitLayout; - /** * Creates a new instance of this class. <b>Do not call this constructor manually. It will only be accessed by Spring.</b> */ @@ -128,15 +130,14 @@ public class CurrentCockpitEditorBean { try { // Make sure that the initialization will only be done for the init request. if (!FacesContext.getCurrentInstance().isPostback()) { - // Remember the given parameters - this.project = this.projectsBean.openProject(this.projectName); + this.loadProject(); + if (this.project != null) { - // Remember the current time! This is important for the later comparison of the time stamps. this.resetTimeStamp(); - this.reloadComponents(); + this.reloadAvailableComponents(); - this.createDashboard(); - this.cockpitLayout = new CockpitLayout(this.project, this.projectService.getCockpitLayout(this.projectName), NUMBER_COLUMNS); + this.createDashboardComponent(); + this.loadCockpitLayout(); } this.unsavedModifications = false; @@ -150,37 +151,132 @@ public class CurrentCockpitEditorBean { } } + public boolean isUnsavedModification() { + return this.unsavedModifications; + } + + private void loadCockpitLayout() { + this.cockpitLayout = new CockpitLayout(this.project, this.projectService.getCockpitLayout(this.projectName), NUMBER_OF_COCKPIT_COLUMNS); + } + + private void loadProject() throws ProjectLoadException { + this.project = this.projectsBean.openProject(this.projectName); + } + /** - * Delivers the currently selected display connector. + * This is a dummy method returning just a collection of null objects. This is necessary due to Primefaces. * - * @return The selected connector. + * @return A collection with three null objects. */ - public MIDisplayConnector getSelectedNode() { - return this.selectedNode; + public Collection<Object> getProperties() { + return Collections.nCopies(3, null); } /** - * Creates the initial dashboard object. + * This method sets the time stamp to the current system time. */ - private void createDashboard() { - final FacesContext fc = FacesContext.getCurrentInstance(); - final Application application = fc.getApplication(); + public void resetTimeStamp() { + this.timeStampSinceLastSaving = System.currentTimeMillis(); + } + + public MIView getActiveView() { + return this.activeView; + } + + /** + * Sets the active view and updates the dashboard. + * + * @param view + * The new view. + */ + public void setActiveView(final MIView view) { + this.activeView = view; + + this.fillDashboard(); + } + + /** + * Deletes the given view from the model. + * + * @param view + * The view to be removed. + * + */ + public void deleteView(final MIView view) { + this.project.getViews().remove(view); + this.cockpitLayout.removeView(view); - // Create the Primefaces dashboard - this.dashboard = (Dashboard) application.createComponent(fc, "org.primefaces.component.Dashboard", "org.primefaces.component.DashboardRenderer"); - this.dashboard.setId("dashboard"); + this.setModificationsFlag(); + } - // Create the model and add the columns - this.dashboardModel = new DefaultDashboardModel(); - for (int i = 0; i < NUMBER_COLUMNS; i++) { - final DashboardColumn column = new DefaultDashboardColumn(); - this.dashboardModel.addColumn(column); + /** + * This method adds a new view to the project. + * + * @param viewName + * The name of the new view. + */ + public void addView(final String viewName) { + if (this.project != null) { + // Create the view and add it to our project + final MIView view = this.factory.createView(); + view.setName(viewName); + view.setDescription("No description available."); + + this.project.getViews().add(view); + this.cockpitLayout.addView(view); + + this.setModificationsFlag(); } + } + + public Dashboard getDashboard() { + return this.dashboard; + } + + /** + * Setter for the property {@link CurrentCockpitEditorBean#dashboard}. + * + * @param dashboard + * The new value for the property. + */ + public void setDashboard(final Dashboard dashboard) { + this.dashboard = dashboard; this.dashboard.setModel(this.dashboardModel); } - public boolean isUnsavedModification() { - return this.unsavedModifications; + public MIProject getProject() { + return this.project; + } + + public void setProjectName(final String newName) { + this.projectName = newName; + } + + public String getProjectName() { + return this.projectName; + } + + public MIDisplayConnector getSelectedNode() { + return this.selectedNode; + } + + private void createDashboardComponent() { + final FacesContext facesContext = FacesContext.getCurrentInstance(); + final Application application = facesContext.getApplication(); + + // Create the Primefaces dashboard component + this.dashboard = (Dashboard) application.createComponent(facesContext, "org.primefaces.component.Dashboard", "org.primefaces.component.DashboardRenderer"); + this.dashboard.setId(DASHBOARD_ID); + + // Create the corresponding model with the correct number of columns + this.dashboard.setModel(new DefaultDashboardModel()); + + for (int i = 0; i < NUMBER_OF_COCKPIT_COLUMNS; i++) { + this.dashboard.getModel().addColumn(new DefaultDashboardColumn()); + } + + // Remember the dashboard model + this.dashboardModel = this.dashboard.getModel(); } /** @@ -195,7 +291,7 @@ public class CurrentCockpitEditorBean { final List<List<MIDisplayConnector>> layout = this.cockpitLayout.getCurrentLayout(this.activeView); - for (int col = 0; col < NUMBER_COLUMNS; col++) { + for (int col = 0; col < NUMBER_OF_COCKPIT_COLUMNS; col++) { final DashboardColumn column = this.dashboard.getModel().getColumn(col); for (final MIDisplayConnector displayConnector : layout.get(col)) { @@ -208,29 +304,26 @@ public class CurrentCockpitEditorBean { } } - private void reloadComponents() { - this.availableComponents = this.projectService.getAvailableComponents(this.projectName); - } - - public MIProject getProject() { - return this.project; - } - /** - * This is a dummy method returning just a collection of null objects. This is necessary due to Primefaces. - * - * @return A collection with two null objects. + * This method sets the property {@link CurrentCockpitEditorBean#unsavedModifications} to false and refreshes the necessary components within the editor to make + * this visible. */ - public Collection<Object> getProperties() { - return Collections.nCopies(3, null); + private void clearModificationsFlag() { + this.unsavedModifications = false; + RequestContext.getCurrentInstance().update("menuForm"); } - public void setProjectName(final String newName) { - this.projectName = newName; + /** + * This method sets the property {@link CurrentCockpitEditorBean#unsavedModifications} to true and refreshes the necessary components within the editor + * to make this visible. + */ + private void setModificationsFlag() { + this.unsavedModifications = true; + RequestContext.getCurrentInstance().update("menuForm"); } - public String getProjectName() { - return this.projectName; + private void reloadAvailableComponents() { + this.availableComponents = this.projectService.getAvailableComponents(this.projectName); } /** @@ -277,7 +370,7 @@ public class CurrentCockpitEditorBean { */ public void saveProject(final boolean overwriteNewerProject) { try { - this.projectService.saveProject(this.projectName, this.project, this.timeStamp, overwriteNewerProject, this.userBean.getUsername(), null, + this.projectService.saveProject(this.projectName, this.project, this.timeStampSinceLastSaving, overwriteNewerProject, this.userBean.getUsername(), null, this.cockpitLayout.serializeToString()); GlobalPropertiesBean.showMessage(FacesMessage.SEVERITY_INFO, this.globalPropertiesBean.getMsgProjectSaved()); // Update the time stamp! @@ -298,81 +391,6 @@ public class CurrentCockpitEditorBean { } } - /** - * This method sets the property {@link CurrentCockpitEditorBean#unsavedModifications} to false and refreshes the necessary components within the editor to make - * this visible. - */ - private void clearModificationsFlag() { - this.unsavedModifications = false; - RequestContext.getCurrentInstance().update("menuForm"); - } - - /** - * This method sets the property {@link CurrentCockpitEditorBean#unsavedModifications} to true and refreshes the necessary components within the editor - * to make this visible. - */ - private void setModificationsFlag() { - this.unsavedModifications = true; - RequestContext.getCurrentInstance().update("menuForm"); - } - - /** - * This method sets the time stamp to the current system time. - */ - public void resetTimeStamp() { - this.timeStamp = System.currentTimeMillis(); - } - - /** - * This method adds a new view to the project. - * - * @param viewName - * The name of the new view. - */ - public void addView(final String viewName) { - if (this.project != null) { - // Create the view and add it to our project - final MIView view = this.factory.createView(); - view.setName(viewName); - view.setDescription("No description available."); - - this.project.getViews().add(view); - this.cockpitLayout.addView(view); - - this.setModificationsFlag(); - } - } - - public MIView getActiveView() { - return this.activeView; - } - - /** - * Sets the active view and updates the dashboard. - * - * @param view - * The new view. - */ - public void setActiveView(final MIView view) { - this.activeView = view; - - this.fillDashboard(); - } - - /** - * Deletes the given view from the model. - * - * @param view - * The view to be removed. - * - */ - public void deleteView(final MIView view) { - this.project.getViews().remove(view); - this.cockpitLayout.removeView(view); - - this.setModificationsFlag(); - } - private Panel createPanelFromDisplayConnector(final MIDisplayConnector connector) { final FacesContext fc = FacesContext.getCurrentInstance(); final Application application = fc.getApplication(); @@ -414,6 +432,8 @@ public class CurrentCockpitEditorBean { this.activeView.getDisplayConnectors().remove(connector); this.cockpitLayout.removeDisplayConnector(this.activeView, connector); + + this.setModificationsFlag(); } } @@ -427,7 +447,7 @@ public class CurrentCockpitEditorBean { if (this.activeView != null) { final MIDisplayConnector connector = this.factory.createDisplayConnector(); connector.setDisplay(display); - connector.setName("Display " + this.currId); + connector.setName("Display " + this.displayConnectors.getSize() + 1); this.activeView.getDisplayConnectors().add(connector); // Now add it to the dashboard as well @@ -443,6 +463,12 @@ public class CurrentCockpitEditorBean { } } + /** + * This handler should be executed when the user moves an element within the dashboard. + * + * @param event + * The move event. + */ public void handleReorder(final DashboardReorderEvent event) { final MIDisplayConnector connector = this.idToDisplayConnector(event.getWidgetId()); @@ -455,6 +481,8 @@ public class CurrentCockpitEditorBean { } this.cockpitLayout.moveDisplayConnector(this.activeView, connector, senderIndex, event.getColumnIndex(), event.getItemIndex()); + + this.setModificationsFlag(); } /** @@ -474,18 +502,6 @@ public class CurrentCockpitEditorBean { } } - /** - * This event is used if the currently selected tab changes. It modifies the active view property within this bean. - * - * @param event - * The onChange-event. - */ - public void onChange(final TabChangeEvent event) { - if (event.getData() instanceof MIView) { - this.setActiveView((MIView) event.getData()); - } - } - /** * This is the event if a node has been clicked and should be selected. */ @@ -501,26 +517,6 @@ public class CurrentCockpitEditorBean { } } - /** - * Getter for the property {@link CurrentCockpitEditorBean#dashboard}. - * - * @return The current value of the property. - */ - public Dashboard getDashboard() { - return this.dashboard; - } - - /** - * Setter for the property {@link CurrentCockpitEditorBean#dashboard}. - * - * @param dashboard - * The new value for the property. - */ - public void setDashboard(final Dashboard dashboard) { - this.dashboard = dashboard; - this.dashboard.setModel(this.dashboardModel); - } - /** * This method checks whether a display connector with the given name exists already. * @@ -553,16 +549,17 @@ public class CurrentCockpitEditorBean { for (final DashboardColumn column : columns) { column.getWidgets().clear(); } + // Now remove the items from the dashboard this.dashboard.getChildren().clear(); } private String displayConnectorToID(final MIDisplayConnector displayConnector) { - return ID_PREFIX + this.displayConnectors.get(displayConnector); + return PANEL_ID_PREFIX + this.displayConnectors.get(displayConnector); } private MIDisplayConnector idToDisplayConnector(final String id) { - final String shortID = id.substring(ID_PREFIX.length()); + final String shortID = id.substring(PANEL_ID_PREFIX.length()); final int intID = Integer.valueOf(shortID); return this.displayConnectors.get(intID); diff --git a/Kieker.WebGUI/src/main/webapp/pages/CockpitPage.xhtml b/Kieker.WebGUI/src/main/webapp/pages/CockpitPage.xhtml index 77a05ba6e33b383df0567e29290c17190840e371..9a311551aecf3b608f9e71c2bf21e7033c919e58 100644 --- a/Kieker.WebGUI/src/main/webapp/pages/CockpitPage.xhtml +++ b/Kieker.WebGUI/src/main/webapp/pages/CockpitPage.xhtml @@ -60,7 +60,6 @@ <ui:define name="furtherLayoutUnits"> <p:layoutUnit position="west" size="300" header="Views" resizable="true" collapsible="true"> <h:form id="viewForm"> - <p:poll interval="1" listener="#{currentCockpitBean.updateDisplays()}" update=":centerForm"/> <p:dataList value="#{currentCockpitBean.project.views}" var="currView"> <p:commandLink style="#{currView == currentCockpitBean.activeView ? 'font-weight:bold' : ''}" value="#{currView.name}" action="#{currentCockpitBean.setActiveView(currView)}" id="viewLink" update=":viewForm"/> <p:tooltip for="viewLink"> @@ -98,8 +97,13 @@ <h:outputText value="#{localizedMessages.stateFailed}"/> </h:panelGrid> </h:form> + + <h:form> + <p:poll interval="1" listener="#{currentCockpitBean.updateDisplaysOfActiveView()}" update=":centerForm :ledsForm"/> + </h:form> </div> </p:layoutUnit> + </ui:define> </ui:composition>