diff --git a/Kieker.WebGUI/.settings/org.eclipse.jdt.core.prefs b/Kieker.WebGUI/.settings/org.eclipse.jdt.core.prefs index 1217752323225e12414fa2158831026f60470d85..6c11a6dcbcd0592369564c1556361e7d71854743 100644 --- a/Kieker.WebGUI/.settings/org.eclipse.jdt.core.prefs +++ b/Kieker.WebGUI/.settings/org.eclipse.jdt.core.prefs @@ -120,7 +120,7 @@ org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert -org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=do not insert org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert diff --git a/Kieker.WebGUI/src/main/java/kieker/webgui/common/ComponentListContainer.java b/Kieker.WebGUI/src/main/java/kieker/webgui/common/ComponentListContainer.java new file mode 100644 index 0000000000000000000000000000000000000000..a76e74b116dc41b42823de0ea3b94b63753728a7 --- /dev/null +++ b/Kieker.WebGUI/src/main/java/kieker/webgui/common/ComponentListContainer.java @@ -0,0 +1,76 @@ +/*************************************************************************** + * Copyright 2012 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.common; + +import java.util.List; + +/** + * This class is a container for multiple {@link PluginContainer} or {@link RepositoryContainer} instances. For each of the component type (reader, filter, + * repository) there is exactly one list available. This class will mostly be used to deliver a set of available components for a project. A bean using them could + * for example copy the available instances. + * + * @author Nils Christian Ehmke + */ +public class ComponentListContainer { + + private final List<PluginContainer> readers; + private final List<PluginContainer> filters; + private final List<RepositoryContainer> repositories; + + /** + * Creates a new instance of this class using the given parameters. + * + * @param readers + * The list containing the readers. + * @param filters + * The list containing the filters. + * @param repositories + * The list containing the repositories. + */ + public ComponentListContainer(final List<PluginContainer> readers, final List<PluginContainer> filters, final List<RepositoryContainer> repositories) { + this.readers = readers; + this.filters = filters; + this.repositories = repositories; + } + + /** + * Getter for the property {@link ComponentListContainer#readers}. + * + * @return The current value of the property. + */ + public List<PluginContainer> getReaders() { + return this.readers; + } + + /** + * Getter for the property {@link ComponentListContainer#filters}. + * + * @return The current value of the property. + */ + public List<PluginContainer> getFilters() { + return this.filters; + } + + /** + * Getter for the property {@link ComponentListContainer#repositories}. + * + * @return The current value of the property. + */ + public List<RepositoryContainer> getRepositories() { + return this.repositories; + } + +} diff --git a/Kieker.WebGUI/src/main/java/kieker/webgui/common/EnvironmentLoaderListener.java b/Kieker.WebGUI/src/main/java/kieker/webgui/common/EnvironmentLoaderListener.java index 77ace76e3e418d60222ce94ea1bd0909257b315a..d6ce666efb51fd54577bbee5f5a34d4791aa0620 100644 --- a/Kieker.WebGUI/src/main/java/kieker/webgui/common/EnvironmentLoaderListener.java +++ b/Kieker.WebGUI/src/main/java/kieker/webgui/common/EnvironmentLoaderListener.java @@ -39,11 +39,21 @@ public class EnvironmentLoaderListener implements ServletContextListener { // No code necessary } + /* + * (non-Javadoc) + * + * @see javax.servlet.ServletContextListener#contextDestroyed(javax.servlet.ServletContextEvent) + */ @Override public void contextDestroyed(final ServletContextEvent event) { // No code necessary } + /* + * (non-Javadoc) + * + * @see javax.servlet.ServletContextListener#contextInitialized(javax.servlet.ServletContextEvent) + */ @Override public void contextInitialized(final ServletContextEvent event) { // Before setting the logging property, use the log factory to make sure that the log entries of the WebGUI itself will be shown in the terminal diff --git a/Kieker.WebGUI/src/main/java/kieker/webgui/common/IComponentContainer.java b/Kieker.WebGUI/src/main/java/kieker/webgui/common/IComponentContainer.java new file mode 100644 index 0000000000000000000000000000000000000000..9abf5a905c6bae75d96388f8e35a708cbc8fc753 --- /dev/null +++ b/Kieker.WebGUI/src/main/java/kieker/webgui/common/IComponentContainer.java @@ -0,0 +1,34 @@ +/*************************************************************************** + * Copyright 2012 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.common; + +/** + * This is the interface for component containers of different types (plugins and repositories). It exists to have a common base for the implementing classes. + * + * @author Nils Christian Ehmke + */ +public interface IComponentContainer { + + /** + * Delivers the description of the given property. + * + * @param property + * The property name. + * @return The human readable description of the property. + */ + public String getPropertyDescription(final String property); + +} diff --git a/Kieker.WebGUI/src/main/java/kieker/webgui/common/PluginContainer.java b/Kieker.WebGUI/src/main/java/kieker/webgui/common/PluginContainer.java new file mode 100644 index 0000000000000000000000000000000000000000..6bcfaff215b28f512052ce2410325809153e063d --- /dev/null +++ b/Kieker.WebGUI/src/main/java/kieker/webgui/common/PluginContainer.java @@ -0,0 +1,211 @@ +/*************************************************************************** + * Copyright 2012 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.common; + +import java.util.Map; + +import kieker.analysis.model.analysisMetaModel.MIAnalysisMetaModelFactory; +import kieker.analysis.model.analysisMetaModel.MIDisplay; +import kieker.analysis.model.analysisMetaModel.MIFilter; +import kieker.analysis.model.analysisMetaModel.MIInputPort; +import kieker.analysis.model.analysisMetaModel.MIOutputPort; +import kieker.analysis.model.analysisMetaModel.MIPlugin; +import kieker.analysis.model.analysisMetaModel.MIProperty; +import kieker.analysis.model.analysisMetaModel.MIReader; +import kieker.analysis.model.analysisMetaModel.MIRepositoryConnector; + +/** + * This is a container to wrap a single instance of {@link MIPlugin} together with different meta information. + * + * @author Nils Christian Ehmke + */ +public class PluginContainer implements IComponentContainer { + + private final Map<String, String> propertyDescriptions; + private final Map<String, String> displayDescriptions; + private final boolean fullyInitialized; + private final MIPlugin plugin; + private final String description; + private final String dependency; + + /** + * Creates a new instance of this class using the given parameters. + * + * @param plugin + * The plugin to be stored in this container. + * @param description + * The description of the plugin. + * @param dependency + * The dependency description of the plugin. + * @param fullyInitialized + * A flag to determine whether the plugin has been initialized fully or whether there was an error during the class loading. + * @param propertyDescriptions + * A map containing the descriptions of the properties of the plugin. + * @param displayDescriptions + * A map containing the display descriptions of the properties of the plugin. + */ + public PluginContainer(final MIPlugin plugin, final String description, final String dependency, final boolean fullyInitialized, + final Map<String, String> propertyDescriptions, final Map<String, String> displayDescriptions) { + this.plugin = plugin; + this.description = description; + this.dependency = dependency; + this.fullyInitialized = fullyInitialized; + this.propertyDescriptions = propertyDescriptions; + this.displayDescriptions = displayDescriptions; + } + + /** + * Delivers the description of the repository. + * + * @return The human readable description string. + */ + public String getDescription() { + return this.description; + } + + /** + * Delivers the dependency description of the repository. + * + * @return The human readable dependency string. + */ + public String getDependency() { + return this.dependency; + } + + /** + * Tells whether the repository has been fully initialized. + * + * @return true iff the repository has been initialized fully. + */ + public boolean isFullyInitialized() { + return this.fullyInitialized; + } + + /** + * Tells whether the instance is a reader or not. + * + * @return true iff the plugin is a reader + */ + public boolean isReader() { + return this.plugin instanceof MIReader; + } + + /** + * Tells whether the instance is a filter or not. + * + * @return true iff the filter is a reader + */ + public boolean isFilter() { + return this.plugin instanceof MIFilter; + } + + /** + * Delivers the description of the given display. + * + * @param display + * The name of the display. + * @return The human readable description of the given display. + */ + public String getDisplayDescription(final String display) { + return this.displayDescriptions.get(display); + } + + /* + * (non-Javadoc) + * + * @see kieker.webgui.common.IComponentContainer#getPropertyDescription(java.lang.String) + */ + @Override + public String getPropertyDescription(final String property) { + return this.propertyDescriptions.get(property); + } + + /** + * Getter for the property {@link PluginContainer#plugin}. <b>DO NOT MODIFIY THIS OBJECT!</b> Use {@link PluginContainer#newInstance(MIAnalysisMetaModelFactory)} + * instead to get a copy. + * + * @return The current value of the property. + */ + public MIPlugin getPlugin() { + return this.plugin; + } + + /** + * Delivers a copy of the plugin instance within this container. + * + * @param factory + * The factory to be used to create the copy. + * @return A deep copy of the plugin. + */ + public MIPlugin newInstance(final MIAnalysisMetaModelFactory factory) { + final MIPlugin pluginCopy; + + if (this.plugin instanceof MIReader) { + pluginCopy = factory.createReader(); + } else if (this.plugin instanceof MIFilter) { + pluginCopy = factory.createFilter(); + // Copy the input ports of the plugin instance + for (final MIInputPort inputPort : ((MIFilter) this.plugin).getInputPorts()) { + final MIInputPort inputPortCopy = factory.createInputPort(); + inputPortCopy.setName(inputPort.getName()); + inputPortCopy.setParent((MIFilter) pluginCopy); + ((MIFilter) pluginCopy).getInputPorts().add(inputPortCopy); + } + } else { + // This should not happen + return null; + } + + // Copy the output ports of the plugin instance + for (final MIOutputPort outputPort : this.plugin.getOutputPorts()) { + final MIOutputPort outputPortCopy = factory.createOutputPort(); + outputPortCopy.setName(outputPort.getName()); + outputPortCopy.setParent(pluginCopy); + pluginCopy.getOutputPorts().add(outputPortCopy); + } + + // Copy the repository "ports" + for (final MIRepositoryConnector repositoryConnector : this.plugin.getRepositories()) { + final MIRepositoryConnector repositoryConnectorCopy = factory.createRepositoryConnector(); + repositoryConnectorCopy.setName(repositoryConnector.getName()); + pluginCopy.getRepositories().add(repositoryConnectorCopy); + } + + // Copy the displays + for (final MIDisplay display : this.plugin.getDisplays()) { + final MIDisplay displayCopy = factory.createDisplay(); + displayCopy.setName(display.getName()); + displayCopy.setParent(pluginCopy); + pluginCopy.getDisplays().add(displayCopy); + } + + // Copy the properties + for (final MIProperty property : this.plugin.getProperties()) { + final MIProperty propertyCopy = factory.createProperty(); + propertyCopy.setName(property.getName()); + propertyCopy.setValue(property.getValue()); + pluginCopy.getProperties().add(propertyCopy); + } + + // Copy the remaining attributes + pluginCopy.setClassname(this.plugin.getClassname()); + pluginCopy.setName(this.plugin.getName()); + + return pluginCopy; + } + +} diff --git a/Kieker.WebGUI/src/main/java/kieker/webgui/common/RepositoryContainer.java b/Kieker.WebGUI/src/main/java/kieker/webgui/common/RepositoryContainer.java new file mode 100644 index 0000000000000000000000000000000000000000..251d21a943cd7ebc3dac57e6798a997787c6909b --- /dev/null +++ b/Kieker.WebGUI/src/main/java/kieker/webgui/common/RepositoryContainer.java @@ -0,0 +1,135 @@ +/*************************************************************************** + * Copyright 2012 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.common; + +import java.util.Map; + +import kieker.analysis.model.analysisMetaModel.MIAnalysisMetaModelFactory; +import kieker.analysis.model.analysisMetaModel.MIProperty; +import kieker.analysis.model.analysisMetaModel.MIRepository; + +/** + * This is a container to wrap a single instance of {@link MIRepository} together with different meta information. + * + * @author Nils Christian Ehmke + */ +public class RepositoryContainer implements IComponentContainer { + + private final Map<String, String> propertyDescriptions; + private final boolean fullyInitialized; + private final MIRepository repository; + private final String description; + private final String dependency; + + /** + * Creates a new instance of this class using the given parameters. + * + * @param repository + * The repository to be stored in this container. + * @param description + * The description of the repository. + * @param dependency + * The dependency description of the repository. + * @param fullyInitialized + * A flag to determine whether the repository has been initialized fully or whether there was an error during the class loading. + * @param propertyDescriptions + * A map containing the descriptions of the properties of the repository. + */ + public RepositoryContainer(final MIRepository repository, final String description, final String dependency, final boolean fullyInitialized, + final Map<String, String> propertyDescriptions) { + this.repository = repository; + this.description = description; + this.dependency = dependency; + this.fullyInitialized = fullyInitialized; + this.propertyDescriptions = propertyDescriptions; + } + + /** + * Delivers the description of the repository. + * + * @return The human readable description string. + */ + public String getDescription() { + return this.description; + } + + /** + * Delivers the dependency description of the repository. + * + * @return The human readable dependency string. + */ + public String getDependency() { + return this.dependency; + } + + /** + * Tells whether the repository has been fully initialized. + * + * @return true iff the repository has been initialized fully. + */ + public boolean isFullyInitialized() { + return this.fullyInitialized; + } + + /* + * (non-Javadoc) + * + * @see kieker.webgui.common.IComponentContainer#getPropertyDescription(java.lang.String) + */ + @Override + public String getPropertyDescription(final String property) { + return this.propertyDescriptions.get(property); + } + + /** + * Getter for the property {@link RepositoryContainer#repository}. <b>DO NOT MODIFIY THIS OBJECT!</b> Use + * {@link RepositoryContainer#newInstance(MIAnalysisMetaModelFactory)} instead to get a copy. + * + * @return The current value of the property. + */ + public MIRepository getRepository() { + return this.repository; + } + + /** + * Delivers a copy of the repository instance within this container. + * + * @param factory + * The factory to be used to create the copy. + * @return A deep copy of the repository. + */ + public MIRepository newInstance(final MIAnalysisMetaModelFactory factory) { + final MIRepository repositoryCopy; + + repositoryCopy = factory.createRepository(); + + // Copy the properties + for (final MIProperty property : this.repository.getProperties()) { + final MIProperty propertyCopy = factory.createProperty(); + propertyCopy.setName(property.getName()); + propertyCopy.setValue(property.getValue()); + repositoryCopy.getProperties().add(propertyCopy); + } + + // Copy the remaining attributes + repositoryCopy.setClassname(this.repository.getClassname()); + repositoryCopy.setName(this.repository.getName()); + + return repositoryCopy; + } + +} diff --git a/Kieker.WebGUI/src/main/java/kieker/webgui/common/ViewScope.java b/Kieker.WebGUI/src/main/java/kieker/webgui/common/ViewScope.java index af0baa16eeee37b8a180d65aec6f31953d94070f..7962aa9067f69228d0bfe8f0cc4fa7f573e7a9fe 100644 --- a/Kieker.WebGUI/src/main/java/kieker/webgui/common/ViewScope.java +++ b/Kieker.WebGUI/src/main/java/kieker/webgui/common/ViewScope.java @@ -29,6 +29,7 @@ import org.springframework.beans.factory.config.Scope; * @author Nils Christian Ehmke */ public class ViewScope implements Scope { + /** * Default constructor. <b>Do not use this constructor. This bean is Spring managed.</b> */ @@ -51,23 +52,44 @@ public class ViewScope implements Scope { } } + /* + * (non-Javadoc) + * + * @see org.springframework.beans.factory.config.Scope#remove(java.lang.String) + */ @Override public Object remove(final String name) { return FacesContext.getCurrentInstance().getViewRoot().getViewMap().remove(name); } + /* + * (non-Javadoc) + * + * @see org.springframework.beans.factory.config.Scope#getConversationId() + */ @Override public String getConversationId() { return null; } + /* + * (non-Javadoc) + * + * @see org.springframework.beans.factory.config.Scope#registerDestructionCallback(java.lang.String, java.lang.Runnable) + */ @Override public void registerDestructionCallback(final String name, final Runnable callback) { // NOPMD (Threads are not used) // Not supported } + /* + * (non-Javadoc) + * + * @see org.springframework.beans.factory.config.Scope#resolveContextualObject(java.lang.String) + */ @Override public Object resolveContextualObject(final String key) { return null; } + } diff --git a/Kieker.WebGUI/src/main/java/kieker/webgui/common/exception/ComponentInitializationException.java b/Kieker.WebGUI/src/main/java/kieker/webgui/common/exception/ComponentInitializationException.java new file mode 100644 index 0000000000000000000000000000000000000000..c7df6fc73281f1a0ff38a4a054913baa7c5c85a4 --- /dev/null +++ b/Kieker.WebGUI/src/main/java/kieker/webgui/common/exception/ComponentInitializationException.java @@ -0,0 +1,53 @@ +/*************************************************************************** + * Copyright 2012 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.common.exception; + +/** + * @author Nils Christian Ehmke + */ +public class ComponentInitializationException extends AbstractKiekerWebGUIException { + + private static final long serialVersionUID = 1L; + + /** + * Creates a new instance of this class. + */ + public ComponentInitializationException() { + super(); + } + + /** + * Creates a new instance of this class using the given parameters. + * + * @param msg + * The message used for the exception. + */ + public ComponentInitializationException(final String msg) { + super(msg); + } + + /** + * Creates a new instance of this class using the given parameters. + * + * @param msg + * The message used for the exception. + * @param cause + * The cause for the exception. + */ + public ComponentInitializationException(final String msg, final Throwable cause) { + super(msg, cause); + } +} diff --git a/Kieker.WebGUI/src/main/java/kieker/webgui/common/exception/InvalidInputSizeException.java b/Kieker.WebGUI/src/main/java/kieker/webgui/common/exception/InvalidInputSizeException.java index 265c5b162a65abab0ce29723cd579ae1b18a0ce7..5a7fef51fcc51ed3c89e0bcdd676315c0b5aa078 100644 --- a/Kieker.WebGUI/src/main/java/kieker/webgui/common/exception/InvalidInputSizeException.java +++ b/Kieker.WebGUI/src/main/java/kieker/webgui/common/exception/InvalidInputSizeException.java @@ -16,7 +16,6 @@ package kieker.webgui.common.exception; - /** * An Exception that is thrown when an Array of invalid size is loaded by the GraphFlowLayouter. * @@ -29,7 +28,32 @@ public class InvalidInputSizeException extends AbstractKiekerWebGUIException { private static final long serialVersionUID = 1L; + /** + * Creates a new instance of this class. + */ public InvalidInputSizeException() { - super("Input Array has an invalid length!"); + super(); + } + + /** + * Creates a new instance of this class using the given parameters. + * + * @param msg + * The message used for the exception. + */ + public InvalidInputSizeException(final String msg) { + super(msg); + } + + /** + * Creates a new instance of this class using the given parameters. + * + * @param msg + * The message used for the exception. + * @param cause + * The cause for the exception. + */ + public InvalidInputSizeException(final String msg, final Throwable cause) { + super(msg, cause); } } diff --git a/Kieker.WebGUI/src/main/java/kieker/webgui/common/exception/UninitializedGraphException.java b/Kieker.WebGUI/src/main/java/kieker/webgui/common/exception/UninitializedGraphException.java index 88b38a0ec289a4b6335439a0d78156af2a44b8f0..3ae0ab8291c6c716b798cb43887152ef20889488 100644 --- a/Kieker.WebGUI/src/main/java/kieker/webgui/common/exception/UninitializedGraphException.java +++ b/Kieker.WebGUI/src/main/java/kieker/webgui/common/exception/UninitializedGraphException.java @@ -16,7 +16,6 @@ package kieker.webgui.common.exception; - /** * An Exception that is thrown when trying to connect Edges if a graph has not been * loaded by the GraphFlowLayouter. @@ -27,7 +26,32 @@ public class UninitializedGraphException extends AbstractKiekerWebGUIException { private static final long serialVersionUID = 1L; + /** + * Creates a new instance of this class. + */ public UninitializedGraphException() { - super("Cannot apply Edges! Graph has not been loaded!"); + super(); + } + + /** + * Creates a new instance of this class using the given parameters. + * + * @param msg + * The message used for the exception. + */ + public UninitializedGraphException(final String msg) { + super(msg); + } + + /** + * Creates a new instance of this class using the given parameters. + * + * @param msg + * The message used for the exception. + * @param cause + * The cause for the exception. + */ + public UninitializedGraphException(final String msg, final Throwable cause) { + super(msg, cause); } } diff --git a/Kieker.WebGUI/src/main/java/kieker/webgui/common/layout/GraphFlowLayouter.java b/Kieker.WebGUI/src/main/java/kieker/webgui/common/layout/GraphFlowLayouter.java index 03ed18fa1b379a2295f32f53abd1c2667140013c..44edc884073cb8722801f8e858756131a13631e3 100644 --- a/Kieker.WebGUI/src/main/java/kieker/webgui/common/layout/GraphFlowLayouter.java +++ b/Kieker.WebGUI/src/main/java/kieker/webgui/common/layout/GraphFlowLayouter.java @@ -58,13 +58,15 @@ public final class GraphFlowLayouter { /** * Parses a String array, building a graph with nodes and fixed ports. * - * @param input - * The parsed String array - * @return The constructed Graph + * @param positions + * The node positions. + * @param layoutInformation + * The necessary information about the graph. * @throws InvalidInputSizeException + * If the given input is somehow invalid in size. */ private static void addNodes(final String[] positions, final LayoutInformation layoutInformation) throws InvalidInputSizeException { - layoutInformation.graph.getData(KShapeLayout.class).setProperty(LayoutOptions.EDGE_ROUTING, EdgeRouting.ORTHOGONAL); + layoutInformation.getGraph().getData(KShapeLayout.class).setProperty(LayoutOptions.EDGE_ROUTING, EdgeRouting.ORTHOGONAL); // prepare array of lists List<Object> child; @@ -97,10 +99,10 @@ public final class GraphFlowLayouter { // create Node node = KimlUtil.createInitializedNode(); - node.setParent(layoutInformation.graph); + node.setParent(layoutInformation.getGraph()); child = new ArrayList<Object>(); - layoutInformation.children[i] = child; + layoutInformation.getChildren()[i] = child; child.add(node); // set node dimensions @@ -156,8 +158,10 @@ public final class GraphFlowLayouter { * The fourth number must be the index of the target node ArrayList, where * the target port is located. * - * @param input - * The parsed String array + * @param edgeInfo + * The single elements for the edges. + * @param layoutInformation + * The necessary information about the graph. * @throws UninitializedGraphException * If the graph has not yet been initialized. * @throws InvalidInputSizeException @@ -165,7 +169,7 @@ public final class GraphFlowLayouter { */ private static void addEdges(final String[] edgeInfo, final LayoutInformation layoutInformation) throws UninitializedGraphException, InvalidInputSizeException { // cannot add edges if no graph was constructed - if (layoutInformation.children == null) { + if (layoutInformation.getChildren() == null) { throw new UninitializedGraphException(); } if ((edgeInfo.length % 4) != 0) { @@ -186,13 +190,13 @@ public final class GraphFlowLayouter { // read from String sourceID = Integer.parseInt(edgeInfo[e++]); targetID = Integer.parseInt(edgeInfo[e++]); - sourcePort = (KPort) layoutInformation.children[sourceID].get(1 + Integer.parseInt(edgeInfo[e++])); - targetPort = (KPort) layoutInformation.children[targetID].get(1 + Integer.parseInt(edgeInfo[e++])); + sourcePort = (KPort) layoutInformation.getChildren()[sourceID].get(1 + Integer.parseInt(edgeInfo[e++])); + targetPort = (KPort) layoutInformation.getChildren()[targetID].get(1 + Integer.parseInt(edgeInfo[e++])); // add edge to graph edge = KimlUtil.createInitializedEdge(); - edge.setSource((KNode) layoutInformation.children[sourceID].get(0)); - edge.setTarget((KNode) layoutInformation.children[targetID].get(0)); + edge.setSource((KNode) layoutInformation.getChildren()[sourceID].get(0)); + edge.setTarget((KNode) layoutInformation.getChildren()[targetID].get(0)); // set ports of edge edge.setSourcePort(sourcePort); @@ -200,15 +204,24 @@ public final class GraphFlowLayouter { edge.setTargetPort(targetPort); targetPort.getEdges().add(edge); - layoutInformation.edges[ea++] = edge; + layoutInformation.getEdges()[ea++] = edge; } } + /** + * Assembles the necessary layout information using the given parameters. + * + * @param nodesStr + * The string containing the information about the nodes. + * @param edgesStr + * The string containing the information about the edges. + * @return An object containing the layout information for the graph. + */ private static LayoutInformation assembleLayoutInformation(final String nodesStr, final String edgesStr) { final String[] edgeInfo = edgesStr.split(" "); final String[] positions = nodesStr.split(" "); - final ArrayList[] children = new ArrayList[positions.length / 2]; + final List<Object>[] children = new ArrayList[positions.length / 2]; final KEdge[] edges = new KEdge[edgeInfo.length / 4]; final LayoutInformation layoutInformation = new LayoutInformation(KimlUtil.createInitializedNode(), children, edges); @@ -226,6 +239,11 @@ public final class GraphFlowLayouter { * @param edges * A String containing connection information of edges. * @return A String of space separated node positions. + * + * @throws UninitializedGraphException + * If the graph has not been initialized yet. + * @throws InvalidInputSizeException + * If the input size is somehow invalid. */ public static String layoutGraph(final String nodes, final String edges) throws UninitializedGraphException, InvalidInputSizeException { final LayoutInformation layoutInformation = GraphFlowLayouter.assembleLayoutInformation(nodes, edges); @@ -237,7 +255,7 @@ public final class GraphFlowLayouter { final AbstractLayoutProvider layoutProvider = new LayeredLayoutProvider(); // Perform layout on the created graph - layoutProvider.doLayout(layoutInformation.graph, progressMonitor); + layoutProvider.doLayout(layoutInformation.getGraph(), progressMonitor); // convert layouted graph positions to String final String layoutedGraph = GraphFlowLayouter.getPositions(layoutInformation); @@ -248,15 +266,15 @@ public final class GraphFlowLayouter { /** * Reads the (layouted) positions of a constructed KGraph and writes them as integers to a string, seperating each number with a space. * - * @param graph - * The graph with at least one child - * @return The constructed string + * @param layoutInformation + * The object containing the necessary information about the graph. + * @return The constructed string. */ private static String getPositions(final LayoutInformation layoutInformation) { final StringBuilder sb = new StringBuilder(); KShapeLayout layout; - for (final KNode node : layoutInformation.graph.getChildren()) { + for (final KNode node : layoutInformation.getGraph().getChildren()) { layout = node.getData(KShapeLayout.class); final int x = Math.round(layout.getXpos() + (layout.getWidth() / 2)); final int y = Math.round(layout.getYpos() + (layout.getHeight() / 2)); @@ -280,7 +298,7 @@ public final class GraphFlowLayouter { final StringBuilder sb = new StringBuilder(); // iterate edges - for (final KEdge edge : layoutInformation.edges) { + for (final KEdge edge : layoutInformation.getEdges()) { final KEdgeLayout layout = edge.getData(KEdgeLayout.class); final EList<KPoint> bendPoints = layout.getBendPoints(); @@ -299,23 +317,65 @@ public final class GraphFlowLayouter { return sb.toString(); } + /** + * A container for the layout information which will be assembled during the layout process. + * + * @author Nils Christian Ehmke + */ private static class LayoutInformation { - public final KNode graph; + private final KNode graph; /** * An array containing the child node and the ports from left to right. */ - public final List<Object>[] children; + private final List<Object>[] children; /** * An array containing edges. This way we can return edge information in the same order. */ - public final KEdge[] edges; + private final KEdge[] edges; + /** + * Creates a new instance of this class. + * + * @param graph + * The graph itself. + * @param children + * The child notes and the ports. + * @param edges + * The edges of the graph. + */ public LayoutInformation(final KNode graph, final List<Object>[] children, final KEdge[] edges) { this.graph = graph; this.children = children; this.edges = edges; } + /** + * Getter for the property {@link LayoutInformation#graph}. + * + * @return The current value of the property. + */ + public KNode getGraph() { + return this.graph; + } + + /** + * Getter for the property {@link LayoutInformation#children}. + * + * @return The current value of the property. + */ + public List<Object>[] getChildren() { + return this.children; + } + + /** + * Getter for the property {@link LayoutInformation#edges}. + * + * @return The current value of the property. + */ + public KEdge[] getEdges() { + return this.edges; + } + } } diff --git a/Kieker.WebGUI/src/main/java/kieker/webgui/persistence/IProjectDAO.java b/Kieker.WebGUI/src/main/java/kieker/webgui/persistence/IProjectDAO.java index 21d5e001d077ffce11fb0f390dce6ec285165440..cb2f98390ffdadd27e8d3456f01cd3f8a6077c00 100644 --- a/Kieker.WebGUI/src/main/java/kieker/webgui/persistence/IProjectDAO.java +++ b/Kieker.WebGUI/src/main/java/kieker/webgui/persistence/IProjectDAO.java @@ -18,16 +18,14 @@ package kieker.webgui.persistence; import java.io.File; import java.io.IOException; -import java.net.MalformedURLException; -import java.net.URL; import java.util.Collection; import java.util.List; import org.springframework.security.access.prepost.PreAuthorize; -import kieker.analysis.model.analysisMetaModel.MIDependency; import kieker.analysis.model.analysisMetaModel.MIProject; import kieker.webgui.common.ClassAndMethodContainer; +import kieker.webgui.common.ComponentListContainer; import kieker.webgui.common.exception.NewerProjectException; import kieker.webgui.common.exception.ProjectAlreadyExistingException; import kieker.webgui.common.exception.ProjectNotExistingException; @@ -193,28 +191,6 @@ public interface IProjectDAO { @PreAuthorize("isAuthenticated()") public abstract Collection<String> listAllProjects(); - /** - * This method can be used to deliver the fully qualified URL of a given dependency for a given project. - * - * @param lib - * The library whose URL should be delivered. - * @param project - * The corresponding project of the library. - * @return The URL to the given library if everything went well. - * @throws MalformedURLException - * If the URL is for some reason invalid. - */ - @PreAuthorize("isAuthenticated()") - public abstract URL getURL(MIDependency lib, String project) throws MalformedURLException; - - /** - * Delivers the {@link URL}-element pointing to the kieker library. - * - * @return The kieker library. - */ - @PreAuthorize("isAuthenticated()") - public abstract URL getKiekerURL(); - /** * Delivers the kax-file for the given project. * @@ -233,8 +209,20 @@ public interface IProjectDAO { * @param libName * The name of the library. * @return true if and only if the given library has been removed. + * @throws IOException + * If something went wrong during the reloading of the components. */ @PreAuthorize("hasAnyRole('User', 'Administrator')") - public abstract boolean deleteLibrary(String projectName, String libName); + public abstract boolean deleteLibrary(String projectName, String libName) throws IOException; + + /** + * Delivers the available components (readers, filters and repositories) for the given project. + * + * @param project + * The project whose components should be loaded. + * @return An object containing the available components as model instances. + */ + @PreAuthorize("isAuthenticated()") + public abstract ComponentListContainer getAvailableComponents(String project); } diff --git a/Kieker.WebGUI/src/main/java/kieker/webgui/persistence/impl/DerbyUserDAOImpl.java b/Kieker.WebGUI/src/main/java/kieker/webgui/persistence/impl/DerbyUserDAOImpl.java index b64ce0df374631b5438562ad6d888f347bd99cdb..0d61a84a6af00be7b313e2594e10a4b8b45e78e8 100644 --- a/Kieker.WebGUI/src/main/java/kieker/webgui/persistence/impl/DerbyUserDAOImpl.java +++ b/Kieker.WebGUI/src/main/java/kieker/webgui/persistence/impl/DerbyUserDAOImpl.java @@ -23,8 +23,6 @@ import java.sql.SQLException; import java.util.ArrayList; import java.util.List; -import javax.annotation.PostConstruct; -import javax.annotation.PreDestroy; import javax.sql.DataSource; import org.springframework.beans.factory.annotation.Autowired; @@ -52,7 +50,6 @@ public class DerbyUserDAOImpl implements IUserDAO { @Autowired private DataSource dataSource; - private Connection connection; /** * Default constructor. <b>Do not use this constructor. This bean is Spring managed.</b> @@ -61,28 +58,6 @@ public class DerbyUserDAOImpl implements IUserDAO { // No code necessary } - /** - * This method initializes the object. <b>Do not call this method manually. It will only be accessed by Spring.</b> - * - * @throws SQLException - * If something went wrong during the initialization. - */ - @PostConstruct - public void initialize() throws SQLException { - this.connection = DataSourceUtils.getConnection(this.dataSource); - } - - /** - * This method "destroys" the object by closing the connection to the database. <b>Do not call this method manually. It will only be accessed by Spring.</b> - * - * @throws SQLException - * If something went wrong during the initialization. - */ - @PreDestroy - public void destroy() throws SQLException { - this.connection.close(); - } - /* * (non-Javadoc) * @@ -91,9 +66,10 @@ public class DerbyUserDAOImpl implements IUserDAO { @Override @PreAuthorize("hasRole('Administrator')") public void addUser(final User user) throws DataAccessException { + final Connection connection = DataSourceUtils.getConnection(this.dataSource); PreparedStatement userCmd = null; try { - userCmd = this.connection.prepareStatement("INSERT INTO Users (name, password, isGuest, isUser, isAdministrator, isEnabled) VALUES (?, ?, ?, ?, ?, ?)"); + userCmd = connection.prepareStatement("INSERT INTO Users (name, password, isGuest, isUser, isAdministrator, isEnabled) VALUES (?, ?, ?, ?, ?, ?)"); // Use all properties of the given object userCmd.setString(1, user.getName()); @@ -117,6 +93,12 @@ public class DerbyUserDAOImpl implements IUserDAO { DerbyUserDAOImpl.LOG.error("Could not close prepared statement.", ex); } } + + try { + connection.close(); + } catch (final SQLException ex) { + DerbyUserDAOImpl.LOG.error("Could not close connection.", ex); + } } } @@ -128,9 +110,10 @@ public class DerbyUserDAOImpl implements IUserDAO { @Override @PreAuthorize("hasRole('Administrator')") public void deleteUser(final User user) throws DataAccessException { + final Connection connection = DataSourceUtils.getConnection(this.dataSource); PreparedStatement delCmd = null; try { - delCmd = this.connection.prepareStatement("DELETE FROM Users WHERE name=?"); + delCmd = connection.prepareStatement("DELETE FROM Users WHERE name=?"); delCmd.setString(1, user.getName()); @@ -147,6 +130,12 @@ public class DerbyUserDAOImpl implements IUserDAO { DerbyUserDAOImpl.LOG.error("Could not close prepared statement.", ex); } } + + try { + connection.close(); + } catch (final SQLException ex) { + DerbyUserDAOImpl.LOG.error("Could not close connection.", ex); + } } } @@ -158,16 +147,16 @@ public class DerbyUserDAOImpl implements IUserDAO { @Override @PreAuthorize("hasRole('Administrator')") public void editUser(final User user) throws DataAccessException { + final Connection connection = DataSourceUtils.getConnection(this.dataSource); PreparedStatement updateCmd = null; try { // Choose the right update command, depending on whether the password has to be changed or not if (user.getPassword() == null) { - updateCmd = this.connection.prepareStatement("UPDATE Users SET name=?, isGuest=?, isUser=?, isAdministrator=?, isEnabled=? WHERE name=?"); + updateCmd = connection.prepareStatement("UPDATE Users SET name=?, isGuest=?, isUser=?, isAdministrator=?, isEnabled=? WHERE name=?"); updateCmd.setString(6, user.getName()); } else { // In this case we have to set the password as well - updateCmd = this.connection - .prepareStatement("UPDATE Users SET name=?, isGuest=?, isUser=?, isAdministrator=?, isEnabled=?, password=? WHERE name=?"); + updateCmd = connection.prepareStatement("UPDATE Users SET name=?, isGuest=?, isUser=?, isAdministrator=?, isEnabled=?, password=? WHERE name=?"); updateCmd.setString(6, user.getPassword()); updateCmd.setString(7, user.getName()); } @@ -192,6 +181,12 @@ public class DerbyUserDAOImpl implements IUserDAO { DerbyUserDAOImpl.LOG.error("Could not close prepared statement.", ex); } } + + try { + connection.close(); + } catch (final SQLException ex) { + DerbyUserDAOImpl.LOG.error("Could not close connection.", ex); + } } } @@ -203,13 +198,14 @@ public class DerbyUserDAOImpl implements IUserDAO { @Override @PreAuthorize("hasRole('Administrator')") public List<User> getUsers() throws DataAccessException { + final Connection connection = DataSourceUtils.getConnection(this.dataSource); final List<User> result = new ArrayList<User>(); ResultSet queryResult = null; PreparedStatement getQuery = null; try { - getQuery = this.connection.prepareStatement("SELECT name, isGuest, isUser, isAdministrator, isEnabled FROM Users"); + getQuery = connection.prepareStatement("SELECT name, isGuest, isUser, isAdministrator, isEnabled FROM Users"); // Run through all results queryResult = getQuery.executeQuery(); @@ -243,6 +239,12 @@ public class DerbyUserDAOImpl implements IUserDAO { DerbyUserDAOImpl.LOG.error("Could not close prepared statement.", ex); } } + + try { + connection.close(); + } catch (final SQLException ex) { + DerbyUserDAOImpl.LOG.error("Could not close connection.", ex); + } } return result; diff --git a/Kieker.WebGUI/src/main/java/kieker/webgui/persistence/impl/FSProjectDAOImpl.java b/Kieker.WebGUI/src/main/java/kieker/webgui/persistence/impl/FSProjectDAOImpl.java index a067fffb28c6fe251383fe9abca5822fe53eedc0..d577e0543965f049191b943df0dde75a6e739f54 100644 --- a/Kieker.WebGUI/src/main/java/kieker/webgui/persistence/impl/FSProjectDAOImpl.java +++ b/Kieker.WebGUI/src/main/java/kieker/webgui/persistence/impl/FSProjectDAOImpl.java @@ -22,13 +22,20 @@ import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; +import java.lang.annotation.Annotation; import java.lang.ref.WeakReference; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; import java.net.MalformedURLException; import java.net.URL; import java.security.AccessController; import java.security.PrivilegedAction; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; @@ -36,6 +43,7 @@ import java.util.concurrent.ConcurrentHashMap; import javax.annotation.PostConstruct; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.stereotype.Service; import org.springframework.util.FileCopyUtils; @@ -47,20 +55,36 @@ import com.google.common.io.Files; import kieker.analysis.AnalysisController; import kieker.analysis.model.analysisMetaModel.MIAnalysisMetaModelFactory; -import kieker.analysis.model.analysisMetaModel.MIDependency; +import kieker.analysis.model.analysisMetaModel.MIDisplay; +import kieker.analysis.model.analysisMetaModel.MIFilter; +import kieker.analysis.model.analysisMetaModel.MIInputPort; +import kieker.analysis.model.analysisMetaModel.MIOutputPort; +import kieker.analysis.model.analysisMetaModel.MIPlugin; import kieker.analysis.model.analysisMetaModel.MIProject; -import kieker.analysis.model.analysisMetaModel.impl.MAnalysisMetaModelFactory; +import kieker.analysis.model.analysisMetaModel.MIProperty; +import kieker.analysis.model.analysisMetaModel.MIRepository; +import kieker.analysis.model.analysisMetaModel.MIRepositoryConnector; +import kieker.analysis.plugin.AbstractPlugin; +import kieker.analysis.plugin.annotation.Property; +import kieker.analysis.repository.AbstractRepository; import kieker.common.logging.Log; import kieker.common.logging.LogFactory; import kieker.webgui.common.ClassAndMethodContainer; +import kieker.webgui.common.ComponentListContainer; +import kieker.webgui.common.PluginContainer; +import kieker.webgui.common.RepositoryContainer; +import kieker.webgui.common.exception.ComponentInitializationException; import kieker.webgui.common.exception.NewerProjectException; import kieker.webgui.common.exception.ProjectAlreadyExistingException; import kieker.webgui.common.exception.ProjectNotExistingException; import kieker.webgui.persistence.IProjectDAO; import kieker.webgui.persistence.impl.util.CloseableURLClassLoader; +import kieker.webgui.persistence.impl.util.PluginFinder; import org.primefaces.model.UploadedFile; +import org.eclipse.emf.ecore.EObject; + /** * This is an implementation of the {@link IProjectDAO} interface, which uses the file system to store the available projects and everything. * @@ -71,19 +95,19 @@ public class FSProjectDAOImpl implements IProjectDAO, ReleaseListener { private static final Log LOG = LogFactory.getLog(FSProjectDAOImpl.class); + private static final MIAnalysisMetaModelFactory FACTORY = MIAnalysisMetaModelFactory.eINSTANCE; + private static final String KIEKER_LIB = "kieker-1.6_emf.jar"; private static final String KAX_EXTENSION = "kax"; private static final String LIB_EXTENSION = "jar"; private static final String LIB_DIRECTORY = "lib"; private static final String ROOT_DIRECTORY = "data"; - /** - * The library for kieker which is contained in the war-file as a resource. - */ - private static final String KIEKER_LIB = "kieker-1.6_emf.jar"; + @Autowired + private PluginFinder pluginFinder; - private final MIAnalysisMetaModelFactory factory = new MAnalysisMetaModelFactory(); private final Map<CloseableURLClassLoader, WeakReference<Object>> classLoaders = new ConcurrentHashMap<CloseableURLClassLoader, WeakReference<Object>>(); private final Map<File, WeakReference<Object>> tempDirs = new ConcurrentHashMap<File, WeakReference<Object>>(); + private final Map<String, ComponentListContainer> availableComponents = new ConcurrentHashMap<String, ComponentListContainer>(); /** * Default constructor. <b>Do not use this constructor. This bean is Spring managed.</b> @@ -96,12 +120,642 @@ public class FSProjectDAOImpl implements IProjectDAO, ReleaseListener { * This method initializes the object. <b>Do not call this method manually. It will only be accessed by Spring.</b> * * @throws IOException - * If the creation of the necessary directories failed. + * If either the creation of the necessary directories failed or if something went wrong during the initialization of the projects. */ @PostConstruct public void initialize() throws IOException { // Check for the necessary directories and create them if necessary this.checkDir(FSProjectDAOImpl.ROOT_DIRECTORY); + // This method call will initialize the available components for all projects which currently exist. + this.initializeAvailableComponents(); + } + + /** + * This method initializes the available components for all projects. This means that this method will run through all projects and load all available plugins, + * filters and repositories from the existing libraries - including Kieker. + * + * @throws IOException + * If something went really wrong during the initialization of one of the projects. + */ + private void initializeAvailableComponents() throws IOException { + final Collection<String> projects = this.listAllProjects(); + // Initialize all available components within all projects + for (final String project : projects) { + this.initializeAvailableComponents(project); + } + } + + /** + * This method initializes the available components for the given project. This means that this method will load all available plugins, filters and repositories + * from the existing libraries - including Kieker. + * + * @param project + * The name of the project to be initialized. + * @throws IOException + * If something went really wrong during the initialization of the project. + */ + private void initializeAvailableComponents(final String project) throws IOException { + try { + final CloseableURLClassLoader classLoader = (CloseableURLClassLoader) this.getClassLoader(project, this); + final ClassAndMethodContainer classAndMethodContainer = new ClassAndMethodContainer(classLoader); + + final List<PluginContainer> readers = new ArrayList<PluginContainer>(); + final List<PluginContainer> filters = new ArrayList<PluginContainer>(); + final List<RepositoryContainer> repositories = new ArrayList<RepositoryContainer>(); + + // Update the components using all available dependencies + final Collection<String> libs = this.listAllLibraries(project); + for (final String lib : libs) { + this.initializeAvailableComponents(readers, filters, repositories, this.getURL(lib, project), classLoader, classAndMethodContainer); + } + // Update the components using the Kieker library. + this.initializeAvailableComponents(readers, filters, repositories, this.getKiekerURL(), classLoader, classAndMethodContainer); + + // Assemble the final object containing the available components - but make sure that the lists cannot be modified. + final ComponentListContainer componentList = new ComponentListContainer(Collections.unmodifiableList(readers), Collections.unmodifiableList(filters), + Collections.unmodifiableList(repositories)); + this.availableComponents.put(project, componentList); + + classLoader.close(); + } catch (final ProjectNotExistingException ex) { + // Technically this should not happen. Log but ignore this exception. + FSProjectDAOImpl.LOG.warn("An error occured while initializing the project '" + project + "'.", ex); + } + } + + /** + * This method initializes the available components for the given library and the given project. It runs through the library and extracts all available readers, + * filters and repositories. + * + * @param readers + * The list of readers which will be filled during the extraction process. + * @param filters + * The list of filters which will be filled during the extraction process. + * @param repositories + * The list of repositories which will be filled during the extraction process. + * @param lib + * The url to the library which will be used for the extraction. + * @param classLoader + * The classloader which will be used to load the classes. + * @param classAndMethodContainer + * The object containing classes and methods for the extraction process. + * @throws IOException + * If something went really wrong during the process. + */ + private void initializeAvailableComponents(final List<PluginContainer> readers, final List<PluginContainer> filters, + final List<RepositoryContainer> repositories, final URL lib, final ClassLoader classLoader, final ClassAndMethodContainer classAndMethodContainer) throws + IOException { + // FInd the available classes within the library + final List<Class<AbstractRepository>> repositoryClasses = this.pluginFinder.getAllRepositoriesWithinJar(lib, classLoader, classAndMethodContainer); + final List<Class<AbstractPlugin>> pluginClasses = this.pluginFinder.getAllPluginsWithinJar(lib, classLoader, classAndMethodContainer); + + // Convert the repositories into model instances + for (final Class<AbstractRepository> repository : repositoryClasses) { + if (!(Modifier.isAbstract(repository.getModifiers()) || FSProjectDAOImpl.isProgrammaticOnly(repository, classAndMethodContainer))) { + repositories.add(FSProjectDAOImpl.convertRepository(repository, classAndMethodContainer)); + } + } + + // Convert the plugins into model instances + for (final Class<AbstractPlugin> plugin : pluginClasses) { + if (!(Modifier.isAbstract(plugin.getModifiers()) || FSProjectDAOImpl.isProgrammaticOnly(plugin, classAndMethodContainer))) { + final PluginContainer pluginContainer = FSProjectDAOImpl.convertPlugin(plugin, classAndMethodContainer); + if (pluginContainer.isReader()) { + readers.add(pluginContainer); + } else { + filters.add(pluginContainer); + } + } + } + } + + /** + * This method converts the given class instance into a model instance. + * + * @param clazz + * The class to be converted. + * @param classAndMethodContainer + * The container which will be used for the converting. + * @return An object containing the model instance. + */ + private static PluginContainer convertPlugin(final Class<AbstractPlugin> clazz, final ClassAndMethodContainer classAndMethodContainer) { + final Map<String, String> propertyDescriptions = new HashMap<String, String>(); + final Map<String, String> displayDescriptions = new HashMap<String, String>(); + boolean fullyInitialized; + String description; + String dependency; + + // Create a new instance for the model + final MIPlugin plugin; + if (classAndMethodContainer.getAbstractReaderPluginClass().isAssignableFrom(clazz)) { + plugin = FSProjectDAOImpl.FACTORY.createReader(); + } else { + plugin = FSProjectDAOImpl.FACTORY.createFilter(); + } + plugin.setClassname(clazz.getName()); + plugin.setName(clazz.getSimpleName()); + + // Those are the attributes which can go wrong because of the lazy class loading + try { + FSProjectDAOImpl.fillProperties(clazz, plugin, classAndMethodContainer, propertyDescriptions); + FSProjectDAOImpl.fillPorts(clazz, plugin, classAndMethodContainer); + FSProjectDAOImpl.fillDisplays(clazz, plugin, classAndMethodContainer, displayDescriptions); + description = FSProjectDAOImpl.getDescription(clazz, classAndMethodContainer); + dependency = FSProjectDAOImpl.getDependencies(clazz, classAndMethodContainer); + + fullyInitialized = true; + } catch (final ComponentInitializationException ex) { + // This exception can occur if (for example) a class is missing. We clear the component as far as possible + plugin.getProperties().clear(); + plugin.getOutputPorts().clear(); + propertyDescriptions.clear(); + displayDescriptions.clear(); + if (plugin instanceof MIFilter) { + ((MIFilter) plugin).getInputPorts().clear(); + } + description = ""; + dependency = ""; + + fullyInitialized = false; + + FSProjectDAOImpl.LOG.warn("A component with the classname '" + clazz.getCanonicalName() + "' could not be initialized.", ex); + } + + // Assemble the resulting instance with everything we could extract and return it + return new PluginContainer(plugin, description, dependency, fullyInitialized, Collections.unmodifiableMap(propertyDescriptions), + Collections.unmodifiableMap(displayDescriptions)); + } + + /** + * Tells whether the given class is "programmaticOnly". If there is no annotation which could tell the state of the flag, true will be returned. + * + * @param clazz + * The class of the plugin or repository. + * @param classAndMethodContainer + * The container which will be used for the reflection access. + * @return The state of the programmaticOnly flag of the plugin or repository. + */ + private static boolean isProgrammaticOnly(final Class<?> clazz, final ClassAndMethodContainer classAndMethodContainer) { + boolean result; + try { + // Get the two potential annotations + final Annotation annotationPlugin = clazz.getAnnotation(classAndMethodContainer.getPluginAnnotationClass()); + final Annotation annotationRepository = clazz.getAnnotation(classAndMethodContainer.getRepositoryAnnotationClass()); + + final Method pluginProgOnlyMethod = classAndMethodContainer.getPluginProgrammaticOnlyMethod(); + final Method repoProgOnlyMethod = classAndMethodContainer.getRepositoryProgrammaticOnlyMethod(); + + // Now check which one of them is available + if (annotationPlugin == null) { + if (annotationRepository == null) { + // None. Say it is programmatic only. + result = true; + } else { + result = (Boolean) repoProgOnlyMethod.invoke(annotationRepository); + } + } else { + result = (Boolean) pluginProgOnlyMethod.invoke(annotationPlugin); + } + + } catch (final IllegalAccessException ex) { + result = true; + } catch (final IllegalArgumentException ex) { + result = true; + } catch (final InvocationTargetException ex) { + result = true; + } catch (final NoClassDefFoundError ex) { + result = true; + } + + return result; + } + + /** + * This method converts the given class instance into a model instance. + * + * @param clazz + * The class to be converted. + * @param classAndMethodContainer + * The container which will be used for the converting. + * @return An object containing the model instance. + */ + private static RepositoryContainer convertRepository(final Class<AbstractRepository> clazz, final ClassAndMethodContainer classAndMethodContainer) { + final Map<String, String> propertyDescriptions = new HashMap<String, String>(); + boolean fullyInitialized; + String description; + String dependency; + + // Create a new instance for the model + final MIRepository repository = FSProjectDAOImpl.FACTORY.createRepository(); + + repository.setClassname(clazz.getName()); + repository.setName(clazz.getSimpleName()); + + // Those are the attributes which can go wrong because of the lazy class loading + try { + FSProjectDAOImpl.fillProperties(clazz, repository, classAndMethodContainer, propertyDescriptions); + description = FSProjectDAOImpl.getDescription(clazz, classAndMethodContainer); + dependency = FSProjectDAOImpl.getDependencies(clazz, classAndMethodContainer); + + fullyInitialized = true; + } catch (final ComponentInitializationException ex) { + // This exception can occur if (for example) a class is missing. We clear the component as far as possible + repository.getProperties().clear(); + description = ""; + dependency = ""; + + fullyInitialized = false; + + FSProjectDAOImpl.LOG.warn("A component with the classname '" + clazz.getCanonicalName() + "' could not be initialized.", ex); + } + + // Assemble the resulting instance with everything we could extract and return it + return new RepositoryContainer(repository, description, dependency, fullyInitialized, Collections.unmodifiableMap(propertyDescriptions)); + } + + /** + * This method can be used to get the description of an {@link AbstractPlugin}- or an {@link AbstractRepository}-class. The description is read via the + * annotation using the java reflection API. + * + * @param clazz + * The class whose description should be extracted. + * @param classAndMethodContainer + * The container which will be used for the reflection invocation. + * @return The description for the class. + * @throws ComponentInitializationException + * If the description could not be loaded. + */ + private static String getDescription(final Class<?> clazz, final ClassAndMethodContainer classAndMethodContainer) throws ComponentInitializationException { + try { + final String description; + // Get the two potential annotations + final Annotation annotationPlugin = clazz.getAnnotation(classAndMethodContainer.getPluginAnnotationClass()); + final Annotation annotationRepository = clazz.getAnnotation(classAndMethodContainer.getRepositoryAnnotationClass()); + + final Method pluginDescrMethod = classAndMethodContainer.getPluginDescriptionMethod(); + final Method repoDescrMethod = classAndMethodContainer.getRepositoryDescriptionMethod(); + + // Now check which one of them is available + if ((annotationPlugin == null) || ((String) pluginDescrMethod.invoke(annotationPlugin)).isEmpty()) { + if ((annotationRepository == null) || ((String) repoDescrMethod.invoke(annotationRepository)).isEmpty()) { + // None. Deliver a human readable substitute. + description = "No description available"; + } else { + description = (String) repoDescrMethod.invoke(annotationRepository); + } + } else { + description = (String) pluginDescrMethod.invoke(annotationPlugin); + } + + return description; + } catch (final IllegalAccessException ex) { + throw new ComponentInitializationException("Could not load the description of the class.", ex); + } catch (final IllegalArgumentException ex) { + throw new ComponentInitializationException("Could not load the description of the class.", ex); + } catch (final InvocationTargetException ex) { + throw new ComponentInitializationException("Could not load the description of the class.", ex); + } catch (final NoClassDefFoundError ex) { + throw new ComponentInitializationException("Could not load the description of the class.", ex); + } + } + + /** + * Delivers the content of the dependencies-field of the given component (plugin or repository). + * + * @param clazz + * The class whose dependencies should be extracted. + * @param classAndMethodContainer + * The container which will be used for the reflection invocation. + * @return The dependencies for the class. + * @throws ComponentInitializationException + * If the dependencies could not be loaded. + */ + private static String getDependencies(final Class<?> clazz, final ClassAndMethodContainer classAndMethodContainer) throws ComponentInitializationException { + try { + final String description; + + // Get the two potential annotations + final Annotation annotationPlugin = clazz.getAnnotation(classAndMethodContainer.getPluginAnnotationClass()); + final Annotation annotationRepository = clazz.getAnnotation(classAndMethodContainer.getRepositoryAnnotationClass()); + + final Method pluginDepMethod = classAndMethodContainer.getPluginDependenciesMethod(); + final Method repoDepMethod = classAndMethodContainer.getRepositoryDependenciesMethod(); + + // Now check which one of them is available + if (annotationPlugin == null) { + if (annotationRepository == null) { + // None. Deliver a human readable substitute. + description = "No dependency information available"; + } else { + description = (String) repoDepMethod.invoke(annotationRepository); + } + } else { + description = (String) pluginDepMethod.invoke(annotationPlugin); + } + + return description; + } catch (final IllegalAccessException ex) { + throw new ComponentInitializationException("Could not load the dependencies of the class.", ex); + } catch (final IllegalArgumentException ex) { + throw new ComponentInitializationException("Could not load the dependencies of the class.", ex); + } catch (final InvocationTargetException ex) { + throw new ComponentInitializationException("Could not load the dependencies of the class.", ex); + } catch (final NoClassDefFoundError ex) { + throw new ComponentInitializationException("Could not load the dependencies of the class.", ex); + } + } + + /** + * This method fills the properties of the given repository or plugin. In other words: It tries to extract the list of properties from the given class (using the + * annotations) and to convert them into model instances. + * + * @param clazz + * The class to be used as a base. + * @param component + * The component to be filled. + * @param classAndMethodContainer + * The container which will be used for the reflection invocation. + * @param propertyDescriptions + * The map which will later contain the properties description. + * @throws ComponentInitializationException + * If the properties could not be loaded. + */ + private static void fillProperties(final Class<?> clazz, final EObject component, final ClassAndMethodContainer classAndMethodContainer, + final Map<String, String> propertyDescriptions) throws ComponentInitializationException { + try { + // Get the default configuration and use it to initialize the model repository + final List<Annotation> properties = FSProjectDAOImpl.getProperties(clazz, classAndMethodContainer); + + for (final Annotation property : properties) { + final MIProperty mProperty = FSProjectDAOImpl.FACTORY.createProperty(); + + mProperty.setName((String) classAndMethodContainer.getPropertyNameMethod().invoke(property)); + mProperty.setValue((String) classAndMethodContainer.getPropertyDefaultValueMethod().invoke(property)); + + if (component instanceof MIPlugin) { + ((MIPlugin) component).getProperties().add(mProperty); + } else { + ((MIRepository) component).getProperties().add(mProperty); + } + + // Get the description as well + final String description = (String) classAndMethodContainer.getPropertyDescriptionMethod().invoke(property); + propertyDescriptions.put(mProperty.getName(), description); + } + } catch (final IllegalAccessException ex) { + throw new ComponentInitializationException("Could not load the properties of the class.", ex); + } catch (final IllegalArgumentException ex) { + throw new ComponentInitializationException("Could not load the properties of the class.", ex); + } catch (final InvocationTargetException ex) { + throw new ComponentInitializationException("Could not load the properties of the class.", ex); + } catch (final NoClassDefFoundError ex) { + throw new ComponentInitializationException("Could not load the properties of the class.", ex); + } + } + + /** + * Delivers the properties of the given class, using the annotations and the java reflection API. + * + * @param clazz + * The class whose properties will be delivered. + * @param classAndMethodContainer + * The container which will be used for the reflection invocation. + * @return A list of properties. It contains instances of {@link Property} - but those from the project class loader. + * @throws InvocationTargetException + * If something went wrong during the reflection invocation. + * @throws IllegalAccessException + * If something went wrong during the reflection invocation. + * @throws IllegalArgumentException + * If something went wrong during the reflection invocation. + */ + private static List<Annotation> getProperties(final Class<?> clazz, final ClassAndMethodContainer classAndMethodContainer) throws IllegalArgumentException, + IllegalAccessException, InvocationTargetException { + // Get the two potential annotations + final Annotation annotationPlugin = clazz.getAnnotation(classAndMethodContainer.getPluginAnnotationClass()); + final Annotation annotationRepository = clazz.getAnnotation(classAndMethodContainer.getRepositoryAnnotationClass()); + + final Annotation[] properties; + + // Now check which one of them is available + if (annotationPlugin == null) { + if (annotationRepository == null) { + // None. + properties = new Property[0]; + } else { + properties = (Annotation[]) classAndMethodContainer.getRepositoryConfigurationMethod().invoke(annotationRepository); + } + } else { + properties = (Annotation[]) classAndMethodContainer.getPluginConfigurationMethod().invoke(annotationPlugin); + } + + return Arrays.asList(properties); + } + + /** + * This method fills the displays of the given plugin. In other words: It tries to extract the list of displays from the given class (using the annotations) and + * to convert them into model instances. + * + * @param clazz + * The class to be used as a base. + * @param plugin + * The plugin to be filled. + * @param classAndMethodContainer + * The container which will be used for the reflection invocation. + * @param displayDescriptions + * The map which will later contain the display description. + * @throws ComponentInitializationException + * If the properties could not be loaded. + */ + private static void fillDisplays(final Class<AbstractPlugin> clazz, final MIPlugin plugin, final ClassAndMethodContainer classAndMethodContainer, + final Map<String, String> displayDescriptions) throws ComponentInitializationException { + try { + // Get the displays and convert them into model instances + final List<Annotation> displays = FSProjectDAOImpl.getDisplays(clazz, classAndMethodContainer); + + for (final Annotation display : displays) { + final MIDisplay mDisplay = FSProjectDAOImpl.FACTORY.createDisplay(); + mDisplay.setName((String) classAndMethodContainer.getDisplayNameMethod().invoke(display)); + plugin.getDisplays().add(mDisplay); + + // Get the description as well + final String description = (String) classAndMethodContainer.getDisplayDescriptionMethod().invoke(display); + displayDescriptions.put(mDisplay.getName(), description); + } + } catch (final IllegalAccessException ex) { + throw new ComponentInitializationException("Could not load the displays of the class.", ex); + } catch (final IllegalArgumentException ex) { + throw new ComponentInitializationException("Could not load the displays of the class.", ex); + } catch (final InvocationTargetException ex) { + throw new ComponentInitializationException("Could not load the displays of the class.", ex); + } catch (final NoClassDefFoundError ex) { + throw new ComponentInitializationException("Could not load the displays of the class.", ex); + } + } + + /** + * This method fills the ports of the given plugin. In other words: It tries to extract the list of ports from the given class (using the annotations) and + * to convert them into model instances. + * + * @param clazz + * The class to be used as a base. + * @param plugin + * The plugin to be filled. + * @param classAndMethodContainer + * The container which will be used for the reflection invocation. + * @throws ComponentInitializationException + * If the properties could not be loaded. + */ + private static void fillPorts(final Class<AbstractPlugin> clazz, final MIPlugin plugin, final ClassAndMethodContainer classAndMethodContainer) throws + ComponentInitializationException { + try { + // Get the ports + final List<Annotation> inputPorts = FSProjectDAOImpl.getInputPorts(clazz, classAndMethodContainer); + final List<Annotation> outputPorts = FSProjectDAOImpl.getOutputPorts(clazz, classAndMethodContainer); + final List<Annotation> repositoryPorts = FSProjectDAOImpl.getRepositoryPorts(clazz, classAndMethodContainer); + + // Add input ports + if (plugin instanceof MIFilter) { + for (final Annotation inputPort : inputPorts) { + final MIInputPort mInputPort = FSProjectDAOImpl.FACTORY.createInputPort(); + mInputPort.setName((String) classAndMethodContainer.getInputPortNameMethod().invoke(inputPort)); + mInputPort.setParent((MIFilter) plugin); + } + } + + // Add output ports. + for (final Annotation outputPort : outputPorts) { + final MIOutputPort mOutputPort = FSProjectDAOImpl.FACTORY.createOutputPort(); + mOutputPort.setName((String) classAndMethodContainer.getOutputPortNameMethod().invoke(outputPort)); + mOutputPort.setParent(plugin); + } + + // Add repository ports. + for (final Annotation repositoryPort : repositoryPorts) { + final MIRepositoryConnector mConnector = FSProjectDAOImpl.FACTORY.createRepositoryConnector(); + mConnector.setName((String) classAndMethodContainer.getRepositoryPortNameMethod().invoke(repositoryPort)); + plugin.getRepositories().add(mConnector); + } + } catch (final IllegalAccessException ex) { + throw new ComponentInitializationException("Could not load the ports of the class.", ex); + } catch (final IllegalArgumentException ex) { + throw new ComponentInitializationException("Could not load the ports of the class.", ex); + } catch (final InvocationTargetException ex) { + throw new ComponentInitializationException("Could not load the ports of the class.", ex); + } catch (final NoClassDefFoundError ex) { + throw new ComponentInitializationException("Could not load the ports of the class.", ex); + } + } + + /** + * Searches for input ports within the given class and returns them. + * + * @param clazz + * The class to be analyzed. + * @param classAndMethodContainer + * The container which will be used for the reflection invocation. + * @return A list containing the available input ports. + */ + private static List<Annotation> getInputPorts(final Class<?> clazz, final ClassAndMethodContainer classAndMethodContainer) { + final List<Annotation> result = new ArrayList<Annotation>(); + + for (final Method method : clazz.getMethods()) { + // Get the potential annotation + final Annotation annotationPort = method.getAnnotation(classAndMethodContainer.getInputPortAnnotationClass()); + // Now check whether it is available + if (annotationPort != null) { + result.add(annotationPort); + } + } + + return result; + } + + /** + * Searches for output ports within the given class and returns them. + * + * @param clazz + * The class to be analyzed. + * @param classAndMethodContainer + * The container which will be used for the reflection invocation. + * @return A list containing the available output ports. + * @throws InvocationTargetException + * If something went wrong during the reflection invocation. + * @throws IllegalAccessException + * If something went wrong during the reflection invocation. + * @throws IllegalArgumentException + * If something went wrong during the reflection invocation. + */ + private static List<Annotation> getOutputPorts(final Class<?> clazz, final ClassAndMethodContainer classAndMethodContainer) throws IllegalArgumentException, + IllegalAccessException, InvocationTargetException { + final List<Annotation> result = new ArrayList<Annotation>(); + + // Get the potential annotation + final Annotation annotationPlugin = clazz.getAnnotation(classAndMethodContainer.getPluginAnnotationClass()); + + // Now check whether it is available + if (annotationPlugin != null) { + for (final Annotation oPort : (Annotation[]) classAndMethodContainer.getPluginOutputPortsMethod().invoke(annotationPlugin)) { + result.add(oPort); + } + } + + return result; + } + + /** + * Searches for repository ports within the given class and returns them. + * + * @param clazz + * The class to be analyzed. + * @param classAndMethodContainer + * The container which will be used for the reflection invocation. + * @return A list containing the available repository ports. + * @throws InvocationTargetException + * If something went wrong during the reflection invocation. + * @throws IllegalAccessException + * If something went wrong during the reflection invocation. + * @throws IllegalArgumentException + * If something went wrong during the reflection invocation. + */ + private static List<Annotation> getRepositoryPorts(final Class<?> clazz, final ClassAndMethodContainer classAndMethodContainer) throws IllegalArgumentException, + IllegalAccessException, InvocationTargetException { + final List<Annotation> result = new ArrayList<Annotation>(); + + // Get the potential annotation + final Annotation annotationPlugin = clazz.getAnnotation(classAndMethodContainer.getPluginAnnotationClass()); + + // Now check whether it is available + if (annotationPlugin != null) { + for (final Annotation rPort : (Annotation[]) classAndMethodContainer.getPluginRepositoryPortsMethod().invoke(annotationPlugin)) { + result.add(rPort); + } + } + + return result; + } + + /** + * Searches for displays within the given class and returns them. + * + * @param clazz + * The class to be analyzed. + * @param classAndMethodContainer + * The container which will be used for the reflection invocation. + * @return A list containing the available displays. + */ + private static List<Annotation> getDisplays(final Class<?> clazz, final ClassAndMethodContainer classAndMethodContainer) { + final List<Annotation> result = new ArrayList<Annotation>(); + + for (final Method method : clazz.getMethods()) { + // Get the potential annotation + final Annotation display = method.getAnnotation(classAndMethodContainer.getDisplayAnnotationClass()); + // Now check whether it is available + if (display != null) { + result.add(display); + } + } + + return result; } /** @@ -134,7 +788,7 @@ public class FSProjectDAOImpl implements IProjectDAO, ReleaseListener { final File libDir = this.assembleLibDir(projectName); // We need an "empty" project in order to save it. - final MIProject emptyProject = this.factory.createProject(); + final MIProject emptyProject = FSProjectDAOImpl.FACTORY.createProject(); // Make sure that the project doesn't exist already if (projectDir.exists()) { @@ -146,6 +800,7 @@ public class FSProjectDAOImpl implements IProjectDAO, ReleaseListener { if (projectDir.mkdir() && libDir.mkdir()) { // Try to save the file AnalysisController.saveToFile(projectFile, emptyProject); + this.initializeAvailableComponents(projectName); } else { // The directories could not be created throw new IOException("Project-Directories could not be created."); @@ -322,6 +977,9 @@ public class FSProjectDAOImpl implements IProjectDAO, ReleaseListener { // Copy the data - the streams will be closed in this method. FileCopyUtils.copy(in, out); + + // Reload the available components + this.initializeAvailableComponents(projectName); } /* @@ -432,6 +1090,12 @@ public class FSProjectDAOImpl implements IProjectDAO, ReleaseListener { return result; } + @Override + @PreAuthorize("isAuthenticated()") + public ComponentListContainer getAvailableComponents(final String project) { + return this.availableComponents.get(project); + } + /** * Checks whether a project with the name exists on the file system. * @@ -443,16 +1107,9 @@ public class FSProjectDAOImpl implements IProjectDAO, ReleaseListener { return this.assembleKaxFile(projectName).exists(); } - /* - * (non-Javadoc) - * - * @see kieker.webgui.persistence.IProjectDAO#getURL(kieker.analysis.model.analysisMetaModel.MIDependency, java.lang.String) - */ - @Override - @PreAuthorize("isAuthenticated()") - public URL getURL(final MIDependency lib, final String project) throws MalformedURLException { + private URL getURL(final String lib, final String project) throws MalformedURLException { final File file = new File(FSProjectDAOImpl.ROOT_DIRECTORY + File.separator + project + File.separator + FSProjectDAOImpl.LIB_DIRECTORY + File.separator - + lib.getFilePath()); + + lib); return file.toURI().toURL(); } @@ -496,20 +1153,22 @@ public class FSProjectDAOImpl implements IProjectDAO, ReleaseListener { */ @Override @PreAuthorize("hasAnyRole('User', 'Administrator')") - public boolean deleteLibrary(final String projectName, final String libName) { + public boolean deleteLibrary(final String projectName, final String libName) throws IOException { final File libDir = this.assembleLibDir(projectName); final File libFile = new File(libDir, libName); - return libFile.delete(); + final boolean result = libFile.delete(); + // Reload the available components + this.initializeAvailableComponents(projectName); + + return result; } - /* - * (non-Javadoc) + /** + * Delivers an URL pointing to the kieker library within this application. * - * @see kieker.webgui.persistence.IProjectDAO#getKiekerURL() + * @return The kieker url. */ - @Override - @PreAuthorize("isAuthenticated()") - public URL getKiekerURL() { + private URL getKiekerURL() { return Thread.currentThread().getContextClassLoader().getResource(FSProjectDAOImpl.KIEKER_LIB); } diff --git a/Kieker.WebGUI/src/main/java/kieker/webgui/service/impl/util/PluginFinder.java b/Kieker.WebGUI/src/main/java/kieker/webgui/persistence/impl/util/PluginFinder.java similarity index 99% rename from Kieker.WebGUI/src/main/java/kieker/webgui/service/impl/util/PluginFinder.java rename to Kieker.WebGUI/src/main/java/kieker/webgui/persistence/impl/util/PluginFinder.java index 14114b25f355339da753723e53282968bc6d6b26..77b1d731fc083ed72cf7c6e4c93d1080206a069e 100644 --- a/Kieker.WebGUI/src/main/java/kieker/webgui/service/impl/util/PluginFinder.java +++ b/Kieker.WebGUI/src/main/java/kieker/webgui/persistence/impl/util/PluginFinder.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. ***************************************************************************/ -package kieker.webgui.service.impl.util; +package kieker.webgui.persistence.impl.util; import java.io.IOException; import java.net.URL; 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 c346a35772eaa38ccfa999789e12bc1b1b65b5dd..5b441bd9f9f9f3af8920b110d59e838c9bf14bce 100644 --- a/Kieker.WebGUI/src/main/java/kieker/webgui/service/IProjectService.java +++ b/Kieker.WebGUI/src/main/java/kieker/webgui/service/IProjectService.java @@ -21,15 +21,12 @@ import java.util.Collection; import java.util.List; import kieker.analysis.AnalysisController.STATE; -import kieker.analysis.model.analysisMetaModel.MIDependency; import kieker.analysis.model.analysisMetaModel.MIProject; -import kieker.analysis.plugin.AbstractPlugin; -import kieker.analysis.repository.AbstractRepository; import kieker.webgui.common.ClassAndMethodContainer; +import kieker.webgui.common.ComponentListContainer; import kieker.webgui.common.exception.AnalysisInitializationException; import kieker.webgui.common.exception.AnalysisStateException; import kieker.webgui.common.exception.DisplayNotFoundException; -import kieker.webgui.common.exception.LibraryLoadException; import kieker.webgui.common.exception.NewerProjectException; import kieker.webgui.common.exception.ProjectAlreadyExistingException; import kieker.webgui.common.exception.ProjectNotExistingException; @@ -181,71 +178,13 @@ public interface IProjectService { public List<String> listAllLibraries(final String projectName) throws ProjectNotExistingException; /** - * This method delivers the available classes from the type {@link AbstractRepository} within the given dependency for the given project, using the given - * parameters. + * Delivers the available components (readers, filters and repositories) for the given project. * - * @param lib - * The library to be searched. * @param project - * The project for the given library. - * @param classLoader - * The class loader to be used. - * @param classAndMethodContainer - * The container with classes and methods to be used. - * @return A list with all repository-classes. - * @throws LibraryLoadException - * If something went wrong during the loading of the library. - * @throws IOException - */ - public List<Class<AbstractRepository>> getAllRepositoriesWithinLib(final MIDependency lib, final String project, final ClassLoader classLoader, - final ClassAndMethodContainer classAndMethodContainer) throws LibraryLoadException; - - /** - * This method delivers the available classes from the type {@link AbstractPlugin} within the given dependency for the given project, using the given - * parameters. - * - * @param lib - * The library to be searched. - * @param project - * The project for the given library. - * @param classLoader - * The class loader to be used. - * @param classAndMethodContainer - * The container with classes and methods to be used. - * @return A list with all plugin-classes. - * @throws LibraryLoadException - * If something went wrong during the loading of the library. + * The project whose components should be loaded. + * @return An object containing the available components as model instances. */ - public List<Class<AbstractPlugin>> getAllPluginsWithinLib(final MIDependency lib, final String project, final ClassLoader classLoader, - final ClassAndMethodContainer classAndMethodContainer) throws LibraryLoadException; - - /** - * This method delivers the available classes from the type {@link AbstractRepository} within the kieker dependency using the given parameters. - * - * @param classLoader - * The class loader to be used. - * @param classAndMethodContainer - * The container with classes and methods to be used. - * @return A list with all repository-classes. - * @throws LibraryLoadException - * If something went wrong during the loading of the library. - */ - public List<Class<AbstractRepository>> getAllRepositoriesWithinKiekerLib(final ClassLoader classLoader, - final ClassAndMethodContainer classAndMethodContainer) throws LibraryLoadException; - - /** - * This method delivers the available classes from the type {@link AbstractPlugin} within the kieker dependency using the given parameters. - * - * @param classLoader - * The class loader to be used. - * @param classAndMethodContainer - * The container with classes and methods to be used. - * @return A list with all plugin-classes. - * @throws LibraryLoadException - * If something went wrong during the loading of the library. - */ - public List<Class<AbstractPlugin>> getAllPluginsWithinKiekerLib(final ClassLoader classLoader, final ClassAndMethodContainer classAndMethodContainer) throws - LibraryLoadException; + public abstract ComponentListContainer getAvailableComponents(String project); /** * This method lists all available projects on the file system. @@ -353,6 +292,8 @@ public interface IProjectService { * @param libName * The name of the library. * @return true if and only if the given library has been removed. + * @throws IOException + * If something went wrong during the reloading of the components. */ - public boolean deleteLibrary(String projectName, String libName); + public boolean deleteLibrary(String projectName, String libName) throws IOException; } 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 0bde1b845332c63df4bb36a38e0df4237f7344e7..3ef5078671b796a9b2cf891d6f8489d3e9ee5c73 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 @@ -17,7 +17,6 @@ package kieker.webgui.service.impl; import java.io.IOException; -import java.net.MalformedURLException; import java.util.Collection; import java.util.List; import java.util.concurrent.ConcurrentHashMap; @@ -26,22 +25,18 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import kieker.analysis.AnalysisController.STATE; -import kieker.analysis.model.analysisMetaModel.MIDependency; import kieker.analysis.model.analysisMetaModel.MIProject; -import kieker.analysis.plugin.AbstractPlugin; -import kieker.analysis.repository.AbstractRepository; import kieker.webgui.common.ClassAndMethodContainer; +import kieker.webgui.common.ComponentListContainer; import kieker.webgui.common.exception.AnalysisInitializationException; import kieker.webgui.common.exception.AnalysisStateException; import kieker.webgui.common.exception.DisplayNotFoundException; -import kieker.webgui.common.exception.LibraryLoadException; import kieker.webgui.common.exception.NewerProjectException; import kieker.webgui.common.exception.ProjectAlreadyExistingException; import kieker.webgui.common.exception.ProjectNotExistingException; import kieker.webgui.persistence.IProjectDAO; import kieker.webgui.service.IProjectService; import kieker.webgui.service.impl.util.ACManager; -import kieker.webgui.service.impl.util.PluginFinder; import org.primefaces.model.UploadedFile; @@ -59,8 +54,6 @@ public final class ProjectServiceImpl implements IProjectService { private ACManager acManager; @Autowired private IProjectDAO projectDAO; - @Autowired - private PluginFinder pluginFinder; /** * Default constructor. <b>Do not use this constructor. This bean is Spring managed.</b> @@ -161,59 +154,11 @@ public final class ProjectServiceImpl implements IProjectService { } @Override - public List<Class<AbstractRepository>> getAllRepositoriesWithinLib(final MIDependency lib, final String projectName, final ClassLoader classLoader, - final ClassAndMethodContainer classAndMethodContainer) throws LibraryLoadException { - final Object projectLock = this.getLock(projectName, this.fileSystemLocks); - - synchronized (projectLock) { - try { - return this.pluginFinder.getAllRepositoriesWithinJar(this.projectDAO.getURL(lib, projectName), classLoader, classAndMethodContainer); - } catch (final MalformedURLException ex) { - throw new LibraryLoadException("An error occured while loading the library.", ex); - } catch (final IOException ex) { - throw new LibraryLoadException("An error occured while loading the library.", ex); - } - } - } - - @Override - public List<Class<AbstractPlugin>> getAllPluginsWithinLib(final MIDependency lib, final String projectName, final ClassLoader classLoader, - final ClassAndMethodContainer classAndMethodContainer) throws LibraryLoadException { + public ComponentListContainer getAvailableComponents(final String projectName) { final Object projectLock = this.getLock(projectName, this.fileSystemLocks); synchronized (projectLock) { - try { - return this.pluginFinder.getAllPluginsWithinJar(this.projectDAO.getURL(lib, projectName), classLoader, classAndMethodContainer); - } catch (final MalformedURLException ex) { - throw new LibraryLoadException("An error occured while loading the library.", ex); - } catch (final IOException ex) { - throw new LibraryLoadException("An error occured while loading the library.", ex); - } - } - } - - @Override - public List<Class<AbstractRepository>> getAllRepositoriesWithinKiekerLib(final ClassLoader classLoader, - final ClassAndMethodContainer classAndMethodContainer) throws LibraryLoadException { - try { - return this.pluginFinder.getAllRepositoriesWithinJar(this.projectDAO.getKiekerURL(), classLoader, classAndMethodContainer); - - } catch (final NullPointerException ex) { - throw new LibraryLoadException("An error occured while loading the library.", ex); - } catch (final IOException ex) { - throw new LibraryLoadException("An error occured while loading the library.", ex); - } - } - - @Override - public List<Class<AbstractPlugin>> getAllPluginsWithinKiekerLib(final ClassLoader classLoader, final ClassAndMethodContainer classAndMethodContainer) throws - LibraryLoadException { - try { - return this.pluginFinder.getAllPluginsWithinJar(this.projectDAO.getKiekerURL(), classLoader, classAndMethodContainer); - } catch (final NullPointerException ex) { - throw new LibraryLoadException("An error occured while loading the library.", ex); - } catch (final IOException ex) { - throw new LibraryLoadException("An error occured while loading the library.", ex); + return this.projectDAO.getAvailableComponents(projectName); } } @@ -331,7 +276,7 @@ public final class ProjectServiceImpl implements IProjectService { * @see kieker.webgui.service.IProjectService#deleteLibrary(java.lang.String) */ @Override - public boolean deleteLibrary(final String projectName, final String libName) { + public boolean deleteLibrary(final String projectName, final String libName) throws IOException { final Object projectLock = this.getLock(projectName, this.fileSystemLocks); synchronized (projectLock) { diff --git a/Kieker.WebGUI/src/main/java/kieker/webgui/web/beans/view/CurrentAnalysisEditorBean.java b/Kieker.WebGUI/src/main/java/kieker/webgui/web/beans/view/CurrentAnalysisEditorBean.java index 137e5b80576df4441abedba5b70b7bd2a8517b63..768fbc7cd0a65aade07762d2652fc720ff32683a 100644 --- a/Kieker.WebGUI/src/main/java/kieker/webgui/web/beans/view/CurrentAnalysisEditorBean.java +++ b/Kieker.WebGUI/src/main/java/kieker/webgui/web/beans/view/CurrentAnalysisEditorBean.java @@ -17,13 +17,8 @@ package kieker.webgui.web.beans.view; import java.io.IOException; -import java.lang.annotation.Annotation; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; -import java.util.Iterator; import java.util.List; import javax.faces.application.FacesMessage; @@ -36,25 +31,21 @@ import org.springframework.stereotype.Component; import kieker.analysis.model.analysisMetaModel.MIAnalysisMetaModelFactory; import kieker.analysis.model.analysisMetaModel.MIDependency; -import kieker.analysis.model.analysisMetaModel.MIDisplay; import kieker.analysis.model.analysisMetaModel.MIFilter; import kieker.analysis.model.analysisMetaModel.MIInputPort; import kieker.analysis.model.analysisMetaModel.MIOutputPort; import kieker.analysis.model.analysisMetaModel.MIPlugin; import kieker.analysis.model.analysisMetaModel.MIProject; -import kieker.analysis.model.analysisMetaModel.MIProperty; import kieker.analysis.model.analysisMetaModel.MIReader; import kieker.analysis.model.analysisMetaModel.MIRepository; import kieker.analysis.model.analysisMetaModel.MIRepositoryConnector; import kieker.analysis.model.analysisMetaModel.impl.MAnalysisMetaModelFactory; -import kieker.analysis.plugin.AbstractPlugin; -import kieker.analysis.plugin.annotation.Property; -import kieker.analysis.plugin.filter.AbstractFilterPlugin; -import kieker.analysis.plugin.reader.AbstractReaderPlugin; -import kieker.analysis.repository.AbstractRepository; import kieker.common.logging.Log; import kieker.common.logging.LogFactory; -import kieker.webgui.common.ClassAndMethodContainer; +import kieker.webgui.common.ComponentListContainer; +import kieker.webgui.common.IComponentContainer; +import kieker.webgui.common.PluginContainer; +import kieker.webgui.common.RepositoryContainer; import kieker.webgui.common.exception.LibraryLoadException; import kieker.webgui.common.exception.NewerProjectException; import kieker.webgui.common.exception.ProjectLoadException; @@ -68,7 +59,6 @@ import org.primefaces.context.RequestContext; import org.primefaces.event.FileUploadEvent; import org.primefaces.model.UploadedFile; -import org.eclipse.emf.common.util.EList; import org.eclipse.emf.ecore.EObject; /** @@ -76,10 +66,7 @@ import org.eclipse.emf.ecore.EObject; * project, as the analysis editor is the most important part of the whole application. The connection to the graph within the editor is done via another bean (the * {@link CurrentAnalysisEditorGraphBean}).<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.<br> - * If editing this class as a programmer keep in mind that the class makes excessive use of java reflection (via {@link ClassAndMethodContainer}) to use the correct - * classes within every project. It doesn't really use the classes loaded by the WebGUI-ClassLoader. This results also in some methods which returns elements from - * the type {@link Object} instead of more specific types to avoid incompatibilities between the multiple loaded classes. + * problems. * * @author Nils Christian Ehmke */ @@ -87,96 +74,34 @@ import org.eclipse.emf.ecore.EObject; @Scope("view") // TODO Remove libraries in a clean way // TODO Receive libraries once as MIDependencies instead of Strings -// TODO Use a possibility to reload the classloader explicitly - in this case the manager can close it directly in order to avoid too much memory use public final class CurrentAnalysisEditorBean { - /** - * This field contains the log-object for the whole class, which will be used to log all important errors and exceptions which occur. - */ + private static final Log LOG = LogFactory.getLog(CurrentAnalysisEditorBean.class); - /** - * This field contains the factory, which will be used to create the new components for the project. - */ - private final MIAnalysisMetaModelFactory factory = new MAnalysisMetaModelFactory(); - /** - * This field contains the actual model instance, representing the in-memory model of the current view. - */ + private static final MIAnalysisMetaModelFactory FACTORY = MAnalysisMetaModelFactory.eINSTANCE; + + private ComponentListContainer availableComponents; + private EObject selectedComponent; private MIProject project; - /** - * This is the corresponding class loader to the project. It contains always the libraries within the lib-folder of the project and additionaly the kieker - * library by default. - */ - private ClassLoader classLoader; - /** - * This component contains the classes and methods loaded with the specific project class loader. - */ - private ClassAndMethodContainer classAndMethodContainer; - /** - * This is the name of the stored project. It can be used as an identifier within the FS-Manager - */ private String projectName; - /** - * This is the time stamp of the moment, the project was loaded or last saved. It can be used to check whether the project has been modified externally in the - * meanwhile. - */ private long timeStamp; - /** - * This list contains the available repositories for the current project. Keep in mind: The list doesn't really contain classes from the type - * {@code AbstractRepository}, as the repositories are loaded with the specific project class loader. This is just possible as the generic-classes work - * internally with {@code Object}. - */ - private final List<Class<AbstractRepository>> availableRepositories = Collections.synchronizedList(new ArrayList<Class<AbstractRepository>>()); - /** - * This list contains the available filters for the current project. Keep in mind: The list doesn't really contain classes from the type - * {@code AbstractFilterPlugin}, as the repositories are loaded with the specific project class loader. This is just possible as the generic-classes work - * internally with {@code Object}. - */ - private final List<Class<AbstractFilterPlugin>> availableFilters = Collections.synchronizedList(new ArrayList<Class<AbstractFilterPlugin>>()); - /** - * This list contains the available readers for the current project. Keep in mind: The list doesn't really contain classes from the type - * {@code AbstractReaderPlugin}, as the repositories are loaded with the specific project class loader. This is just possible as the generic-classes work - * internally with {@code Object}. - */ - private final List<Class<AbstractReaderPlugin>> availableReaders = Collections.synchronizedList(new ArrayList<Class<AbstractReaderPlugin>>()); - /** - * This field contains the currently selected component (this can either be a plugin ({@link MIPlugin}) or a repository ({@link MIRepository})). - */ - private EObject selectedComponent; + + @Autowired + private CurrentAnalysisEditorGraphBean currentAnalysisEditorGraphBean; + @Autowired + private GlobalPropertiesBean globalPropertiesBean; @Autowired private IProjectService projectService; @Autowired private ProjectsBean projectsBean; @Autowired - private CurrentAnalysisEditorGraphBean currentAnalysisEditorGraphBean; - @Autowired private UserBean userBean; - @Autowired - private GlobalPropertiesBean globalPropertiesBean; /** * Creates a new instance of this class. <b>Do not call this constructor manually. It will only be accessed by Spring.</b> */ public CurrentAnalysisEditorBean() { - // No code necessary - } - - /** - * This method delivers the project stored in this bean. - * - * @return The project for this user. - */ - public synchronized MIProject getProject() { - return this.project; - } - - /** - * This method sets the project stored within this bean and returns the new page for the navigation - depending on the given values. - * - * @param newName - * The name of the project. - */ - public synchronized void setProjectName(final String newName) { - // Remember the given parameters - this.projectName = newName; + this.availableComponents = new ComponentListContainer(Collections.<PluginContainer>emptyList(), Collections.<PluginContainer>emptyList(), + Collections.<RepositoryContainer>emptyList()); } /** @@ -191,13 +116,10 @@ public final class CurrentAnalysisEditorBean { this.project = this.projectsBean.openProject(this.projectName); // 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(); // Add the libraries within the lib-folder to the current model this.initializeModelLibraries(); // Load the available readers, filters and repositories - this.initializeToolPalette(); + this.reloadAvailableComponents(); } } catch (final ProjectLoadException ex) { CurrentAnalysisEditorBean.LOG.error("An error occured while loading the project.", ex); @@ -209,115 +131,6 @@ public final class CurrentAnalysisEditorBean { } } - /** - * 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 synchronized void reloadClassesAndMethods() throws ProjectLoadException { - this.classAndMethodContainer = new ClassAndMethodContainer(this.classLoader); - } - - /** - * This method loads the list of available readers, filters and repositories, using the current libraries within the model. - */ - private synchronized void initializeToolPalette() { - // Clean our tool palette - this.availableFilters.clear(); - this.availableReaders.clear(); - this.availableRepositories.clear(); - - // Run through all libraries and add their content to our lists - final Iterator<MIDependency> dependencies = this.project.getDependencies().iterator(); - while (dependencies.hasNext()) { - final MIDependency lib = dependencies.next(); - try { - this.addContentsToToolPalette(lib); - } catch (final NullPointerException ex) { - dependencies.remove(); - } catch (final LibraryLoadException ex) { - CurrentAnalysisEditorBean.LOG.error("A library could not be loaded correctly.", ex); - } - } - try { - // Run also through the kieker library - this.addKiekerContentsToToolPalette(); - } catch (final LibraryLoadException ex) { - CurrentAnalysisEditorBean.LOG.error("A library could not be loaded correctly.", ex); - } - } - - /** - * This method adds the contents (plugins, repositories) within the given dependency to the tool palette. - * - * @param dependency - * The dependency which should be added to the palette. - * @throws LibraryLoadException - * If something went wrong during the loading of the library. - */ - private synchronized void addContentsToToolPalette(final MIDependency dependency) throws LibraryLoadException { - this.addContentsToToolPalette( - this.projectService.getAllPluginsWithinLib(dependency, this.projectName, this.classLoader, this.classAndMethodContainer), - this.projectService.getAllRepositoriesWithinLib(dependency, this.projectName, this.classLoader, this.classAndMethodContainer)); - } - - /** - * This method adds the contents (plugins, repositories) within the kieker dependency to the tool palette. - * - * @throws LibraryLoadException - * If something went wrong during the loading of the library. - */ - private synchronized void addKiekerContentsToToolPalette() throws LibraryLoadException { - this.addContentsToToolPalette( - this.projectService.getAllPluginsWithinKiekerLib(this.classLoader, this.classAndMethodContainer), - this.projectService.getAllRepositoriesWithinKiekerLib(this.classLoader, this.classAndMethodContainer)); - } - - /** - * This method adds all available readers, filters and repositories within the given parameters to the tool palette. - * - * @param plugins - * The available readers and filters. - * @param repositories - * The available repositories. - */ - @SuppressWarnings("unchecked") - private synchronized void addContentsToToolPalette(final List<Class<AbstractPlugin>> plugins, final List<Class<AbstractRepository>> repositories) { - // Now run through the available classes and add all non-abstract classes to our lists. - for (final Class<AbstractRepository> repository : repositories) { - if (!Modifier.isAbstract(repository.getModifiers())) { - // Make also sure that the current repository is not "programmaticOnly" - final Annotation annotationRepository = repository.getAnnotation(this.classAndMethodContainer.getRepositoryAnnotationClass()); - final boolean programmaticOnly = (Boolean) ClassAndMethodContainer.invokeMethod( - this.classAndMethodContainer.getRepositoryProgrammaticOnlyMethod(), - annotationRepository, false); - if (!programmaticOnly) { - this.availableRepositories.add(repository); - } - } - } - - for (final Class<?> plugin : plugins) { - // Make sure that the current plugin is neither abstract nor "programmaticOnly" - final Annotation annotationPlugin = plugin.getAnnotation(this.classAndMethodContainer.getPluginAnnotationClass()); - final boolean programmaticOnly = (Boolean) ClassAndMethodContainer.invokeMethod(this.classAndMethodContainer.getPluginProgrammaticOnlyMethod(), - annotationPlugin, false); - final boolean isAbstract = Modifier.isAbstract(plugin.getModifiers()); - - if (!isAbstract && !programmaticOnly) { - // The following cast results in the unchecked-cast-warnings, but we know that the cast should be correct. - if (this.classAndMethodContainer.getAbstractFilterPluginClass().isAssignableFrom(plugin)) { - this.availableFilters.add((Class<AbstractFilterPlugin>) plugin); - } else { - if (this.classAndMethodContainer.getAbstractReaderPluginClass().isAssignableFrom(plugin)) { - this.availableReaders.add((Class<AbstractReaderPlugin>) plugin); - } - } - } - } - } - /** * This method takes all libraries from the lib-folder and adds them to the in-memory-model. * @@ -328,11 +141,10 @@ public final class CurrentAnalysisEditorBean { try { final List<String> libs = this.projectService.listAllLibraries(this.projectName); // Add them, but remove all existing dependencies so far to avoid double entries. This also makes sure that the model - after it has been opened - - // points - // just to valid dependencies (and to all of them). + // points just to valid dependencies (and to all of them). this.project.getDependencies().clear(); for (final String lib : libs) { - final MIDependency dep = this.factory.createDependency(); + final MIDependency dep = CurrentAnalysisEditorBean.FACTORY.createDependency(); dep.setFilePath(lib); this.project.getDependencies().add(dep); } @@ -342,329 +154,130 @@ public final class CurrentAnalysisEditorBean { } /** - * 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. + * This method is the handler for the file upload. It tries to upload the given file and informs the user via the growl-component. * - * @throws ProjectLoadException - * If something went wrong. + * @param event + * The upload event. */ - private synchronized void reloadClassLoader() throws ProjectLoadException { + public synchronized void handleFileUpload(final FileUploadEvent event) { + // Get the file from the event + final UploadedFile file = event.getFile(); + try { - this.classLoader = this.projectService.getClassLoader(this.projectName, this); // NOPMD (ClassLoader) - } catch (final NullPointerException ex) { - throw new ProjectLoadException("Invalid class loader.", ex); + // Use the file system manager to upload the new file + final MIDependency lib; + this.projectService.uploadLibrary(file, this.projectName); + GlobalPropertiesBean.showMessage(FacesMessage.SEVERITY_INFO, this.globalPropertiesBean.getMsgLibraryUploaded()); + // As it seem to have worked, we can add the library to our model. + lib = CurrentAnalysisEditorBean.FACTORY.createDependency(); + lib.setFilePath(file.getFileName()); + this.project.getDependencies().add(lib); + // Update our class loader and the available plugins & repositories + + // We have to reinitialize the tool palette completely! This is necessary as some of the already existing classes could need the newly loaded classes. + this.reloadAvailableComponents(); } catch (final IOException ex) { - throw new ProjectLoadException("Could not load classes.", ex); + CurrentAnalysisEditorBean.LOG.error("An error occured while uploading the library.", ex); + GlobalPropertiesBean.showMessage(FacesMessage.SEVERITY_ERROR, this.globalPropertiesBean.getMsgLibraryUploadingException()); + } catch (final ProjectLoadException ex) { + CurrentAnalysisEditorBean.LOG.error("An error occured while uploading the library.", ex); + GlobalPropertiesBean.showMessage(FacesMessage.SEVERITY_ERROR, this.globalPropertiesBean.getMsgLibraryUploadingException()); } catch (final ProjectNotExistingException ex) { - throw new ProjectLoadException("Project does not exist.", ex); - } - } - - /** - * This method sets the time stamp to the current system time. - */ - public synchronized void resetTimeStamp() { - this.timeStamp = System.currentTimeMillis(); - } - - /** - * This method delivers the project name stored in this bean. - * - * @return The project name for this user. - */ - public synchronized String getProjectName() { - return this.projectName; - } - - /** - * This method delivers the current time stamp. - * - * @return The time stamp for this user. - */ - public synchronized long getTimeStamp() { - return this.timeStamp; - } - - /** - * This method can be used to get the description of an {@link AbstractPlugin}- or an {@link AbstractRepository}-class. The description is read via the - * annotation using the java reflection API. - * - * @param clazz - * The class whose description should be extracted. - * @return The description for the class or a substitute if none is available. This is in either case human readable. - */ - public synchronized String getDescription(final Class<?> clazz) { - final String description; - // Get the two potential annotations - final Annotation annotationPlugin = clazz.getAnnotation(this.classAndMethodContainer.getPluginAnnotationClass()); - final Annotation annotationRepository = clazz.getAnnotation(this.classAndMethodContainer.getRepositoryAnnotationClass()); - - final Method pluginDescrMethod = this.classAndMethodContainer.getPluginDescriptionMethod(); - final Method repoDescrMethod = this.classAndMethodContainer.getRepositoryDescriptionMethod(); - - // Now check which one of them is available - if ((annotationPlugin == null) || ((String) ClassAndMethodContainer.invokeMethod(pluginDescrMethod, annotationPlugin, "")).isEmpty()) { - if ((annotationRepository == null) || ((String) ClassAndMethodContainer.invokeMethod(repoDescrMethod, annotationRepository, "")).isEmpty()) { - // None. Deliver a human readable substitute. - description = "No description available"; - } else { - description = (String) ClassAndMethodContainer.invokeMethod(repoDescrMethod, annotationRepository, "No description available"); - } - } else { - description = (String) ClassAndMethodContainer.invokeMethod(pluginDescrMethod, annotationPlugin, "No description available"); + CurrentAnalysisEditorBean.LOG.error("Project does not exist.", ex); + GlobalPropertiesBean.showMessage(FacesMessage.SEVERITY_ERROR, this.globalPropertiesBean.getMsgProjectNotExistingException()); + } catch (final LibraryLoadException ex) { + CurrentAnalysisEditorBean.LOG.error("An error occured while uploading the library.", ex); + GlobalPropertiesBean.showMessage(FacesMessage.SEVERITY_ERROR, this.globalPropertiesBean.getMsgLibraryUploadingException()); } - - return description; } /** - * Delivers the content of the dependencies-field of the given component (plugin or repository). + * Removes the library with the given name and reloads the classes and the classloader. Furthermore the tool palette will be reinitalized. * - * @param clazz - * The class whose dependencies should be extracted. - * @return The dependencies for the class or a substitute if none is available. This is in either case human readable. + * @param name + * The name of the library to be removed. */ - public synchronized String getDependencies(final Class<?> clazz) { - final String description; - - // Get the two potential annotations - final Annotation annotationPlugin = clazz.getAnnotation(this.classAndMethodContainer.getPluginAnnotationClass()); - final Annotation annotationRepository = clazz.getAnnotation(this.classAndMethodContainer.getRepositoryAnnotationClass()); - - final Method pluginDepMethod = this.classAndMethodContainer.getPluginDependenciesMethod(); - final Method repoDepMethod = this.classAndMethodContainer.getRepositoryDependenciesMethod(); - - // Now check which one of them is available - if (annotationPlugin == null) { - if (annotationRepository == null) { - // None. Deliver a human readable substitute. - description = "No dependency information available"; - } else { - description = (String) ClassAndMethodContainer.invokeMethod(repoDepMethod, annotationRepository, "No dependency information available"); + public synchronized void deleteLibrary(final String name) { + try { + if (this.projectService.deleteLibrary(this.projectName, name)) { + // We have to reinitialize the tool palette completely! This is necessary as some of the already existing classes could need the newly loaded + // classes. + this.reloadAvailableComponents(); } - } else { - description = (String) ClassAndMethodContainer.invokeMethod(pluginDepMethod, annotationPlugin, "No dependency information available"); + } catch (final IOException ex) { + CurrentAnalysisEditorBean.LOG.error("An error occured while removing the library.", ex); + GlobalPropertiesBean.showMessage(FacesMessage.SEVERITY_ERROR, "An error occured while removing the library."); } - - return description; } /** - * Delivers the description of the property of the given component (plugin or repository). - * - * @param component - * The component whose property description should be delivered. - * @param propertyName - * The name of the property in question. - * @return The description of the property. + * This method loads the list of available readers, filters and repositories, using the current libraries within the model. */ - public synchronized String getDescription(final EObject component, final String propertyName) { - try { - final String className; - if (component instanceof MIPlugin) { - className = ((MIPlugin) component).getClassname(); - } else { - if (component instanceof MIRepository) { - className = ((MIRepository) component).getClassname(); - } else { - // Unknown type - return "N/A"; - } - } - final Class<?> clazz = this.classLoader.loadClass(className); - final List<Annotation> properties = this.getProperties(clazz); - // Run through all the properties and find the correct one - for (final Annotation property : properties) { - final String name = (String) ClassAndMethodContainer.invokeMethod(this.classAndMethodContainer.getPropertyNameMethod(), property, ""); - if (propertyName.equals(name)) { - return (String) ClassAndMethodContainer.invokeMethod(this.classAndMethodContainer.getPropertyDescriptionMethod(), property, ""); - } - } - } catch (final ClassNotFoundException ex) { - CurrentAnalysisEditorBean.LOG.warn("Could not find class.", ex); - } - // If we are here we found nothing - return "N/A"; + private synchronized void reloadAvailableComponents() { + this.availableComponents = this.projectService.getAvailableComponents(this.projectName); } /** - * Delivers the properties of the given class, using the annotations and the java reflection API. + * This method delivers the project stored in this bean. * - * @param clazz - * The class whose properties will be delivered. - * @return A list of properties. It contains instances of {@link Property} - but those from the project class loader. + * @return The project for this user. */ - public synchronized List<Annotation> getProperties(final Class<?> clazz) { - // Get the two potential annotations - final Annotation annotationPlugin = clazz.getAnnotation(this.classAndMethodContainer.getPluginAnnotationClass()); - final Annotation annotationRepository = clazz.getAnnotation(this.classAndMethodContainer.getRepositoryAnnotationClass()); - - final Annotation[] properties; - - // Now check which one of them is available - if (annotationPlugin == null) { - if (annotationRepository == null) { - // None. - properties = new Property[0]; - } else { - properties = (Annotation[]) ClassAndMethodContainer.invokeMethod(this.classAndMethodContainer.getRepositoryConfigurationMethod(), - annotationRepository, new Property[0]); - } - } else { - properties = (Annotation[]) ClassAndMethodContainer.invokeMethod(this.classAndMethodContainer.getPluginConfigurationMethod(), - annotationPlugin, new Property[0]); - } - - return Arrays.asList(properties); + public synchronized MIProject getProject() { + return this.project; } /** - * Searches for input ports within the given class and returns them. + * This method sets the project stored within this bean and returns the new page for the navigation - depending on the given values. * - * @param clazz - * The class to be analyzed. - * @return A list containing the available input ports. + * @param newName + * The name of the project. */ - public synchronized List<Annotation> getInputPorts(final Class<?> clazz) { - final List<Annotation> result = new ArrayList<Annotation>(); - - for (final Method method : clazz.getMethods()) { - // Get the potential annotation - final Annotation annotationPort = method.getAnnotation(this.classAndMethodContainer.getInputPortAnnotationClass()); - // Now check whether it is available - if (annotationPort != null) { - result.add(annotationPort); - } - } - - return result; + public synchronized void setProjectName(final String newName) { + // Remember the given parameters + this.projectName = newName; } /** - * Searches for output ports within the given class and returns them. + * This method delivers the project name stored in this bean. * - * @param clazz - * The class to be analyzed. - * @return A list containing the available output ports. + * @return The project name for this user. */ - public synchronized List<Annotation> getOutputPorts(final Class<?> clazz) { - final List<Annotation> result = new ArrayList<Annotation>(); - - // Get the potential annotation - final Annotation annotationPlugin = clazz.getAnnotation(this.classAndMethodContainer.getPluginAnnotationClass()); - - // Now check whether it is available - if (annotationPlugin != null) { - for (final Annotation oPort : (Annotation[]) ClassAndMethodContainer.invokeMethod(this.classAndMethodContainer.getPluginOutputPortsMethod(), - annotationPlugin, new Annotation[0])) { - result.add(oPort); - } - } - - return result; + public synchronized String getProjectName() { + return this.projectName; } /** - * Searches for repository ports within the given class and returns them. + * This method delivers the available components for the current project. The delivered components are never abstract or programmaticOnly. * - * @param clazz - * The class to be analyzed. - * @return A list containing the available repository ports. + * @return A list with all components. */ - public synchronized List<Annotation> getRepositoryPorts(final Class<?> clazz) { - final List<Annotation> result = new ArrayList<Annotation>(); - - // Get the potential annotation - final Annotation annotationPlugin = clazz.getAnnotation(this.classAndMethodContainer.getPluginAnnotationClass()); - - // Now check whether it is available - if (annotationPlugin != null) { - for (final Annotation rPort : (Annotation[]) ClassAndMethodContainer.invokeMethod(this.classAndMethodContainer.getPluginRepositoryPortsMethod(), - annotationPlugin, new Annotation[0])) { - result.add(rPort); - } - } - - return result; + public synchronized ComponentListContainer getAvailableComponents() { + return this.availableComponents; } /** - * Searches for displays within the given class and returns them. - * - * @param clazz - * The class to be analyzed. - * @return A list containing the available displays. + * This method sets the time stamp to the current system time. */ - public synchronized List<Annotation> getDisplays(final Class<?> clazz) { - final List<Annotation> result = new ArrayList<Annotation>(); - - for (final Method method : clazz.getMethods()) { - // Get the potential annotation - final Annotation display = method.getAnnotation(this.classAndMethodContainer.getDisplayAnnotationClass()); - // Now check whether it is available - if (display != null) { - result.add(display); - } - } - - return result; + public synchronized void resetTimeStamp() { + this.timeStamp = System.currentTimeMillis(); } /** - * This method is the handler for the file upload. It tries to upload the given file and informs the user via the growl-component. + * This method delivers the current time stamp. * - * @param event - * The upload event. + * @return The time stamp for this user. */ - public synchronized void handleFileUpload(final FileUploadEvent event) { - // Get the file from the event - final UploadedFile file = event.getFile(); - - try { - // Use the file system manager to upload the new file - final MIDependency lib; - this.projectService.uploadLibrary(file, this.projectName); - GlobalPropertiesBean.showMessage(FacesMessage.SEVERITY_INFO, this.globalPropertiesBean.getMsgLibraryUploaded()); - // As it seem to have worked, we can add the library to our model. - lib = this.factory.createDependency(); - lib.setFilePath(file.getFileName()); - this.project.getDependencies().add(lib); - // Update our class loader and the available plugins & repositories - this.reloadClassLoader(); - this.reloadClassesAndMethods(); - - // We have to reinitialize the tool palette completely! This is necessary as some of the already existing classes could need the newly loaded classes. - this.initializeToolPalette(); - } catch (final IOException ex) { - CurrentAnalysisEditorBean.LOG.error("An error occured while uploading the library.", ex); - GlobalPropertiesBean.showMessage(FacesMessage.SEVERITY_ERROR, this.globalPropertiesBean.getMsgLibraryUploadingException()); - } catch (final ProjectLoadException ex) { - CurrentAnalysisEditorBean.LOG.error("An error occured while uploading the library.", ex); - GlobalPropertiesBean.showMessage(FacesMessage.SEVERITY_ERROR, this.globalPropertiesBean.getMsgLibraryUploadingException()); - } catch (final ProjectNotExistingException ex) { - CurrentAnalysisEditorBean.LOG.error("Project does not exist.", ex); - GlobalPropertiesBean.showMessage(FacesMessage.SEVERITY_ERROR, this.globalPropertiesBean.getMsgProjectNotExistingException()); - } catch (final LibraryLoadException ex) { - CurrentAnalysisEditorBean.LOG.error("An error occured while uploading the library.", ex); - GlobalPropertiesBean.showMessage(FacesMessage.SEVERITY_ERROR, this.globalPropertiesBean.getMsgLibraryUploadingException()); - } + public synchronized long getTimeStamp() { + return this.timeStamp; } /** - * Removes the library with the given name and reloads the classes and the classloader. Furthermore the tool palette will be reinitalized. + * This method delivers the currently selected plugin (or the currently selected repository). If nothing has been selected, null will be returned. * - * @param name - * The name of the library to be removed. + * @return The currently selected plugin or repository. */ - public synchronized void deleteLibrary(final String name) { - if (this.projectService.deleteLibrary(this.projectName, name)) { - // Update our class loader and the available plugins & repositories - this.reloadClassLoader(); - this.reloadClassesAndMethods(); - - // We have to reinitialize the tool palette completely! This is necessary as some of the already existing classes could need the newly loaded classes. - this.initializeToolPalette(); - } + public synchronized EObject getSelectedPlugin() { + return this.selectedComponent; } /** @@ -688,33 +301,6 @@ public final class CurrentAnalysisEditorBean { return new ArrayList<String>(); } - /** - * This method delivers the available reader-plugins for the current main project. The delivered plugins are never abstract. - * - * @return A list with all readers. - */ - public synchronized List<Class<AbstractReaderPlugin>> getAvailableReaders() { - return this.availableReaders; - } - - /** - * This method delivers the available filter-plugins for the current main project. The delivered plugins are never abstract. - * - * @return A list with all filter. - */ - public synchronized List<Class<AbstractFilterPlugin>> getAvailableFilters() { - return this.availableFilters; - } - - /** - * This method delivers the available repositories for the current main project. The delivered repositories are never abstract. - * - * @return A list with all repositories. - */ - public synchronized List<Class<AbstractRepository>> getAvailableRepositories() { - return this.availableRepositories; - } - /** * This method tries to save the current project and informs the user about success or fail. * @@ -742,163 +328,39 @@ public final class CurrentAnalysisEditorBean { } /** - * This method fills the displays of the given plugin. In other words: It tries to extract the list of displays from the given class (using the annotations) and - * to convert them into model instances. - * - * @param clazz - * The class to be used as a base. - * @param plugin - * The plugin to be filled. - */ - private synchronized void fillDisplays(final Class<AbstractPlugin> clazz, final MIPlugin plugin) { - // Get the displays and convert them into model instances - final List<Annotation> displays = this.getDisplays(clazz); - for (final Annotation display : displays) { - final MIDisplay mDisplay = this.factory.createDisplay(); - mDisplay.setName((String) ClassAndMethodContainer.invokeMethod(this.classAndMethodContainer.getDisplayNameMethod(), display, "N/A")); - plugin.getDisplays().add(mDisplay); - } - } - - /** - * This method fills the ports of the given plugin. In other words: It tries to extract the list of ports from the given class (using the annotations) and - * to convert them into model instances. + * This method adds a new repository to the current model, using the given instance of {@code RepositoryContainer} for it. * - * @param clazz - * The class to be used as a base. - * @param plugin - * The plugin to be filled. + * @param container + * The container which delivers the copy of the repository. */ - private synchronized void fillPorts(final Class<AbstractPlugin> clazz, final MIPlugin plugin) { - // Get the ports - final List<Annotation> inputPorts = this.getInputPorts(clazz); - final List<Annotation> outputPorts = this.getOutputPorts(clazz); - final List<Annotation> repositoryPorts = this.getRepositoryPorts(clazz); - - // Add input ports - if (plugin instanceof MIFilter) { - for (final Annotation inputPort : inputPorts) { - final MIInputPort mInputPort = this.factory.createInputPort(); - mInputPort.setName((String) ClassAndMethodContainer.invokeMethod(this.classAndMethodContainer.getInputPortNameMethod(), inputPort, "N/A")); - mInputPort.setParent((MIFilter) plugin); - } - } + public synchronized void addRepository(final RepositoryContainer container) { + // Create a new instance for the model + final MIRepository repository = container.newInstance(CurrentAnalysisEditorBean.FACTORY); - // Add output ports. - for (final Annotation outputPort : outputPorts) { - final MIOutputPort mOutputPort = this.factory.createOutputPort(); - mOutputPort.setName((String) ClassAndMethodContainer.invokeMethod(this.classAndMethodContainer.getOutputPortNameMethod(), outputPort, "N/A")); - mOutputPort.setParent(plugin); - } - - // Add repository ports. - for (final Annotation repositoryPort : repositoryPorts) { - final MIRepositoryConnector mConnector = this.factory.createRepositoryConnector(); - mConnector.setName((String) ClassAndMethodContainer.invokeMethod(this.classAndMethodContainer.getRepositoryPortNameMethod(), repositoryPort, - "N/A")); - plugin.getRepositories().add(mConnector); - } - } - - /** - * This method fills the properties of the given repository or plugin. In other words: It tries to extract the list of properties from the given class (using the - * annotations) and to convert them into model instances. - * - * @param clazz - * The class to be used as a base. - * @param component - * The component to be filled. - */ - private synchronized void fillProperties(final Class<?> clazz, final EObject component) { - // Get the default configuration and use it to initialize the model repository - final List<Annotation> properties = this.getProperties(clazz); - - for (final Annotation property : properties) { - final MIProperty mProperty = this.factory.createProperty(); - - mProperty.setName((String) ClassAndMethodContainer.invokeMethod(this.classAndMethodContainer.getPropertyNameMethod(), property, "N/A")); - mProperty.setValue((String) ClassAndMethodContainer.invokeMethod(this.classAndMethodContainer.getPropertyDefaultValueMethod(), property, "N/A")); - - if (component instanceof MIPlugin) { - ((MIPlugin) component).getProperties().add(mProperty); - } else { - ((MIRepository) component).getProperties().add(mProperty); - } - } - - } - - /** - * This method adds a new repository to the current model, using the given instance of {@code Class} for it. - * - * @param clazz - * The class of the repository to be added. - */ - public synchronized void addRepository(final Class<AbstractRepository> clazz) { - try { - // Create a new instance for the model - final MIRepository repository = this.factory.createRepository(); - repository.setClassname(clazz.getName()); - repository.setName(clazz.getSimpleName()); - - this.fillProperties(clazz, repository); - // Add it to the project - and to the graph - this.project.getRepositories().add(repository); - this.currentAnalysisEditorGraphBean.addRepository(repository); - this.currentAnalysisEditorGraphBean.refreshGraph(); - } catch (final NoClassDefFoundError ex) { - // This exception can occur if (for example) a class is missing - GlobalPropertiesBean.showMessage(FacesMessage.SEVERITY_ERROR, this.globalPropertiesBean.getMsgRepositoryCreationException()); - CurrentAnalysisEditorBean.LOG.error("An error occured during the creation of the repository. Check the dependencies.", ex); - } + // Add it to the project - and to the graph + this.project.getRepositories().add(repository); + this.currentAnalysisEditorGraphBean.addRepository(repository); + this.currentAnalysisEditorGraphBean.refreshGraph(); } /** - * This method adds a new plugin to the current model, using the given instance of {@code Class} for it. + * This method adds a new plugin to the current model, using the given instance of {@code PluginContainer} for it. * - * @param clazz - * The class of the plugin to be added. This can be both, a filter or a reader. + * @param container + * The container which delivers the copy of the plugin. */ - public synchronized void addPlugin(final Class<AbstractPlugin> clazz) { - try { - // Create a new instance for the model - final MIPlugin plugin; - if (this.classAndMethodContainer.getAbstractReaderPluginClass().isAssignableFrom(clazz)) { - plugin = this.factory.createReader(); - } else { - plugin = this.factory.createFilter(); - } - plugin.setClassname(clazz.getName()); - plugin.setName(clazz.getSimpleName()); - - this.fillProperties(clazz, plugin); - this.fillPorts(clazz, plugin); - this.fillDisplays(clazz, plugin); + public synchronized void addPlugin(final PluginContainer container) { + // Create a new instance for the model + final MIPlugin plugin = container.newInstance(CurrentAnalysisEditorBean.FACTORY); - // Add it to the project - this.project.getPlugins().add(plugin); - - // Add the element to the graph - if (plugin instanceof MIReader) { - this.currentAnalysisEditorGraphBean.addReader((MIReader) plugin); - } else { - this.currentAnalysisEditorGraphBean.addFilter((MIFilter) plugin); - } - this.currentAnalysisEditorGraphBean.refreshGraph(); - } catch (final NoClassDefFoundError ex) { - // This exception can occur if (for example) a class is missing - GlobalPropertiesBean.showMessage(FacesMessage.SEVERITY_ERROR, this.globalPropertiesBean.getMsgPluginCreationException()); - CurrentAnalysisEditorBean.LOG.error("An error occured during the creation of the plugin. Check the dependencies.", ex); + // Add it to the project - and to the graph + this.project.getPlugins().add(plugin); + if (plugin instanceof MIReader) { + this.currentAnalysisEditorGraphBean.addReader((MIReader) plugin); + } else { + this.currentAnalysisEditorGraphBean.addFilter((MIFilter) plugin); } - } - - /** - * This method delivers the currently selected plugin (or the currently selected repository). If nothing has been selected, null will be returned. - * - * @return The currently selected plugin or repository. - */ - public synchronized EObject getSelectedPlugin() { - return this.selectedComponent; + this.currentAnalysisEditorGraphBean.refreshGraph(); } /** @@ -926,48 +388,51 @@ public final class CurrentAnalysisEditorBean { } /** - * This method delivers a list containing all available filters within the model instance. It is the same as calling project.getPlugins() but instead this method - * returns only instances of {@link MIFilter}. + * Delivers a human readable description of the given property. * - * @return A list with the available filters. - */ - public synchronized List<MIPlugin> getFilters() { - final EList<MIPlugin> plugins = this.project.getPlugins(); - final List<MIPlugin> filter = new ArrayList<MIPlugin>(); - - for (final MIPlugin plugin : plugins) { - if (plugin instanceof MIFilter) { - filter.add(plugin); + * @param component + * The parent of the property. + * @param property + * The property name. + * @return A human readable description and a substitution if there is no description. + */ + public synchronized String getDescription(final EObject component, final String property) { + IComponentContainer container = null; + + // Find the container which contains the component + if (component instanceof MIReader) { + final String className = ((MIReader) component).getClassname(); + for (final PluginContainer reader : this.availableComponents.getReaders()) { + if (reader.getPlugin().getClassname().equals(className)) { + container = reader; + break; + } } - } - - return filter; - } - - /** - * Delivers all available repositories. - * - * @return A list with all repositories within the model. - */ - public synchronized EList<MIRepository> getRepositories() { - return this.project.getRepositories(); - } - - /** - * Delivers the name of the currently selected plugin/repository. This is only necessary for the correct renaming of the components. - * - * @return The name of the plugin/repository. - */ - public synchronized String getCurrentPluginName() { - if (this.selectedComponent != null) { - if (this.selectedComponent instanceof MIPlugin) { - return ((MIPlugin) this.selectedComponent).getName(); - } else { - return ((MIRepository) this.selectedComponent).getName(); + } else if (component instanceof MIFilter) { + final String className = ((MIFilter) component).getClassname(); + for (final PluginContainer filter : this.availableComponents.getFilters()) { + if (filter.getPlugin().getClassname().equals(className)) { + container = filter; + break; + } } } else { - return ""; + final String className = ((MIRepository) component).getClassname(); + for (final RepositoryContainer repository : this.availableComponents.getRepositories()) { + if (repository.getRepository().getClassname().equals(className)) { + container = repository; + break; + } + } } + + // Extract the description + if (container != null) { + return container.getPropertyDescription(property); + } + + // Nothing found + return "No description available."; } /** @@ -1091,7 +556,7 @@ public final class CurrentAnalysisEditorBean { } } - // Unselect the currently selected node if it is the one which has just been removed + // Deselect the currently selected node if it is the one which has just been removed if (this.selectedComponent == node) { this.selectedComponent = null; // NOPMD } 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 7fbcc0684f8018f0e15fa1d53568771b5851f530..d16f63c374f6519bb48d86d3cd140b8bcfeaca86 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 @@ -16,7 +16,6 @@ package kieker.webgui.web.beans.view; -import java.io.IOException; import java.util.List; import javax.faces.application.Application; @@ -179,17 +178,12 @@ public final class CurrentCockpitBean { this.project = this.projectsBean.openProject(this.projectName); if (this.project != null) { - final ClassLoader classLoader = this.projectService.getClassLoader(this.projectName, this); // NOPMD (ClassLoader) - this.classAndMethodContainer = new ClassAndMethodContainer(classLoader); this.fillDashboard(); } } } catch (final ProjectNotExistingException ex) { CurrentCockpitBean.LOG.error("An error occured while loading the project.", ex); GlobalPropertiesBean.showMessage(FacesMessage.SEVERITY_ERROR, "An error occured while loading the project."); - } catch (final IOException ex) { - CurrentCockpitBean.LOG.error("An error occured while loading the project.", ex); - GlobalPropertiesBean.showMessage(FacesMessage.SEVERITY_ERROR, "An error occured while loading the project."); } catch (final NullPointerException ex) { // This exception occurs, when the projectsBean has not been initialized CurrentCockpitBean.LOG.error("An error occured while loading the project.", ex); 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 41a019cc0d434b5d7c64326170389dffa5104491..ae1e9aa34dcf6de4842456af73e1420d0dc1ed0b 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 @@ -17,9 +17,7 @@ package kieker.webgui.web.beans.view; import java.io.IOException; -import java.lang.annotation.Annotation; -import java.lang.reflect.Method; -import java.util.ArrayList; +import java.util.Collections; import java.util.List; import javax.faces.application.Application; @@ -33,17 +31,17 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; -import kieker.analysis.display.annotation.Display; import kieker.analysis.model.analysisMetaModel.MIAnalysisMetaModelFactory; import kieker.analysis.model.analysisMetaModel.MIDisplay; import kieker.analysis.model.analysisMetaModel.MIDisplayConnector; import kieker.analysis.model.analysisMetaModel.MIProject; import kieker.analysis.model.analysisMetaModel.MIView; import kieker.analysis.model.analysisMetaModel.impl.MAnalysisMetaModelFactory; -import kieker.analysis.plugin.AbstractPlugin; import kieker.common.logging.Log; import kieker.common.logging.LogFactory; -import kieker.webgui.common.ClassAndMethodContainer; +import kieker.webgui.common.ComponentListContainer; +import kieker.webgui.common.PluginContainer; +import kieker.webgui.common.RepositoryContainer; import kieker.webgui.common.exception.NewerProjectException; import kieker.webgui.common.exception.ProjectLoadException; import kieker.webgui.common.exception.ProjectNotExistingException; @@ -77,14 +75,13 @@ public final class CurrentCockpitEditorBean { private final MIAnalysisMetaModelFactory factory = new MAnalysisMetaModelFactory(); @Autowired private IProjectService projectService; - private ClassAndMethodContainer classAndMethodContainer; + private ComponentListContainer availableComponents; private long currId; private long timeStamp; private String projectName; private MIProject project; private MIView activeView; - private ClassLoader classLoader; private Dashboard dashboard; private DashboardModel dashboardModel; @Autowired @@ -96,6 +93,8 @@ public final class CurrentCockpitEditorBean { * Creates a new instance of this class. <b>Do not call this constructor manually. It will only be accessed by Spring.</b> */ public CurrentCockpitEditorBean() { + this.availableComponents = new ComponentListContainer(Collections.<PluginContainer>emptyList(), Collections.<PluginContainer>emptyList(), + Collections.<RepositoryContainer>emptyList()); this.createDashboard(); } @@ -180,9 +179,8 @@ public final class CurrentCockpitEditorBean { if (this.project != null) { // Remember the current time! This is important for the later comparison of the time stamps. this.resetTimeStamp(); + this.reloadComponents(); // Update the class loader and the specific classes used within various methods in this bean - this.reloadClassLoader(); - this.reloadClassesAndMethods(); this.fillDashboard(); } } @@ -194,32 +192,10 @@ public final 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. + * Reloads the available components within this bean. */ - private synchronized void reloadClassesAndMethods() throws ProjectLoadException { - 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 synchronized void reloadClassLoader() throws ProjectLoadException { - try { - this.classLoader = this.projectService.getClassLoader(this.projectName, this); // 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); - } + private synchronized void reloadComponents() { + this.availableComponents = this.projectService.getAvailableComponents(this.projectName); } /** @@ -251,35 +227,38 @@ public final 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. + * This method can be used to get the description of a {@link MIDisplay}. Currently it is a little bit expensive to search for the description. * * @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 synchronized 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"); - } + final String parentClassname = display.getParent().getClassname(); + PluginContainer parentContainer = null; + + // Find the correct parent container + for (final PluginContainer plugin : this.availableComponents.getFilters()) { + if (plugin.getPlugin().getClassname().equals(parentClassname)) { + parentContainer = plugin; + break; + } + } + if (parentContainer == null) { + for (final PluginContainer plugin : this.availableComponents.getReaders()) { + if (plugin.getPlugin().getClassname().equals(parentClassname)) { + parentContainer = plugin; + break; } } - } catch (final ClassNotFoundException ex) { - CurrentCockpitEditorBean.LOG.warn("Class not found.", ex); } + + // If we have now the correct parent, we can search for the correct display instance. + if (parentContainer != null) { + return parentContainer.getDisplayDescription(display.getName()); + } + + // Nothing found return "No description available"; } @@ -316,31 +295,6 @@ public final class CurrentCockpitEditorBean { this.timeStamp = System.currentTimeMillis(); } - /** - * This method delivers the available displays of the given plugin. - * - * @param clazz - * The plugin whose displays should be delivered. - * @return The available displays. - */ - public synchronized List<Display> getDisplays(final Class<AbstractPlugin> clazz) { - final List<Display> result = new ArrayList<Display>(); - - if (clazz != null) { - // Run through all available methods and find the annotated ones - final Method[] methods = clazz.getMethods(); - for (final Method method : methods) { - final Display displayAnnot = method.getAnnotation(Display.class); - // Check whether the annotation is available. If yes - add it to our result list - if (displayAnnot != null) { - result.add(displayAnnot); - } - } - } - - return result; - } - /** * This method adds a new view to the project. * @@ -487,4 +441,13 @@ public final class CurrentCockpitEditorBean { this.dashboard.setModel(this.dashboardModel); } + /** + * Delivers the available components. + * + * @return A list with the available components. + */ + public synchronized ComponentListContainer getAvailableComponents() { + return this.availableComponents; + } + } diff --git a/Kieker.WebGUI/src/main/java/kieker/webgui/web/beans/view/CurrentUserManagementBean.java b/Kieker.WebGUI/src/main/java/kieker/webgui/web/beans/view/CurrentUserManagementBean.java index 1af38a827385a663b5a749cef05025dbd83f4af8..bc2be83f402a3f720ae55534c3e5c13fa90972d0 100644 --- a/Kieker.WebGUI/src/main/java/kieker/webgui/web/beans/view/CurrentUserManagementBean.java +++ b/Kieker.WebGUI/src/main/java/kieker/webgui/web/beans/view/CurrentUserManagementBean.java @@ -189,7 +189,7 @@ public final class CurrentUserManagementBean { * * @return The current value of the property. */ - public User getSelectedUser() { + public synchronized User getSelectedUser() { return this.selectedUser; } @@ -198,7 +198,7 @@ public final class CurrentUserManagementBean { * * @return The current value of the property. */ - public User getSelectedUserCopy() { + public synchronized User getSelectedUserCopy() { return this.selectedUserCopy; } @@ -208,7 +208,7 @@ public final class CurrentUserManagementBean { * @param selectedUser * The new value of the property. */ - public void setSelectedUser(final User selectedUser) { + public synchronized void setSelectedUser(final User selectedUser) { // We remember the selected user, but we make also a copy. This is necessary, because otherwise we would have to do something like a rollback, if for example // an edit within the DB fails. this.selectedUser = selectedUser; diff --git a/Kieker.WebGUI/src/main/webapp/pages/AnalysisEditorPage.xhtml b/Kieker.WebGUI/src/main/webapp/pages/AnalysisEditorPage.xhtml index b7425888d8ecf41bd7a500631703bda11e00d2ef..063bbab76c3c640e9dacd484b3724130f8476364 100644 --- a/Kieker.WebGUI/src/main/webapp/pages/AnalysisEditorPage.xhtml +++ b/Kieker.WebGUI/src/main/webapp/pages/AnalysisEditorPage.xhtml @@ -159,97 +159,97 @@ <h:form id="toolpalette"> <p:accordionPanel multiple="true" activeIndex="0,1,2"> <p:tab title="#{localizedAnalysisEditorPageMessages.reader}"> - <ui:repeat value="#{currentAnalysisEditorBean.availableReaders}" var="reader"> - <p:commandLink id="readerLink" value="#{reader.simpleName}" action="#{currentAnalysisEditorBean.addPlugin(reader)}" update=":messages" /><br/> + <ui:repeat value="#{currentAnalysisEditorBean.availableComponents.readers}" var="reader"> + <p:commandLink id="readerLink" value="#{reader.plugin.name}" action="#{currentAnalysisEditorBean.addPlugin(reader)}" update=":messages" disabled="#{not reader.fullyInitialized}" /><br/> <p:tooltip for="readerLink"> - <b><h:outputText value="#{reader.simpleName} (#{reader.name})"/></b> + <b><h:outputText value="#{reader.plugin.name} (#{reader.plugin.classname})"/></b> <br/> - <h:outputText value="#{currentAnalysisEditorBean.getDescription(reader)}"/> + <h:outputText value="#{reader.description}"/> <br/><br/> - <ui:fragment rendered="#{not empty currentAnalysisEditorBean.getOutputPorts(reader)}"> + <ui:fragment rendered="#{not empty reader.plugin.outputPorts}"> <b><h:outputText value="#{localizedAnalysisEditorPageMessages.outputPorts}:"/></b> - <p:dataList value="#{currentAnalysisEditorBean.getOutputPorts(reader)}" var="port"> - #{port.name()} + <p:dataList value="#{reader.plugin.outputPorts}" var="port"> + #{port.name} </p:dataList> </ui:fragment> - <ui:fragment rendered="#{not empty currentAnalysisEditorBean.getRepositoryPorts(reader)}"> + <ui:fragment rendered="#{not empty reader.plugin.repositories}"> <b><h:outputText value="#{localizedAnalysisEditorPageMessages.repositoryPorts}:" /></b> - <p:dataList value="#{currentAnalysisEditorBean.getRepositoryPorts(reader)}" var="port"> - #{port.name()} + <p:dataList value="#{reader.plugin.repositories}" var="port"> + #{port.name} </p:dataList> </ui:fragment> - <ui:fragment rendered="#{not empty currentAnalysisEditorBean.getProperties(reader)}"> + <ui:fragment rendered="#{not empty reader.plugin.properties}"> <b><h:outputText value="#{localizedAnalysisEditorPageMessages.configuration}:"/></b> - <p:dataList value="#{currentAnalysisEditorBean.getProperties(reader)}" var="property"> - #{property.name()} + <p:dataList value="#{reader.plugin.properties}" var="property"> + #{property.name} </p:dataList> </ui:fragment> - <ui:fragment rendered="#{not empty currentAnalysisEditorBean.getDependencies(reader)}"> + <ui:fragment rendered="#{not empty reader.dependency}"> <b><h:outputText value="#{localizedAnalysisEditorPageMessages.dependencies}:"/></b> <br/> - <h:outputText value="#{currentAnalysisEditorBean.getDependencies(reader)}"/> + <h:outputText value="#{reader.dependency}"/> </ui:fragment> </p:tooltip> </ui:repeat> </p:tab> <p:tab title="#{localizedAnalysisEditorPageMessages.filter}"> - <ui:repeat value="#{currentAnalysisEditorBean.availableFilters}" var="filter"> - <p:commandLink id="filterLink" value="#{filter.simpleName}" action="#{currentAnalysisEditorBean.addPlugin(filter)}" update=":messages"/><br/> + <ui:repeat value="#{currentAnalysisEditorBean.availableComponents.filters}" var="filter"> + <p:commandLink id="filterLink" value="#{filter.plugin.name}" action="#{currentAnalysisEditorBean.addPlugin(filter)}" update=":messages"/><br/> <p:tooltip for="filterLink"> - <b><h:outputText value="#{filter.simpleName} (#{filter.name})"/></b> + <b><h:outputText value="#{filter.plugin.name} (#{filter.plugin.classname})"/></b> <br/> - <h:outputText value="#{currentAnalysisEditorBean.getDescription(filter)}"/> + <h:outputText value="#{filter.description}"/> <br/><br/> - <ui:fragment rendered="#{not empty currentAnalysisEditorBean.getInputPorts(filter)}"> + <ui:fragment rendered="#{not empty filter.plugin.inputPorts}"> <b><h:outputText value="#{localizedAnalysisEditorPageMessages.inputPorts}:"/></b> - <p:dataList value="#{currentAnalysisEditorBean.getInputPorts(filter)}" var="port"> - #{port.name()} + <p:dataList value="#{filter.plugin.inputPorts}" var="port"> + #{port.name} </p:dataList> </ui:fragment> - <ui:fragment rendered="#{not empty currentAnalysisEditorBean.getOutputPorts(filter)}"> + <ui:fragment rendered="#{not empty filter.plugin.outputPorts}"> <b><h:outputText value="#{localizedAnalysisEditorPageMessages.outputPorts}:"/></b> - <p:dataList value="#{currentAnalysisEditorBean.getOutputPorts(filter)}" var="port"> - #{port.name()} + <p:dataList value="#{filter.plugin.outputPorts}" var="port"> + #{port.name} </p:dataList> </ui:fragment> - <ui:fragment rendered="#{not empty currentAnalysisEditorBean.getRepositoryPorts(filter)}"> + <ui:fragment rendered="#{not empty filter.plugin.repositories}"> <b><h:outputText value="#{localizedAnalysisEditorPageMessages.repositoryPorts}:"/></b> - <p:dataList value="#{currentAnalysisEditorBean.getRepositoryPorts(filter)}" var="port"> - #{port.name()} + <p:dataList value="#{filter.plugin.repositories}" var="port"> + #{port.name} </p:dataList> </ui:fragment> - <ui:fragment rendered="#{not empty currentAnalysisEditorBean.getProperties(filter)}"> + <ui:fragment rendered="#{not empty filter.plugin.properties}"> <b><h:outputText value="#{localizedAnalysisEditorPageMessages.configuration}:"/></b> - <p:dataList value="#{currentAnalysisEditorBean.getProperties(filter)}" var="property"> - #{property.name()} + <p:dataList value="#{filter.plugin.properties}" var="property"> + #{property.name} </p:dataList> </ui:fragment> - <ui:fragment rendered="#{not empty currentAnalysisEditorBean.getDependencies(filter)}"> + <ui:fragment rendered="#{not empty filter.dependency}"> <b><h:outputText value="#{localizedAnalysisEditorPageMessages.dependencies}:"/></b> <br/> - <h:outputText value="#{currentAnalysisEditorBean.getDependencies(filter)}"/> + <h:outputText value="#{filter.dependency}"/> </ui:fragment> </p:tooltip> </ui:repeat> </p:tab> <p:tab title="#{localizedAnalysisEditorPageMessages.repositories}"> - <ui:repeat value="#{currentAnalysisEditorBean.availableRepositories}" var="repository"> - <p:commandLink id="repositoryLink" value="#{repository.simpleName}" action="#{currentAnalysisEditorBean.addRepository(repository)}" update=":messages"/><br/> + <ui:repeat value="#{currentAnalysisEditorBean.availableComponents.repositories}" var="repository"> + <p:commandLink id="repositoryLink" value="#{repository.repository.name}" action="#{currentAnalysisEditorBean.addRepository(repository)}" update=":messages"/><br/> <p:tooltip for="repositoryLink"> - <b><h:outputText value="#{repository.simpleName} (#{repository.name})"/></b> + <b><h:outputText value="#{repository.repository.name} (#{repository.repository.classname})"/></b> <br/> - <h:outputText value="#{currentAnalysisEditorBean.getDescription(repository)}"/> + <h:outputText value="#{repository.description}"/> <br/><br/> - <ui:fragment rendered="#{not empty currentAnalysisEditorBean.getProperties(repository)}"> + <ui:fragment rendered="#{not empty repository.repository.properties}"> <b><h:outputText value="#{localizedAnalysisEditorPageMessages.configuration}:"/></b> - <p:dataList value="#{currentAnalysisEditorBean.getProperties(repository)}" var="property"> - #{property.name()} + <p:dataList value="#{repository.repository.properties}" var="property"> + #{property.name} </p:dataList> </ui:fragment> - <ui:fragment rendered="#{not empty currentAnalysisEditorBean.getDependencies(repository)}"> + <ui:fragment rendered="#{not empty repository.dependency}"> <b><h:outputText value="#{localizedAnalysisEditorPageMessages.dependencies}:"/></b> <br/> - <h:outputText value="#{currentAnalysisEditorBean.getDependencies(repository)}"/> + <h:outputText value="#{repository.dependency}"/> </ui:fragment> </p:tooltip> </ui:repeat>