Skip to content
Snippets Groups Projects
Commit e6534e35 authored by Nils Christian Ehmke's avatar Nils Christian Ehmke
Browse files

Views are now visible and are updated frequently; Modified the way the...

Views are now visible and are updated frequently; Modified the way the analysis controllers are instantiated; Removed the deprecated view-directory; Updated the kieker-jar
parent be4622c7
No related branches found
No related tags found
No related merge requests found
No preview for this file type
......@@ -24,6 +24,8 @@ import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
import kieker.analysis.model.analysisMetaModel.MIProject;
import kieker.analysis.model.analysisMetaModel.MIView;
import kieker.webgui.common.ACManager;
import org.primefaces.model.DashboardColumn;
import org.primefaces.model.DashboardModel;
......@@ -60,6 +62,7 @@ public class CurrentAnalysisCockpitProjectBean {
* This is the actual model instance. It is the in-memory-model of the current (session) user.
*/
private MIProject project;
private MIView activeView;
/**
* Creates a new instance of this class.
......@@ -137,6 +140,14 @@ public class CurrentAnalysisCockpitProjectBean {
}
}
public String updateDisplay(final String displayName) {
try {
return ACManager.getInstance().getDisplay(this.projectName, this.activeView.getName(), displayName).toString();
} catch (final NullPointerException ex) {
return "N/A";
}
}
/**
* This method clears the bean. In other words: The stored project is set to null and it will return the page of the project overview for navigation purposes.
*
......@@ -150,4 +161,12 @@ public class CurrentAnalysisCockpitProjectBean {
return CurrentAnalysisCockpitProjectBean.PAGE_PROJECT_OVERVIEW;
}
public MIView getActiveView() {
return this.activeView;
}
public void setActiveView(final MIView activeView) {
this.activeView = activeView;
}
}
......@@ -142,6 +142,18 @@ public class CurrentAnalysisControllerProjectBean {
}
}
public void cleanAnalysis() {
try {
ACManager.getInstance().cleanAnalysisController(this.projectName);
} catch (final AnalysisNotInstantiatedException e) {
CurrentAnalysisControllerProjectBean.showMessage(FacesMessage.SEVERITY_WARN, "The analysis has not been instantiated yet.");
} catch (final AnalysisNotRunningException e) {
CurrentAnalysisControllerProjectBean.showMessage(FacesMessage.SEVERITY_WARN, "The analysis has not been started yet.");
} catch (final InterruptedException e) {
CurrentAnalysisControllerProjectBean.showMessage(FacesMessage.SEVERITY_ERROR, "An error occurred while cleaning the analysis.");
}
}
/**
* Checks whether the analysis is currently running.
*
......
......@@ -21,15 +21,26 @@
package kieker.webgui.common;
import java.io.IOException;
import java.lang.Thread.State;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import kieker.analysis.AnalysisController;
import kieker.analysis.AnalysisController.AnalysisControllerWithMapping;
import kieker.analysis.AnalysisController.STATE;
import kieker.analysis.display.AbstractDisplay;
import kieker.analysis.display.PlainText;
import kieker.analysis.display.annotation.Display;
import kieker.analysis.exception.AnalysisConfigurationException;
import kieker.analysis.model.analysisMetaModel.MIDisplay;
import kieker.analysis.model.analysisMetaModel.MIPlugin;
import kieker.analysis.model.analysisMetaModel.MIProject;
import kieker.analysis.model.analysisMetaModel.MIView;
import kieker.analysis.plugin.AbstractPlugin;
import kieker.webgui.common.exception.AnalysisNotInstantiatedException;
import kieker.webgui.common.exception.AnalysisNotRunningException;
import kieker.webgui.common.exception.ProjectAlreadyStartedException;
......@@ -42,6 +53,7 @@ import kieker.webgui.common.exception.ProjectStillRunningException;
* @author Nils Christian Ehmke
* @version 1.0
*/
// TODO How to differ between views and displays with same names?
public final class ACManager {
/**
* This is the maximal time the application will wait for an analysis thread to be terminated.
......@@ -55,7 +67,7 @@ public final class ACManager {
* This list contains the current analysis controllers and their corresponding threads. Not every project does have a controller, but every project can have
* maximal one.
*/
private final ConcurrentHashMap<String, Pair<AnalysisController, Thread>> analysisController = new ConcurrentHashMap<String, Pair<AnalysisController, Thread>>();
private final ConcurrentHashMap<String, Triple<AnalysisController, Thread, UpdateDisplaysThread>> analysisController = new ConcurrentHashMap<String, Triple<AnalysisController, Thread, UpdateDisplaysThread>>();
/**
* Creates a new instance of this class.
......@@ -84,16 +96,17 @@ public final class ACManager {
* If the current thread has somehow been interrupted while waiting for the existing analysis thread.
*/
public void stopAnalysisController(final String project) throws AnalysisNotRunningException, InterruptedException {
final Pair<AnalysisController, Thread> currController = this.analysisController.get(project);
synchronized (currController) {
// Is there a thread available?
if ((currController.getFst() == null) || (currController.getSnd() == null)) {
throw new AnalysisNotRunningException();
final Triple<AnalysisController, Thread, UpdateDisplaysThread> currController = this.analysisController.get(project);
try {
synchronized (currController) {
// Try to stop the analysis
currController.getFst().terminate();
currController.getThd().terminate();
currController.getSnd().join(ACManager.MAX_THREAD_WAIT_TIME_MS);
currController.getThd().join(ACManager.MAX_THREAD_WAIT_TIME_MS);
}
// Try to stop the analysis
currController.getFst().terminate();
currController.getSnd().join(ACManager.MAX_THREAD_WAIT_TIME_MS);
} catch (final NullPointerException ex) {
throw new AnalysisNotRunningException();
}
}
......@@ -108,33 +121,35 @@ public final class ACManager {
* If the analysis has not yet been initialized.
*/
public void startAnalysisController(final String project) throws ProjectAlreadyStartedException, AnalysisNotInstantiatedException {
final Pair<AnalysisController, Thread> currController = this.analysisController.get(project);
synchronized (currController) {
// Is there an analysis after all?
if (currController.getFst() == null) {
throw new AnalysisNotInstantiatedException();
final Triple<AnalysisController, Thread, UpdateDisplaysThread> currController = this.analysisController.get(project);
// Is there an analysis after all?
try {
synchronized (currController) {
// Check whether this analysis has already been started - or is already dead.
if (currController.getSnd().getState() != State.NEW) {
throw new ProjectAlreadyStartedException();
}
// Otherwise start the analysis
currController.getSnd().start();
currController.getThd().start();
}
// Check whether this analysis has already been started.
if (currController.getSnd() != null) {
throw new ProjectAlreadyStartedException();
} else {
// Otherwise start a thread and run the analysis in it.
currController.setSnd(new Thread() {
@Override
public void run() {
try {
currController.getFst().run();
} catch (final IllegalStateException ex) {
ex.printStackTrace();
} catch (final AnalysisConfigurationException ex) {
ex.printStackTrace();
}
}
});
} catch (final NullPointerException ex) {
throw new AnalysisNotInstantiatedException();
}
}
currController.getSnd().start();
public void cleanAnalysisController(final String project) throws AnalysisNotInstantiatedException, AnalysisNotRunningException, InterruptedException {
final Triple<AnalysisController, Thread, UpdateDisplaysThread> currController = this.analysisController.get(project);
// Is there an analysis after all?
try {
synchronized (currController) {
this.stopAnalysisController(project);
this.analysisController.remove(project);
}
} catch (final NullPointerException ex) {
throw new AnalysisNotInstantiatedException();
}
}
/**
......@@ -153,29 +168,36 @@ public final class ACManager {
*/
public void instantiateAnalysisController(final String project) throws NullPointerException, AnalysisConfigurationException, IOException,
ProjectStillRunningException {
// Add the potential new project atomically
final MIProject modelProject = FSManager.getInstance().openProject(project);
final ClassLoader classLoader = FSManager.getInstance().getClassLoader(project);
final Pair<AnalysisController, Thread> newController = new Pair<AnalysisController, Thread>(new AnalysisController(modelProject, classLoader), null);
final Pair<AnalysisController, Thread> currController = this.analysisController.putIfAbsent(project, newController);
final Triple<AnalysisController, Thread, UpdateDisplaysThread> newController = new Triple<AnalysisController, Thread, UpdateDisplaysThread>();
synchronized (newController) {
// Add the potential new project atomically
final Triple<AnalysisController, Thread, UpdateDisplaysThread> currController = this.analysisController.putIfAbsent(project, newController);
// We can just add a new controller if there wasn't an old one.
if (currController != null) {
throw new ProjectStillRunningException("The project with the name '" + project + "' is still running.");
}
final MIProject modelProject = FSManager.getInstance().openProject(project);
final ClassLoader classLoader = FSManager.getInstance().getClassLoader(project);
final AnalysisControllerWithMapping controller = AnalysisController.createAnalysisController(modelProject, classLoader);
if (currController != null) {
// There is currently an analysis controller available. Decide what to do.
synchronized (currController) {
switch (currController.getFst().getState()) {
case FAILED:
case TERMINATED:
case READY:
// No problem in instantiating a new controller
this.analysisController.put(project, newController);
break;
case RUNNING:
throw new ProjectStillRunningException("The project with the name '" + project + "' is still running.");
default:
// No code necessary
break;
// Create the necessary threads for the analysis
final Thread runningThread = new Thread() {
@Override
public void run() {
try {
controller.getController().run();
} catch (final IllegalStateException ex) {
ex.printStackTrace();
} catch (final AnalysisConfigurationException ex) {
ex.printStackTrace();
}
}
}
};
final UpdateDisplaysThread displayThread = new UpdateDisplaysThread(controller.getPluginMap(), modelProject);
// Put everything into our container
newController.setFst(controller.getController());
newController.setSnd(runningThread);
newController.setThd(displayThread);
}
}
......@@ -199,6 +221,11 @@ public final class ACManager {
return controllerStateString;
}
public AbstractDisplay getDisplay(final String project, final String viewName, final String displayName) {
// TODO Catch exceptions
return this.analysisController.get(project).getThd().getDisplay(viewName, displayName);
}
/**
* This method can be used to deliver the state of the analysis controller of the given project as a human readable string.
*
......@@ -211,7 +238,7 @@ public final class ACManager {
if (project == null) {
return null;
}
final Pair<AnalysisController, Thread> controller = this.analysisController.get(project);
final Triple<AnalysisController, Thread, UpdateDisplaysThread> controller = this.analysisController.get(project);
final STATE controllerState;
if (controller == null) {
......@@ -249,30 +276,72 @@ public final class ACManager {
* @author Nils Christian Ehmke
* @version 1.0
*/
@SuppressWarnings("unused")
private static class DisplayUpdateThread extends Thread {
private static class UpdateDisplaysThread extends Thread {
/**
* This is the time the thread waits between the updates.
*/
private static final long SLEEP_TIME_MS = 2 * 1000;
private final Map<MIPlugin, AbstractPlugin> myPluginMap;
private final MIProject myProject;
private final Map<String, Map<String, AbstractDisplay>> displayObjects = new ConcurrentHashMap<String, Map<String, AbstractDisplay>>();
private volatile boolean terminated = false;
/**
* Default constructor.
*/
public DisplayUpdateThread() {
// No code necessary
public UpdateDisplaysThread(final Map<MIPlugin, AbstractPlugin> pluginMap, final MIProject modelProject) {
this.myPluginMap = pluginMap;
this.myProject = modelProject;
// Initialize the hashmap and the necessary objects
for (final MIView view : this.myProject.getViews()) {
final Map<String, AbstractDisplay> viewMap = new ConcurrentHashMap<String, AbstractDisplay>();
this.displayObjects.put(view.getName(), viewMap);
for (final MIDisplay display : view.getDisplays()) {
// TODO Use correct display object
viewMap.put(display.getName(), new PlainText());
}
}
}
public void terminate() {
this.terminated = true;
}
public AbstractDisplay getDisplay(final String viewName, final String displayName) {
return this.displayObjects.get(viewName).get(displayName);
}
@Override
public void run() {
// Run until we have been interrupted
while (!Thread.interrupted()) {
// TODO Implement
while (!this.terminated) {
for (final MIView view : this.myProject.getViews()) {
final Map<String, AbstractDisplay> viewMap = this.displayObjects.get(view.getName());
for (final MIDisplay display : view.getDisplays()) {
final AbstractDisplay displayObject = viewMap.get(display.getName());
final AbstractPlugin pluginObject = this.myPluginMap.get(display.getParent());
// Update the display object
final Method[] methods = pluginObject.getClass().getMethods();
for (final Method method : methods) {
final Display displayAnnot = method.getAnnotation(Display.class);
if ((displayAnnot != null) && displayAnnot.name().equals(display.getName())) {
// We found the correct method
try {
method.invoke(pluginObject, displayObject);
} catch (final IllegalAccessException e) {
e.printStackTrace();
} catch (final IllegalArgumentException e) {
e.printStackTrace();
} catch (final InvocationTargetException e) {
e.printStackTrace();
}
}
}
}
}
// Wait a little bit.
try {
Thread.sleep(DisplayUpdateThread.SLEEP_TIME_MS);
Thread.sleep(UpdateDisplaysThread.SLEEP_TIME_MS);
} catch (final InterruptedException ex) {
// We have been interrupted. Exit the thread
return;
......
......@@ -79,10 +79,6 @@ public final class FSManager {
* This is the name of the root-directory.
*/
private static final String ROOT_DIRECTORY = "data";
/**
* This is the name of the directory for the views.
*/
private static final String VIEW_DIRECTORY = "view";
/**
* This is the buffer (in bytes) used to copy files.
*/
......@@ -146,7 +142,6 @@ public final class FSManager {
final File projectDir = new File(FSManager.ROOT_DIRECTORY + File.separator + project);
final File projectFile = new File(FSManager.ROOT_DIRECTORY + File.separator + project + File.separator + project + "." + FSManager.KAX_EXTENSION);
final File libDir = new File(FSManager.ROOT_DIRECTORY + File.separator + project + File.separator + FSManager.LIB_DIRECTORY);
final File viewDir = new File(FSManager.ROOT_DIRECTORY + File.separator + project + File.separator + FSManager.VIEW_DIRECTORY);
// We need an "empty" project in order to save it.
......@@ -162,17 +157,15 @@ public final class FSManager {
// Create the directories
final boolean projDirCreated = projectDir.mkdir();
final boolean libDirCreated = libDir.mkdir();
final boolean viewDirCreated = viewDir.mkdir();
// The following part is only necessary to calm FindBugs...
@SuppressWarnings("unused")
final boolean createResults = projDirCreated && libDirCreated && viewDirCreated;
final boolean createResults = projDirCreated && libDirCreated;
// Now the empty project file
try {
AnalysisController.saveToFile(projectFile, emptyProject);
} catch (final IOException ex) { // NOPMD (Rethrow to clean the directories)
// Something went wrong. Remove the directories and files!
final boolean viewDirDeleted = viewDir.delete();
final boolean libDirDeleted = libDir.delete();
// Keep in mind that the potential remains of the file have to be deleted before the directory.
final boolean projectFileDeleted = projectFile.delete();
......@@ -180,7 +173,7 @@ public final class FSManager {
// The following part is only necessary to calm FindBugs...
@SuppressWarnings("unused")
final boolean deleteResults = viewDirDeleted && libDirDeleted && projectFileDeleted && projectDeleted;
final boolean deleteResults = libDirDeleted && projectFileDeleted && projectDeleted;
// Rethrow the exception in order to inform the caller of this method
throw ex;
}
......@@ -348,27 +341,6 @@ public final class FSManager {
}
}
/**
* Delivers a list with all available views of the given project.
*
* @param project
* The name of the project.
* @return A list with the names of the available views.
*/
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;
}
/**
* Checks whether a project with the name exists on the file system.
*
......
package kieker.webgui.common;
/**
* This is a simple helper class which can store three values.
*
* @author Nils Christian Ehmke
* @version 1.0
*
* @param <F>
* The type of the first element.
* @param <S>
* The type of the second element.
* @param <T>
* The type of the third element.
*/
public class Triple<F, S, T> {
/**
* This is the first element.
*/
private F fst;
/**
* This is the second element.
*/
private S snd;
/**
* This is the third element
*/
private T thd;
/**
* Creates a new instance of this class with null values stored for the elements.
*/
public Triple() {
// No code necessary
}
/**
* Creates a new instance of this class using the given values.
*
* @param fst
* The first element to be stored in this object.
* @param snd
* The second element to be stored in this object.
* @param thd
* The third element to be stored in this object.
*/
public Triple(final F fst, final S snd, final T thd) {
this.fst = fst;
this.snd = snd;
this.thd = thd;
}
/**
* Delivers the first element.
*
* @return The first element.
*/
public F getFst() {
return this.fst;
}
/**
* Sets the first element to a new value.
*
* @param fst
* The new first element.
*/
public void setFst(final F fst) {
this.fst = fst;
}
/**
* Delivers the second element.
*
* @return The second element.
*/
public S getSnd() {
return this.snd;
}
/**
* Sets the second element to a new value.
*
* @param snd
* The new second element.
*/
public void setSnd(final S snd) {
this.snd = snd;
}
/**
* Delivers the third element.
*
* @return The third element.
*/
public T getThd() {
return this.thd;
}
/**
* Sets the third element to a new value.
*
* @param thd
* The new third element.
*/
public void setThd(final T thd) {
this.thd = thd;
}
}
......@@ -36,7 +36,7 @@
<p:menuitem value="Controller"/>
<p:menuitem value="Cockpit" disabled="true"/>
</p:submenu>
<p:submenu label="Help">
<p:menuitem value="User Guide" ajax="true" disabled="true"/>
<p:separator/>
......@@ -51,24 +51,24 @@
<p:layoutUnit position="center" id="centerLayout">
<p:dashboard id="board" model="#{currentAnalysisCockpitProjectBean.model}">
<p:panel id="panel1" header="N/A" toggleable="true">
<h:outputText value="N/A" />
</p:panel>
<p:panel id="panel2" header="N/A" toggleable="true">
<h:outputText value="N/A" />
</p:panel>
</p:dashboard>
<h:form id="centerForm">
<ui:repeat value="#{currentAnalysisCockpitProjectBean.activeView.displays}" var="display">
<p:panel header="#{display.name}">
<h:outputText value="#{currentAnalysisCockpitProjectBean.updateDisplay(display.name)}"/>
</p:panel>
</ui:repeat>
</h:form>
</p:layoutUnit>
<p:layoutUnit position="west" size="300" header="Views" resizable="true" collapsible="true">
<h:form id="viewsForm">
<p:poll interval="1" update=":centerForm"/>
<p:accordionPanel multiple="true" activeIndex="" value="#{currentAnalysisCockpitProjectBean.project.views}" var="currView">
<p:tab title="#{currView.name}">
<h:outputText value="#{currView.description}" rendered="#{not empty currView.description}"/>
<h:outputText value="No description available." rendered="#{empty currView.description}"/>
<hr/>
Click <h:commandLink value="here"/> to activate this view.
<hr/>
Click <h:commandLink value="here" action="#{currentAnalysisCockpitProjectBean.setActiveView(currView)}"/> to activate this view.
</p:tab>
</p:accordionPanel>
</h:form>
......
......@@ -36,7 +36,7 @@
<p:menuitem value="Controller" disabled="true"/>
<p:menuitem value="Cockpit"/>
</p:submenu>
<p:submenu label="Help">
<p:menuitem value="User Guide" ajax="true" disabled="true"/>
<p:separator/>
......@@ -56,8 +56,10 @@
<p:layoutUnit position="south" header="Control" resizable="true" collapsible="true">
<h:form id="controllerForm">
<p:commandButton value="Instantiate Analysis Controller" action="#{currentAnalysisControllerProjectBean.instantiateAnalysis()}" update=":messages" disabled="#{empty currentAnalysisControllerProjectBean.projectName}"/>
<p:commandButton value="Clean Analysis" action="#{currentAnalysisControllerProjectBean.cleanAnalysis()}" update=":messages" disabled="#{empty currentAnalysisControllerProjectBean.projectName}"/>
<p:commandButton value="Start Analysis" action="#{currentAnalysisControllerProjectBean.startAnalysis()}" update=":messages" disabled="#{empty currentAnalysisControllerProjectBean.projectName}"/>
<p:commandButton value="Stop Analysis" action="#{currentAnalysisControllerProjectBean.stopAnalysis()}" update=":messages" disabled="#{empty currentAnalysisControllerProjectBean.projectName}"/>
<p:poll interval="1" update=":ledsForm"/>
</h:form>
<hr/>
<h:form id="ledsForm">
......@@ -83,8 +85,8 @@
<p:tooltip for="iconLEDGreen_2" value="Indicates that the AnalysisController has been started and is running."/>
<p:tooltip for="iconLEDRed2_2" value="Indicates that the AnalysisController has been terminated or has failed."/>
</div>
<p:poll interval="1" update="ledsForm"/>
</h:form>
</p:layoutUnit>
</p:layout>
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment