CCM NG: JavaDoc for the module system
git-svn-id: https://svn.libreccm.org/ccm/ccm_ng@3615 8810af33-2d31-482b-a856-94f89814c4dfpull/2/head
parent
b754e4b04c
commit
ae8943a75c
|
|
@ -42,13 +42,32 @@ public interface CcmModule {
|
||||||
/**
|
/**
|
||||||
* An implementation of this method is called after the module is installed.
|
* An implementation of this method is called after the module is installed.
|
||||||
*
|
*
|
||||||
* @param event
|
* This method can be used to create initial data.
|
||||||
|
*
|
||||||
|
* @param event @see InstallEvent
|
||||||
*/
|
*/
|
||||||
void install(InstallEvent event);
|
void install(InstallEvent event);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called each time the application is restarted.
|
||||||
|
*
|
||||||
|
* @param event @see InitEvent
|
||||||
|
*/
|
||||||
void init(InitEvent event);
|
void init(InitEvent event);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called each time the application is shutdown.
|
||||||
|
*
|
||||||
|
* @param event @see ShutdownEvent
|
||||||
|
*/
|
||||||
void shutdown(ShutdownEvent event);
|
void shutdown(ShutdownEvent event);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called if the module is uninstalled. The implementation of this
|
||||||
|
* method should remove all data created by the {@link #install(InstallEvent)}
|
||||||
|
* method.
|
||||||
|
*
|
||||||
|
* @param event @see UnInstallEvent
|
||||||
|
*/
|
||||||
void uninstall(UnInstallEvent event);
|
void uninstall(UnInstallEvent event);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,9 +25,13 @@ import javax.servlet.annotation.WebListener;
|
||||||
|
|
||||||
import org.apache.logging.log4j.LogManager;
|
import org.apache.logging.log4j.LogManager;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
import org.libreccm.modules.ModuleManager;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* An implementation of the {@link ServletContextListener} interface for
|
||||||
|
* initialising the modules.
|
||||||
|
*
|
||||||
|
* The real work is done by the {@link ModuleManager}. This class only delegates
|
||||||
|
* to the {@code ModuleManager}.
|
||||||
*
|
*
|
||||||
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,8 @@
|
||||||
package org.libreccm.modules;
|
package org.libreccm.modules;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Thrown by the {@link DependencyTreeManager} if something is wrong with the
|
||||||
|
* dependency tree.
|
||||||
*
|
*
|
||||||
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
||||||
*/
|
*/
|
||||||
|
|
@ -32,7 +34,7 @@ public class DependencyException extends Exception {
|
||||||
*/
|
*/
|
||||||
public DependencyException() {
|
public DependencyException() {
|
||||||
super();
|
super();
|
||||||
//Nothin more
|
//Nothing more
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,8 @@ import org.apache.maven.artifact.versioning.ComparableVersion;
|
||||||
* method. The list returned by {@link #orderModules(java.util.List)} contains
|
* method. The list returned by {@link #orderModules(java.util.List)} contains
|
||||||
* all modules in order.
|
* all modules in order.
|
||||||
*
|
*
|
||||||
|
* This class is <strong>not</strong> part of the public API.
|
||||||
|
*
|
||||||
* More information about topological sorting:
|
* More information about topological sorting:
|
||||||
* <a href="https://en.wikipedia.org/wiki/Topological_sorting">https://en.wikipedia.org/wiki/Topological_sorting</a>
|
* <a href="https://en.wikipedia.org/wiki/Topological_sorting">https://en.wikipedia.org/wiki/Topological_sorting</a>
|
||||||
*
|
*
|
||||||
|
|
@ -47,21 +49,34 @@ final class DependencyTreeManager {
|
||||||
private static final Logger LOGGER = LogManager.getLogger(
|
private static final Logger LOGGER = LogManager.getLogger(
|
||||||
DependencyTreeManager.class);
|
DependencyTreeManager.class);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialises the tree with the provided list of modules.
|
||||||
|
*
|
||||||
|
* @param modules The module for which a dependency tree is generated.
|
||||||
|
* @return An ordered list of tree nodes.
|
||||||
|
*
|
||||||
|
* @throws DependencyException If something is wrong with the dependency
|
||||||
|
* tree. For example if a module on which another module depends is missing
|
||||||
|
* or if a cycle is detected in the dependency tree.
|
||||||
|
*/
|
||||||
public List<TreeNode> generateTree(final Iterable<CcmModule> modules)
|
public List<TreeNode> generateTree(final Iterable<CcmModule> modules)
|
||||||
throws DependencyException {
|
throws DependencyException {
|
||||||
|
|
||||||
LOGGER.info("Starting to generate dependency tree...");
|
LOGGER.info("Starting to generate dependency tree...");
|
||||||
|
|
||||||
|
//Create the tree nodes. A HashMap is used to avoid duplicates and
|
||||||
|
//the lookup the nodes based on their name.
|
||||||
final Map<String, TreeNode> nodes = new HashMap<>();
|
final Map<String, TreeNode> nodes = new HashMap<>();
|
||||||
|
|
||||||
for (final CcmModule module : modules) {
|
for (final CcmModule module : modules) {
|
||||||
createTreeNode(module, nodes);
|
createTreeNode(module, nodes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Add the dependency relations to the nodes
|
||||||
for (final CcmModule module : modules) {
|
for (final CcmModule module : modules) {
|
||||||
addDependencyRelations(module, nodes);
|
addDependencyRelations(module, nodes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Generate the node list
|
||||||
final List<TreeNode> nodeList = new ArrayList<>();
|
final List<TreeNode> nodeList = new ArrayList<>();
|
||||||
for (final Map.Entry<String, TreeNode> entry : nodes.entrySet()) {
|
for (final Map.Entry<String, TreeNode> entry : nodes.entrySet()) {
|
||||||
nodeList.add(entry.getValue());
|
nodeList.add(entry.getValue());
|
||||||
|
|
@ -72,15 +87,29 @@ final class DependencyTreeManager {
|
||||||
return nodeList;
|
return nodeList;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates an ordered list of the tree nodes which can be used to
|
||||||
|
* initialise the modules in correct order.
|
||||||
|
*
|
||||||
|
* In this method the topological sorting happens.
|
||||||
|
*
|
||||||
|
* @param dependencyTree The list of tree nodes of the dependency tree.
|
||||||
|
* @return A ordered list of the tree nodes.
|
||||||
|
* @throws DependencyException If something is wrong with the dependency
|
||||||
|
* tree.
|
||||||
|
*/
|
||||||
public List<TreeNode> orderModules(final List<TreeNode> dependencyTree)
|
public List<TreeNode> orderModules(final List<TreeNode> dependencyTree)
|
||||||
throws DependencyException {
|
throws DependencyException {
|
||||||
LOGGER.info("Creating an ordered list from the dependency tree...");
|
LOGGER.info("Creating an ordered list from the dependency tree...");
|
||||||
|
|
||||||
|
//List for the ordered and resolved nodes/modules.
|
||||||
final List<TreeNode> orderedModules = new ArrayList<>();
|
final List<TreeNode> orderedModules = new ArrayList<>();
|
||||||
final List<TreeNode> resolvedModules = new ArrayList<>();
|
final List<TreeNode> resolvedModules = new ArrayList<>();
|
||||||
|
|
||||||
LOGGER.info("Looking for modules which do not depend on any other "
|
LOGGER.info("Looking for modules which do not depend on any other "
|
||||||
+ "modules...");
|
+ "modules...");
|
||||||
|
//Find all modules which do not depend on any other module. These
|
||||||
|
//modules are used as starting point for the sorting.
|
||||||
for (final TreeNode node : dependencyTree) {
|
for (final TreeNode node : dependencyTree) {
|
||||||
if (node.getDependsOn().isEmpty()) {
|
if (node.getDependsOn().isEmpty()) {
|
||||||
LOGGER.info(
|
LOGGER.info(
|
||||||
|
|
@ -93,21 +122,28 @@ final class DependencyTreeManager {
|
||||||
LOGGER.info("Ordering remaining nodes...");
|
LOGGER.info("Ordering remaining nodes...");
|
||||||
while (!resolvedModules.isEmpty()) {
|
while (!resolvedModules.isEmpty()) {
|
||||||
|
|
||||||
|
//Remove the first node from the resolved modules list
|
||||||
final TreeNode current = resolvedModules.remove(0);
|
final TreeNode current = resolvedModules.remove(0);
|
||||||
LOGGER.info("\tProcessing node for module \"{}\"...",
|
LOGGER.info("\tProcessing node for module \"{}\"...",
|
||||||
current.getModuleInfo().getModuleName());
|
current.getModuleInfo().getModuleName());
|
||||||
|
|
||||||
|
//Add the node to the ordered modules list.
|
||||||
orderedModules.add(current);
|
orderedModules.add(current);
|
||||||
|
|
||||||
|
//Remove the edges to the current node.
|
||||||
for (final TreeNode dependent : current.getDependentModules()) {
|
for (final TreeNode dependent : current.getDependentModules()) {
|
||||||
dependent.removeDependsOn(current);
|
dependent.removeDependsOn(current);
|
||||||
|
|
||||||
|
//If the dependent node has node more dependsOn relations put
|
||||||
|
//the node into the resolved modules list.
|
||||||
if (dependent.getDependsOn().isEmpty()) {
|
if (dependent.getDependsOn().isEmpty()) {
|
||||||
resolvedModules.add(dependent);
|
resolvedModules.add(dependent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Check if all nodes have been ordered. If not the tree has at least on
|
||||||
|
//on cycle and can't be processed.
|
||||||
if (orderedModules.size() == dependencyTree.size()) {
|
if (orderedModules.size() == dependencyTree.size()) {
|
||||||
LOGGER.info("Dependency graph proceessed successfully. "
|
LOGGER.info("Dependency graph proceessed successfully. "
|
||||||
+ "Modules in order:");
|
+ "Modules in order:");
|
||||||
|
|
@ -123,6 +159,8 @@ final class DependencyTreeManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Helper method for creating a tree node for a module and putting the
|
||||||
|
//node into the map of tree nodes.
|
||||||
private void createTreeNode(final CcmModule module,
|
private void createTreeNode(final CcmModule module,
|
||||||
final Map<String, TreeNode> nodes) {
|
final Map<String, TreeNode> nodes) {
|
||||||
final TreeNode node = new TreeNode(module);
|
final TreeNode node = new TreeNode(module);
|
||||||
|
|
@ -132,17 +170,29 @@ final class DependencyTreeManager {
|
||||||
nodes.put(node.getModuleInfo().getModuleName(), node);
|
nodes.put(node.getModuleInfo().getModuleName(), node);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper method for adding the dependency relations for a module to the
|
||||||
|
* nodes.
|
||||||
|
*
|
||||||
|
* @param module The module.
|
||||||
|
* @param nodes The map of nodes.
|
||||||
|
*
|
||||||
|
* @throws DependencyException If something goes wrong.
|
||||||
|
*/
|
||||||
private void addDependencyRelations(final CcmModule module,
|
private void addDependencyRelations(final CcmModule module,
|
||||||
final Map<String, TreeNode> nodes)
|
final Map<String, TreeNode> nodes)
|
||||||
throws DependencyException {
|
throws DependencyException {
|
||||||
|
//Load the module info for the current module
|
||||||
final ModuleInfo moduleInfo = new ModuleInfo();
|
final ModuleInfo moduleInfo = new ModuleInfo();
|
||||||
moduleInfo.load(module);
|
moduleInfo.load(module);
|
||||||
|
|
||||||
LOGGER.info("Adding dependency relations for module \"{}\"...",
|
LOGGER.info("Adding dependency relations for module \"{}\"...",
|
||||||
moduleInfo.getModuleName());
|
moduleInfo.getModuleName());
|
||||||
|
|
||||||
|
//Get the name of the module from the module info.
|
||||||
final String moduleName = moduleInfo.getModuleName();
|
final String moduleName = moduleInfo.getModuleName();
|
||||||
|
|
||||||
|
//Check if the nodes map has an entry for the module.
|
||||||
if (!nodes.containsKey(moduleName)) {
|
if (!nodes.containsKey(moduleName)) {
|
||||||
LOGGER.fatal("Modules nodes map does contain an entry for \"{}\". "
|
LOGGER.fatal("Modules nodes map does contain an entry for \"{}\". "
|
||||||
+ "That should not happen.",
|
+ "That should not happen.",
|
||||||
|
|
@ -153,62 +203,33 @@ final class DependencyTreeManager {
|
||||||
moduleName));
|
moduleName));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Get the node from the map
|
||||||
final TreeNode node = nodes.get(moduleName);
|
final TreeNode node = nodes.get(moduleName);
|
||||||
LOGGER.info("Processing required modules for module \"{}\"...",
|
LOGGER.info("Processing required modules for module \"{}\"...",
|
||||||
node.getModuleInfo().getModuleName());
|
node.getModuleInfo().getModuleName());
|
||||||
|
//Process the modules required by the current module and add the
|
||||||
|
//dependency relations.
|
||||||
for (final RequiredModule requiredModule : node.getModuleInfo().
|
for (final RequiredModule requiredModule : node.getModuleInfo().
|
||||||
getRequiredModules()) {
|
getRequiredModules()) {
|
||||||
|
|
||||||
// final ModuleInfo requiredModuleInfo = new ModuleInfo();
|
|
||||||
// requiredModuleInfo.load(requiredModule.module());
|
|
||||||
//
|
|
||||||
// LOGGER.info("\tModule \"{}\" requires module \"{}\".",
|
|
||||||
// node.getModuleInfo().getModuleName(),
|
|
||||||
// requiredModuleInfo.getModuleName());
|
|
||||||
//
|
|
||||||
// if (!nodes.containsKey(requiredModuleInfo.getModuleName())) {
|
|
||||||
//
|
|
||||||
// LOGGER.fatal("Required module \"{}\" no found.",
|
|
||||||
// requiredModuleInfo.getModuleName());
|
|
||||||
//
|
|
||||||
// throw new DependencyException(String.format(
|
|
||||||
// "Module \"%s\" depends on module \"%s\" but the dependency "
|
|
||||||
// + "tree does contain an entry for module \"%s\".",
|
|
||||||
// moduleInfo.getModuleName(),
|
|
||||||
// requiredModuleInfo.getModuleName(),
|
|
||||||
// requiredModuleInfo.getModuleName()));
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// final TreeNode dependencyNode = nodes.get(requiredModuleInfo.
|
|
||||||
// getModuleName());
|
|
||||||
//
|
|
||||||
// //Check version
|
|
||||||
// if (!validateVersion(dependencyNode.getModuleInfo().
|
|
||||||
// getModuleVersion(),
|
|
||||||
// requiredModule.minVersion(),
|
|
||||||
// requiredModule.maxVersion())) {
|
|
||||||
// throw new DependencyException(String.format(
|
|
||||||
// "The required module is avialable but in the correct "
|
|
||||||
// + "version. "
|
|
||||||
// + "Available version: \"%s\"; "
|
|
||||||
// + "minimal required version: \"%s\"; "
|
|
||||||
// + "maximum required version: \"%s\"",
|
|
||||||
// dependencyNode.getModuleInfo().getModuleVersion(),
|
|
||||||
// requiredModule.minVersion(),
|
|
||||||
// requiredModule.maxVersion()));
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// node.addDependsOn(dependencyNode);
|
|
||||||
// dependencyNode.addDependentModule(node);
|
|
||||||
addDependencyRelation(nodes, node, requiredModule);
|
addDependencyRelation(nodes, node, requiredModule);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper method for adding a single dependency relation.
|
||||||
|
*
|
||||||
|
* @param nodes The map of tree nodes.
|
||||||
|
* @param node The node to which the dependency relations are added.
|
||||||
|
* @param requiredModule The module required by the current module/node.
|
||||||
|
* @throws DependencyException
|
||||||
|
*/
|
||||||
private void addDependencyRelation(final Map<String, TreeNode> nodes,
|
private void addDependencyRelation(final Map<String, TreeNode> nodes,
|
||||||
final TreeNode node,
|
final TreeNode node,
|
||||||
final RequiredModule requiredModule)
|
final RequiredModule requiredModule)
|
||||||
throws DependencyException {
|
throws DependencyException {
|
||||||
|
//Get the module info for the required module
|
||||||
final ModuleInfo requiredInfo = new ModuleInfo();
|
final ModuleInfo requiredInfo = new ModuleInfo();
|
||||||
requiredInfo.load(requiredModule.module());
|
requiredInfo.load(requiredModule.module());
|
||||||
|
|
||||||
|
|
@ -216,6 +237,7 @@ final class DependencyTreeManager {
|
||||||
node.getModuleInfo().getModuleName(),
|
node.getModuleInfo().getModuleName(),
|
||||||
requiredInfo.getModuleName());
|
requiredInfo.getModuleName());
|
||||||
|
|
||||||
|
//Check if the nodes list has an entry for the required module.
|
||||||
if (!nodes.containsKey(requiredInfo.getModuleName())) {
|
if (!nodes.containsKey(requiredInfo.getModuleName())) {
|
||||||
LOGGER.fatal("Required module \"{}\" no found.",
|
LOGGER.fatal("Required module \"{}\" no found.",
|
||||||
requiredInfo.getModuleName());
|
requiredInfo.getModuleName());
|
||||||
|
|
@ -227,6 +249,7 @@ final class DependencyTreeManager {
|
||||||
requiredInfo.getModuleName()));
|
requiredInfo.getModuleName()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Validate the version of the required module.
|
||||||
final TreeNode dependencyNode = nodes.get(requiredInfo.
|
final TreeNode dependencyNode = nodes.get(requiredInfo.
|
||||||
getModuleName());
|
getModuleName());
|
||||||
if (!validateVersion(dependencyNode.getModuleInfo().
|
if (!validateVersion(dependencyNode.getModuleInfo().
|
||||||
|
|
@ -244,6 +267,7 @@ final class DependencyTreeManager {
|
||||||
requiredModule.maxVersion()));
|
requiredModule.maxVersion()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Create the dependencies relations.
|
||||||
node.addDependsOn(dependencyNode);
|
node.addDependsOn(dependencyNode);
|
||||||
dependencyNode.addDependentModule(node);
|
dependencyNode.addDependentModule(node);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,10 @@
|
||||||
package org.libreccm.modules;
|
package org.libreccm.modules;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Event object for the module initialisation.
|
||||||
|
*
|
||||||
|
* @see ModuleEvent
|
||||||
|
*
|
||||||
*
|
*
|
||||||
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,9 @@
|
||||||
package org.libreccm.modules;
|
package org.libreccm.modules;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Event object for the module initialisation.
|
||||||
|
*
|
||||||
|
* @see ModuleEvent
|
||||||
*
|
*
|
||||||
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,8 @@ import javax.persistence.Table;
|
||||||
import static org.libreccm.core.CoreConstants.DB_SCHEMA;
|
import static org.libreccm.core.CoreConstants.DB_SCHEMA;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* A JPA entity bean for for installed modules table for use in the LibreCCM
|
||||||
|
* administration UI.
|
||||||
*
|
*
|
||||||
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
||||||
*/
|
*/
|
||||||
|
|
@ -49,9 +51,15 @@ public class InstalledModule implements Serializable {
|
||||||
@Column(name = "MODULE_ID")
|
@Column(name = "MODULE_ID")
|
||||||
private int moduleId;
|
private int moduleId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The fully qualified name of the module class.
|
||||||
|
*/
|
||||||
@Column(name = "MODULE_CLASS_NAME", length = 2048, unique = true)
|
@Column(name = "MODULE_CLASS_NAME", length = 2048, unique = true)
|
||||||
private String moduleClassName;
|
private String moduleClassName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The status of the module.
|
||||||
|
*/
|
||||||
@Column(name = "STATUS")
|
@Column(name = "STATUS")
|
||||||
@Enumerated(EnumType.STRING)
|
@Enumerated(EnumType.STRING)
|
||||||
private ModuleStatus status;
|
private ModuleStatus status;
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@
|
||||||
package org.libreccm.modules;
|
package org.libreccm.modules;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Thrown if an error occurs in the {@link CcmIntegrator}.
|
||||||
*
|
*
|
||||||
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ import static java.lang.annotation.ElementType.*;
|
||||||
import java.lang.annotation.Target;
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Annotation for describing some meta of a module.
|
* Annotation for describing some meta data of a module.
|
||||||
*
|
*
|
||||||
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,8 @@ package org.libreccm.modules;
|
||||||
import javax.persistence.EntityManager;
|
import javax.persistence.EntityManager;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Base class for the module lifecycle events. Provides access to the JPA
|
||||||
|
* {@code EntityManager}.
|
||||||
*
|
*
|
||||||
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -32,21 +32,47 @@ import org.apache.logging.log4j.LogManager;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* A representation of the module metadata combining the data from the modules
|
||||||
|
* info file and the annotations on the module class.
|
||||||
*
|
*
|
||||||
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
||||||
*/
|
*/
|
||||||
public class ModuleInfo {
|
public class ModuleInfo {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constant for the artifactId property in the module info file.
|
||||||
|
*/
|
||||||
private static final String ARTIFACT_ID = "artifactId";
|
private static final String ARTIFACT_ID = "artifactId";
|
||||||
|
/**
|
||||||
|
* Constant for the groupId property in the module info file.
|
||||||
|
*/
|
||||||
private static final String GROUP_ID = "groupId";
|
private static final String GROUP_ID = "groupId";
|
||||||
|
/**
|
||||||
|
* Constant for the version property in the module info file.
|
||||||
|
*/
|
||||||
private static final String VERSION = "version";
|
private static final String VERSION = "version";
|
||||||
|
|
||||||
private static final Logger LOGGER = LogManager.getLogger(ModuleInfo.class);
|
private static final Logger LOGGER = LogManager.getLogger(ModuleInfo.class);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The name of the module (artifact id).
|
||||||
|
*/
|
||||||
private transient String moduleName;
|
private transient String moduleName;
|
||||||
|
/**
|
||||||
|
* The data package of the module (group id).
|
||||||
|
*/
|
||||||
private transient String moduleDataPackage;
|
private transient String moduleDataPackage;
|
||||||
|
/**
|
||||||
|
* The entities provided by the module.
|
||||||
|
*/
|
||||||
private transient Class<?>[] moduleEntities;
|
private transient Class<?>[] moduleEntities;
|
||||||
|
/**
|
||||||
|
* The version of the module.
|
||||||
|
*/
|
||||||
private transient String moduleVersion;
|
private transient String moduleVersion;
|
||||||
|
/**
|
||||||
|
* The modules required by the described module.
|
||||||
|
*/
|
||||||
private transient RequiredModule[] requiredModules;
|
private transient RequiredModule[] requiredModules;
|
||||||
|
|
||||||
public ModuleInfo() {
|
public ModuleInfo() {
|
||||||
|
|
@ -61,36 +87,47 @@ public class ModuleInfo {
|
||||||
return moduleDataPackage;
|
return moduleDataPackage;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getDbSchemaName() {
|
// /**
|
||||||
return moduleName.toLowerCase(Locale.ROOT).replace("-", "_");
|
// * Gets the database schema name for the module. That is the
|
||||||
}
|
// * {@link moduleName} with all hyphens replaced by underscore (hyphens can
|
||||||
|
// * be nasty in SQL).
|
||||||
public String getDbScriptsLocation(final Connection connection)
|
// *
|
||||||
throws SQLException {
|
// * @return The name of the database schema of the module.
|
||||||
final StringBuffer buffer
|
// */
|
||||||
= new StringBuffer("classpath:/db/migrations/");
|
// public String getDbSchemaName() {
|
||||||
buffer.append(moduleDataPackage);
|
// return moduleName.toLowerCase(Locale.ROOT).replace("-", "_");
|
||||||
switch (connection.getMetaData().getDatabaseProductName()) {
|
// }
|
||||||
case "H2": {
|
// /**
|
||||||
buffer.append("/h2");
|
// *
|
||||||
break;
|
// * @param connection
|
||||||
}
|
// * @return
|
||||||
case "MySQL":
|
// * @throws SQLException
|
||||||
buffer.append("/mysql");
|
// */
|
||||||
break;
|
// public String getDbScriptsLocation(final Connection connection)
|
||||||
case "PostgreSQL":
|
// throws SQLException {
|
||||||
buffer.append("/postgresql");
|
// final StringBuffer buffer
|
||||||
break;
|
// = new StringBuffer("classpath:/db/migrations/");
|
||||||
default:
|
// buffer.append(moduleDataPackage);
|
||||||
throw new IntegrationException(String.format(
|
// switch (connection.getMetaData().getDatabaseProductName()) {
|
||||||
"Integration failed. Database \"%s\" is not supported yet.",
|
// case "H2": {
|
||||||
connection.getMetaData().
|
// buffer.append("/h2");
|
||||||
getDatabaseProductName()));
|
// break;
|
||||||
}
|
// }
|
||||||
|
// case "MySQL":
|
||||||
return buffer.toString();
|
// buffer.append("/mysql");
|
||||||
}
|
// break;
|
||||||
|
// case "PostgreSQL":
|
||||||
|
// buffer.append("/postgresql");
|
||||||
|
// break;
|
||||||
|
// default:
|
||||||
|
// throw new IntegrationException(String.format(
|
||||||
|
// "Integration failed. Database \"%s\" is not supported yet.",
|
||||||
|
// connection.getMetaData().
|
||||||
|
// getDatabaseProductName()));
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// return buffer.toString();
|
||||||
|
// }
|
||||||
public String getModuleVersion() {
|
public String getModuleVersion() {
|
||||||
return moduleVersion;
|
return moduleVersion;
|
||||||
}
|
}
|
||||||
|
|
@ -107,85 +144,29 @@ public class ModuleInfo {
|
||||||
load(module.getClass());
|
load(module.getClass());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads the module info data.
|
||||||
|
*
|
||||||
|
* @param moduleClass The module class for which the module data is loaded.
|
||||||
|
*/
|
||||||
public void load(final Class<? extends CcmModule> moduleClass) {
|
public void load(final Class<? extends CcmModule> moduleClass) {
|
||||||
LOGGER.info("Reading module info for {}...", moduleClass.getName());
|
LOGGER.info("Reading module info for {}...", moduleClass.getName());
|
||||||
final Module annotation = moduleClass.getAnnotation(Module.class);
|
final Module annotation = moduleClass.getAnnotation(Module.class);
|
||||||
|
|
||||||
final Properties properties = loadModuleInfoFile(moduleClass);
|
final Properties properties = loadModuleInfoFile(moduleClass);
|
||||||
// final Properties properties = new Properties();
|
|
||||||
//
|
|
||||||
// final String path = String.format("/module-info/%s.properties",
|
|
||||||
// moduleClass.getName());
|
|
||||||
// LOGGER.info("Trying to retrieve module info for module {} from {}...",
|
|
||||||
// moduleClass.getName(),
|
|
||||||
// path);
|
|
||||||
// final InputStream stream = moduleClass.getResourceAsStream(path);
|
|
||||||
// if (stream == null) {
|
|
||||||
// LOGGER.warn("No module info for {} found at {}",
|
|
||||||
// moduleClass.getName(),
|
|
||||||
// path);
|
|
||||||
// } else {
|
|
||||||
// try {
|
|
||||||
// properties.load(stream);
|
|
||||||
// } catch (IOException ex) {
|
|
||||||
// LOGGER.error("Failed to read module info for {} at {}.",
|
|
||||||
// moduleClass.getName(),
|
|
||||||
// path);
|
|
||||||
// LOGGER.error("Cause: ", ex);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
LOGGER.info("Reading module name...");
|
LOGGER.info("Reading module name...");
|
||||||
// if (annotation.name() != null && !annotation.name().isEmpty()) {
|
|
||||||
// moduleName = annotation.name();
|
|
||||||
// } else if (properties.getProperty(ARTIFACT_ID) != null
|
|
||||||
// && !properties.getProperty(ARTIFACT_ID).isEmpty()) {
|
|
||||||
// moduleName = properties.getProperty(ARTIFACT_ID);
|
|
||||||
// } else {
|
|
||||||
// LOGGER.warn(
|
|
||||||
// "The module was not specificied by the module annotation "
|
|
||||||
// + "or by the module info file. Creating name from "
|
|
||||||
// + "simple name of the module class.");
|
|
||||||
// moduleName = moduleClass.getSimpleName().toLowerCase();
|
|
||||||
// }
|
|
||||||
LOGGER.info("Reading module name...");
|
LOGGER.info("Reading module name...");
|
||||||
moduleName = readModuleName(moduleClass, annotation, properties);
|
moduleName = readModuleName(moduleClass, annotation, properties);
|
||||||
LOGGER.info("Module name is \"{}\".", moduleName);
|
LOGGER.info("Module name is \"{}\".", moduleName);
|
||||||
|
|
||||||
LOGGER.info("Reading module package name...");
|
LOGGER.info("Reading module package name...");
|
||||||
// if (annotation.packageName() != null
|
|
||||||
// && !annotation.packageName().isEmpty()) {
|
|
||||||
// moduleDataPackage = annotation.packageName();
|
|
||||||
// } else if (properties.getProperty(GROUP_ID) != null
|
|
||||||
// && !properties.getProperty(GROUP_ID).isEmpty()) {
|
|
||||||
// moduleDataPackage = String.format("%s/%s",
|
|
||||||
// properties.getProperty(GROUP_ID),
|
|
||||||
// properties.
|
|
||||||
// getProperty(ARTIFACT_ID).replace(
|
|
||||||
// "-", "_"));
|
|
||||||
// } else {
|
|
||||||
// LOGGER.warn("The module data package was specified by the module "
|
|
||||||
// + "annotation nore was an group id found in the module info"
|
|
||||||
// + "file. Creating data package name from the name of the "
|
|
||||||
// + "module class.");
|
|
||||||
// moduleDataPackage = moduleClass.getName().toLowerCase();
|
|
||||||
// }
|
|
||||||
moduleDataPackage = readModulePackageName(moduleClass,
|
moduleDataPackage = readModulePackageName(moduleClass,
|
||||||
annotation,
|
annotation,
|
||||||
properties);
|
properties);
|
||||||
LOGGER.info("Module data package is \"{}\".", moduleDataPackage);
|
LOGGER.info("Module data package is \"{}\".", moduleDataPackage);
|
||||||
|
|
||||||
LOGGER.info("Reading module version...");
|
LOGGER.info("Reading module version...");
|
||||||
// if (annotation.version() != null && !annotation.version().isEmpty()) {
|
|
||||||
// moduleVersion = annotation.version();
|
|
||||||
// } else if (properties.getProperty(VERSION) != null
|
|
||||||
// && !properties.getProperty(VERSION).isEmpty()) {
|
|
||||||
// moduleVersion = properties.getProperty(VERSION);
|
|
||||||
// } else {
|
|
||||||
// LOGGER.warn("Module version is not specified by the module "
|
|
||||||
// + "annotation or in the module info file. Module version is "
|
|
||||||
// + "undefinied. This can lead to all sorts of strange errors!");
|
|
||||||
// }
|
|
||||||
moduleVersion = readModuleVersion(annotation, properties);
|
moduleVersion = readModuleVersion(annotation, properties);
|
||||||
LOGGER.info("Module version is \"{}.\"", moduleVersion);
|
LOGGER.info("Module version is \"{}.\"", moduleVersion);
|
||||||
|
|
||||||
|
|
@ -193,8 +174,16 @@ public class ModuleInfo {
|
||||||
moduleEntities = annotation.entities();
|
moduleEntities = annotation.entities();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load the module info properties file.
|
||||||
|
*
|
||||||
|
* @param moduleClass The class for which the module info properties file is
|
||||||
|
* loaded.
|
||||||
|
*
|
||||||
|
* @return The properties from the module info properties file.
|
||||||
|
*/
|
||||||
private Properties loadModuleInfoFile(
|
private Properties loadModuleInfoFile(
|
||||||
final Class<? extends CcmModule> moduleClass) {
|
final Class<? extends CcmModule> moduleClass) {
|
||||||
|
|
||||||
final Properties moduleInfo = new Properties();
|
final Properties moduleInfo = new Properties();
|
||||||
final String path = String.format("/module-info/%s.properties",
|
final String path = String.format("/module-info/%s.properties",
|
||||||
|
|
@ -220,19 +209,30 @@ public class ModuleInfo {
|
||||||
return moduleInfo;
|
return moduleInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads the module name. If the module annotation of the module class has a
|
||||||
|
* value for {@code name} that value is used. Otherwise the name from the
|
||||||
|
* module info file is used. If this name is also empty the simple name of
|
||||||
|
* the module class is used.
|
||||||
|
*
|
||||||
|
* @param moduleClass The module class.
|
||||||
|
* @param annotation The module annotation of the module class.
|
||||||
|
* @param moduleInfo The module info properties.
|
||||||
|
* @return The name of the module.
|
||||||
|
*/
|
||||||
private String readModuleName(final Class<? extends CcmModule> moduleClass,
|
private String readModuleName(final Class<? extends CcmModule> moduleClass,
|
||||||
final Module annotation,
|
final Module annotation,
|
||||||
final Properties moduleInfo) {
|
final Properties moduleInfo) {
|
||||||
@SuppressWarnings(
|
@SuppressWarnings(
|
||||||
"PMD.LongVariable")
|
"PMD.LongVariable")
|
||||||
final boolean annotationHasModuleName = annotation.name() != null
|
final boolean annotationHasModuleName = annotation.name() != null
|
||||||
&& !annotation.name()
|
&& !annotation.name()
|
||||||
.isEmpty();
|
.isEmpty();
|
||||||
@SuppressWarnings("PMD.LongVariable")
|
@SuppressWarnings("PMD.LongVariable")
|
||||||
final boolean moduleInfoHasModuleName = moduleInfo.getProperty(
|
final boolean moduleInfoHasModuleName = moduleInfo.getProperty(
|
||||||
ARTIFACT_ID)
|
ARTIFACT_ID)
|
||||||
!= null && !moduleInfo
|
!= null && !moduleInfo
|
||||||
.getProperty(ARTIFACT_ID).isEmpty();
|
.getProperty(ARTIFACT_ID).isEmpty();
|
||||||
|
|
||||||
if (annotationHasModuleName) {
|
if (annotationHasModuleName) {
|
||||||
return annotation.name();
|
return annotation.name();
|
||||||
|
|
@ -240,29 +240,40 @@ public class ModuleInfo {
|
||||||
return moduleInfo.getProperty(ARTIFACT_ID);
|
return moduleInfo.getProperty(ARTIFACT_ID);
|
||||||
} else {
|
} else {
|
||||||
LOGGER.warn(
|
LOGGER.warn(
|
||||||
"The module was not specificied by the module annotation "
|
"The module was not specificied by the module annotation "
|
||||||
+ "or by the module info file. Creating name from "
|
+ "or by the module info file. Creating name from "
|
||||||
+ "simple name of the module class.");
|
+ "simple name of the module class.");
|
||||||
return moduleClass.getSimpleName().toLowerCase();
|
return moduleClass.getSimpleName().toLowerCase();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads the module name. If the module annotation of the module class has a
|
||||||
|
* value for {@code packageName} that value is used. Otherwise the groupId
|
||||||
|
* from the module info file is used. If this name is also empty the package
|
||||||
|
* name of the module class is used.
|
||||||
|
*
|
||||||
|
* @param moduleClass The module class.
|
||||||
|
* @param annotation The module annotation of the module class.
|
||||||
|
* @param moduleInfo The module info properties.
|
||||||
|
* @return The name of the module.
|
||||||
|
*/
|
||||||
private String readModulePackageName(
|
private String readModulePackageName(
|
||||||
final Class<? extends CcmModule> moduleClass,
|
final Class<? extends CcmModule> moduleClass,
|
||||||
final Module annotation,
|
final Module annotation,
|
||||||
final Properties moduleInfo) {
|
final Properties moduleInfo) {
|
||||||
|
|
||||||
@SuppressWarnings(
|
@SuppressWarnings(
|
||||||
"PMD.LongVariable")
|
"PMD.LongVariable")
|
||||||
final boolean annotationHasPackageName = annotation.packageName()
|
final boolean annotationHasPackageName = annotation.packageName()
|
||||||
!= null
|
!= null
|
||||||
&& !annotation
|
&& !annotation
|
||||||
.packageName()
|
.packageName()
|
||||||
.isEmpty();
|
.isEmpty();
|
||||||
@SuppressWarnings("PMD.LongVariable")
|
@SuppressWarnings("PMD.LongVariable")
|
||||||
final boolean moduleInfoHasPackageName = moduleInfo
|
final boolean moduleInfoHasPackageName = moduleInfo
|
||||||
.getProperty(GROUP_ID) != null && !moduleInfo.getProperty(
|
.getProperty(GROUP_ID) != null && !moduleInfo.getProperty(
|
||||||
GROUP_ID).isEmpty();
|
GROUP_ID).isEmpty();
|
||||||
if (annotationHasPackageName) {
|
if (annotationHasPackageName) {
|
||||||
return annotation.packageName();
|
return annotation.packageName();
|
||||||
} else if (moduleInfoHasPackageName) {
|
} else if (moduleInfoHasPackageName) {
|
||||||
|
|
@ -273,27 +284,39 @@ public class ModuleInfo {
|
||||||
"_"));
|
"_"));
|
||||||
} else {
|
} else {
|
||||||
LOGGER.warn("The module data package was specified by the module "
|
LOGGER.warn("The module data package was specified by the module "
|
||||||
+ "annotation nore was an group id found in the module info"
|
+ "annotation nore was an group id found in the module info"
|
||||||
+ "file. Creating data package name from the name of the "
|
+ "file. Creating data package name from the name of the "
|
||||||
+ "module class.");
|
+ "module class.");
|
||||||
return moduleClass.getName().toLowerCase();
|
return moduleClass.getName().toLowerCase();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads the module version. If the module annotation on the module class
|
||||||
|
* specifies a value for the version that value is used. Otherwise the value
|
||||||
|
* from the module info properties is used. If the properties do not specify
|
||||||
|
* a version the version is undefined. That should be avoided because it can
|
||||||
|
* lead to strange errors.
|
||||||
|
*
|
||||||
|
* @param annotation The module annotation of the module.
|
||||||
|
* @param moduleInfo The module info properties.
|
||||||
|
* @return The version of the module or {@code null} if the version is
|
||||||
|
* unspecified.
|
||||||
|
*/
|
||||||
private String readModuleVersion(
|
private String readModuleVersion(
|
||||||
final Module annotation,
|
final Module annotation,
|
||||||
final Properties moduleInfo) {
|
final Properties moduleInfo) {
|
||||||
|
|
||||||
@SuppressWarnings("PMD.LongVariable")
|
@SuppressWarnings("PMD.LongVariable")
|
||||||
final boolean annotationHasVersion = annotation.version() != null
|
final boolean annotationHasVersion = annotation.version() != null
|
||||||
&& !annotation.version()
|
&& !annotation.version()
|
||||||
.isEmpty();
|
.isEmpty();
|
||||||
@SuppressWarnings("PMD.LongVariable")
|
@SuppressWarnings("PMD.LongVariable")
|
||||||
final boolean moduleInfoHasVersion = moduleInfo.getProperty(VERSION)
|
final boolean moduleInfoHasVersion = moduleInfo.getProperty(VERSION)
|
||||||
!= null
|
!= null
|
||||||
&& !moduleInfo.getProperty(
|
&& !moduleInfo.getProperty(
|
||||||
VERSION)
|
VERSION)
|
||||||
.isEmpty();
|
.isEmpty();
|
||||||
|
|
||||||
if (annotationHasVersion) {
|
if (annotationHasVersion) {
|
||||||
return annotation.version();
|
return annotation.version();
|
||||||
|
|
@ -301,7 +324,7 @@ public class ModuleInfo {
|
||||||
return moduleInfo.getProperty(VERSION);
|
return moduleInfo.getProperty(VERSION);
|
||||||
} else {
|
} else {
|
||||||
LOGGER.warn("Module version is not specified by the module "
|
LOGGER.warn("Module version is not specified by the module "
|
||||||
+ "annotation or in the module info file. Module version is "
|
+ "annotation or in the module info file. Module version is "
|
||||||
+ "undefinied. This can lead to all sorts of strange errors!");
|
+ "undefinied. This can lead to all sorts of strange errors!");
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,10 @@ import org.apache.logging.log4j.LogManager;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* The {@code ModuleManager} manages the lifecycle of all modules.
|
||||||
|
*
|
||||||
|
* Please note: While this class is public it is not part of the public API and
|
||||||
|
* should not be called by other classes.
|
||||||
*
|
*
|
||||||
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
||||||
*/
|
*/
|
||||||
|
|
@ -39,33 +43,45 @@ import org.apache.logging.log4j.Logger;
|
||||||
public class ModuleManager {
|
public class ModuleManager {
|
||||||
|
|
||||||
private static final Logger LOGGER = LogManager.getLogger(
|
private static final Logger LOGGER = LogManager.getLogger(
|
||||||
ModuleManager.class
|
ModuleManager.class
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@code EntityManager} for interacting with the database.
|
||||||
|
*/
|
||||||
@PersistenceContext(name = "LibreCCM")
|
@PersistenceContext(name = "LibreCCM")
|
||||||
private EntityManager entityManager;
|
private EntityManager entityManager;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dependency tree nodes.
|
||||||
|
*/
|
||||||
private List<TreeNode> moduleNodes;
|
private List<TreeNode> moduleNodes;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method for initialising the dependency tree (is put into
|
||||||
|
* {@link #moduleNodes}. The method is annotated with {@link PostConstruct}
|
||||||
|
* which causes the CDI container to call this method after an instance of
|
||||||
|
* this class is generated.
|
||||||
|
*/
|
||||||
@PostConstruct
|
@PostConstruct
|
||||||
public void initDependencyTree() {
|
public void initDependencyTree() {
|
||||||
|
|
||||||
|
//Find all modules using the service loader.
|
||||||
LOGGER.info("Finding modules");
|
LOGGER.info("Finding modules");
|
||||||
final ServiceLoader<CcmModule> modules = ServiceLoader.load(
|
final ServiceLoader<CcmModule> modules = ServiceLoader.load(
|
||||||
CcmModule.class);
|
CcmModule.class);
|
||||||
|
|
||||||
LOGGER.info("Creating dependency tree these modules:");
|
LOGGER.info("Creating dependency tree these modules:");
|
||||||
for (final CcmModule module : modules) {
|
for (final CcmModule module : modules) {
|
||||||
final ModuleInfo moduleInfo = new ModuleInfo();
|
final ModuleInfo moduleInfo = new ModuleInfo();
|
||||||
moduleInfo.load(module);
|
moduleInfo.load(module);
|
||||||
LOGGER.info("\t{} {}",
|
LOGGER.info("\t{} {}",
|
||||||
// ModuleUtil.getModuleName(module),
|
|
||||||
// ModuleUtil.getVersion(module));
|
|
||||||
moduleInfo.getModuleName(),
|
moduleInfo.getModuleName(),
|
||||||
moduleInfo.getModuleVersion());
|
moduleInfo.getModuleVersion());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Create the dependency tree
|
||||||
final DependencyTreeManager treeManager = new DependencyTreeManager();
|
final DependencyTreeManager treeManager = new DependencyTreeManager();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final List<TreeNode> tree = treeManager.generateTree(modules);
|
final List<TreeNode> tree = treeManager.generateTree(modules);
|
||||||
moduleNodes = treeManager.orderModules(tree);
|
moduleNodes = treeManager.orderModules(tree);
|
||||||
|
|
@ -75,23 +91,33 @@ public class ModuleManager {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialises all modules.
|
||||||
|
*/
|
||||||
@Transactional(Transactional.TxType.REQUIRED)
|
@Transactional(Transactional.TxType.REQUIRED)
|
||||||
public void initModules() {
|
public void initModules() {
|
||||||
LOGGER.info("Initalising modules...");
|
LOGGER.info("Initalising modules...");
|
||||||
|
//Initialise all modules in the correct order
|
||||||
for (final TreeNode node : moduleNodes) {
|
for (final TreeNode node : moduleNodes) {
|
||||||
|
|
||||||
|
//Create an install event instance.
|
||||||
final InstallEvent installEvent = new InstallEvent();
|
final InstallEvent installEvent = new InstallEvent();
|
||||||
installEvent.setEntityManager(entityManager);
|
installEvent.setEntityManager(entityManager);
|
||||||
|
|
||||||
|
//Check if the module is a new module. If it is a new module
|
||||||
|
//call the install method of the module and set the module status
|
||||||
|
//to installed after the install method has run sucessfully.
|
||||||
final InstalledModule installedModule = entityManager.find(
|
final InstalledModule installedModule = entityManager.find(
|
||||||
InstalledModule.class,
|
InstalledModule.class,
|
||||||
node.getModule().getClass().getName().hashCode());
|
node.getModule().getClass().getName().hashCode());
|
||||||
if (installedModule != null
|
if (installedModule != null
|
||||||
&& installedModule.getStatus() == ModuleStatus.NEW) {
|
&& installedModule.getStatus() == ModuleStatus.NEW) {
|
||||||
node.getModule().install(installEvent);
|
node.getModule().install(installEvent);
|
||||||
installedModule.setStatus(ModuleStatus.INSTALLED);
|
installedModule.setStatus(ModuleStatus.INSTALLED);
|
||||||
entityManager.merge(installedModule);
|
entityManager.merge(installedModule);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Create an init event instance and call the init method.
|
||||||
final InitEvent initEvent = new InitEvent();
|
final InitEvent initEvent = new InitEvent();
|
||||||
initEvent.setEntityManager(entityManager);
|
initEvent.setEntityManager(entityManager);
|
||||||
node.getModule().init(initEvent);
|
node.getModule().init(initEvent);
|
||||||
|
|
@ -100,8 +126,8 @@ public class ModuleManager {
|
||||||
node.getModule().getClass().getName());
|
node.getModule().getClass().getName());
|
||||||
final Properties moduleInfo = getModuleInfo(node.getModule());
|
final Properties moduleInfo = getModuleInfo(node.getModule());
|
||||||
LOGGER
|
LOGGER
|
||||||
.info("Module group id: {}", moduleInfo.getProperty(
|
.info("Module group id: {}", moduleInfo.getProperty(
|
||||||
"groupId"));
|
"groupId"));
|
||||||
LOGGER.info("Module artifact id: {}", moduleInfo.getProperty(
|
LOGGER.info("Module artifact id: {}", moduleInfo.getProperty(
|
||||||
"artifactId"));
|
"artifactId"));
|
||||||
LOGGER.info("Module version: {}", moduleInfo.getProperty("version"));
|
LOGGER.info("Module version: {}", moduleInfo.getProperty("version"));
|
||||||
|
|
@ -111,16 +137,21 @@ public class ModuleManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper method for retrieving the module info for a module.
|
||||||
|
*
|
||||||
|
* @param module
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
private Properties getModuleInfo(final CcmModule module) {
|
private Properties getModuleInfo(final CcmModule module) {
|
||||||
final Properties moduleInfo = new Properties();
|
final Properties moduleInfo = new Properties();
|
||||||
|
|
||||||
// try {
|
|
||||||
final String moduleInfoPath = String.format(
|
final String moduleInfoPath = String.format(
|
||||||
"/module-info/%s.properties",
|
"/module-info/%s.properties",
|
||||||
module.getClass().getName());
|
module.getClass().getName());
|
||||||
LOGGER.info("Path for module info: {}", moduleInfoPath);
|
LOGGER.info("Path for module info: {}", moduleInfoPath);
|
||||||
try (final InputStream stream = module.getClass().getResourceAsStream(
|
try (final InputStream stream = module.getClass().getResourceAsStream(
|
||||||
moduleInfoPath)) {
|
moduleInfoPath)) {
|
||||||
if (stream == null) {
|
if (stream == null) {
|
||||||
LOGGER.warn("No module info found.");
|
LOGGER.warn("No module info found.");
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -134,11 +165,17 @@ public class ModuleManager {
|
||||||
return moduleInfo;
|
return moduleInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called to shutdown all modules.
|
||||||
|
*
|
||||||
|
*/
|
||||||
@Transactional(Transactional.TxType.REQUIRED)
|
@Transactional(Transactional.TxType.REQUIRED)
|
||||||
public void shutdownModules() {
|
public void shutdownModules() {
|
||||||
LOGGER.info("Shutting down modules...");
|
LOGGER.info("Shutting down modules...");
|
||||||
System.out.println("Shutting down modules...");
|
System.out.println("Shutting down modules...");
|
||||||
for (final TreeNode node : moduleNodes) {
|
for (final TreeNode node : moduleNodes) {
|
||||||
|
//Create a shutdown event instance and call the shutdown method of
|
||||||
|
//the module.
|
||||||
final ShutdownEvent shutdownEvent = new ShutdownEvent();
|
final ShutdownEvent shutdownEvent = new ShutdownEvent();
|
||||||
shutdownEvent.setEntityManager(entityManager);
|
shutdownEvent.setEntityManager(entityManager);
|
||||||
node.getModule().shutdown(shutdownEvent);
|
node.getModule().shutdown(shutdownEvent);
|
||||||
|
|
@ -147,12 +184,13 @@ public class ModuleManager {
|
||||||
System.out.println("Modules shut down.");
|
System.out.println("Modules shut down.");
|
||||||
|
|
||||||
System.out.println("Checking for modules to uninstall...");
|
System.out.println("Checking for modules to uninstall...");
|
||||||
|
//Check for modules to uninstall
|
||||||
for (final TreeNode node : moduleNodes) {
|
for (final TreeNode node : moduleNodes) {
|
||||||
System.out.printf("Checking status of module %s%n",
|
System.out.printf("Checking status of module %s%n",
|
||||||
node.getModule().getClass().getName());
|
node.getModule().getClass().getName());
|
||||||
final InstalledModule installedModule = entityManager.find(
|
final InstalledModule installedModule = entityManager.find(
|
||||||
InstalledModule.class, node.
|
InstalledModule.class, node.
|
||||||
getModule().getClass().getName().hashCode());
|
getModule().getClass().getName().hashCode());
|
||||||
LOGGER.info("Status of module {} ({}): {}",
|
LOGGER.info("Status of module {} ({}): {}",
|
||||||
node.getModuleInfo().getModuleName(),
|
node.getModuleInfo().getModuleName(),
|
||||||
node.getModule().getClass().getName(),
|
node.getModule().getClass().getName(),
|
||||||
|
|
@ -165,20 +203,27 @@ public class ModuleManager {
|
||||||
node.getModule().getClass().getName());
|
node.getModule().getClass().getName());
|
||||||
|
|
||||||
if (ModuleStatus.UNINSTALL.equals(installedModule.getStatus())) {
|
if (ModuleStatus.UNINSTALL.equals(installedModule.getStatus())) {
|
||||||
|
//If the current module is marked for uninstall check if there
|
||||||
|
//modules which depend on the module. If there are now module
|
||||||
|
//depending on the module create an uninstall event instance
|
||||||
|
//and call the uninstall method of the module.
|
||||||
System.out.printf("Module %s is scheduled for uninstall...%n",
|
System.out.printf("Module %s is scheduled for uninstall...%n",
|
||||||
node.getModuleInfo().getModuleName());
|
node.getModuleInfo().getModuleName());
|
||||||
if (node.getDependentModules().isEmpty()) {
|
if (node.getDependentModules().isEmpty()) {
|
||||||
System.out.
|
System.out.
|
||||||
printf("Calling uninstall method of module %s...%n",
|
printf("Calling uninstall method of module %s...%n",
|
||||||
node.getModuleInfo().getModuleName());
|
node.getModuleInfo().getModuleName());
|
||||||
final UnInstallEvent unInstallEvent = new UnInstallEvent();
|
final UnInstallEvent unInstallEvent = new UnInstallEvent();
|
||||||
unInstallEvent.setEntityManager(entityManager);
|
unInstallEvent.setEntityManager(entityManager);
|
||||||
node.getModule().uninstall(null);
|
node.getModule().uninstall(null);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
//If there are module depending on the module marked for
|
||||||
|
//uninstall reset the status of the module to installed.
|
||||||
|
//Normally this should never happen but just in case...
|
||||||
System.out.printf("There are other modules depending on "
|
System.out.printf("There are other modules depending on "
|
||||||
+ "module %s. Module can't be "
|
+ "module %s. Module can't be "
|
||||||
+ "uninstalled. Depending modules:%n",
|
+ "uninstalled. Depending modules:%n",
|
||||||
node.getModuleInfo().getModuleName());
|
node.getModuleInfo().getModuleName());
|
||||||
for (final TreeNode dependent : node.getDependentModules()) {
|
for (final TreeNode dependent : node.getDependentModules()) {
|
||||||
System.out.printf("\t%s%n",
|
System.out.printf("\t%s%n",
|
||||||
|
|
@ -190,8 +235,8 @@ public class ModuleManager {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
System.out.printf(
|
System.out.printf(
|
||||||
"Module %s is *not* scheduled for uninstall.%n",
|
"Module %s is *not* scheduled for uninstall.%n",
|
||||||
node.getModuleInfo().getModuleName());
|
node.getModuleInfo().getModuleName());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,16 +19,20 @@
|
||||||
package org.libreccm.modules;
|
package org.libreccm.modules;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Thrown by the {@link ModuleManager} if something goes wrong.
|
||||||
*
|
*
|
||||||
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
||||||
*/
|
*/
|
||||||
public class ModuleManagerException extends RuntimeException {
|
public class ModuleManagerException extends RuntimeException {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1426939919890655697L;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new instance of <code>ModuleManagerException</code> without
|
* Creates a new instance of <code>ModuleManagerException</code> without
|
||||||
* detail message.
|
* detail message.
|
||||||
*/
|
*/
|
||||||
public ModuleManagerException() {
|
public ModuleManagerException() {
|
||||||
|
super();
|
||||||
//Nothing
|
//Nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,15 +19,31 @@
|
||||||
package org.libreccm.modules;
|
package org.libreccm.modules;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Annotation for describing a dependency relation between a module and another
|
||||||
|
* module.
|
||||||
*
|
*
|
||||||
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
||||||
*/
|
*/
|
||||||
public @interface RequiredModule {
|
public @interface RequiredModule {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The module class required by the module.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
Class<? extends CcmModule> module();
|
Class<? extends CcmModule> module();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The minimal version required by the module.
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
String minVersion() default "";
|
String minVersion() default "";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The maximum version required by the module.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
String maxVersion() default "";
|
String maxVersion() default "";
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,9 @@
|
||||||
package org.libreccm.modules;
|
package org.libreccm.modules;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Event object for the module shutdown.
|
||||||
*
|
*
|
||||||
|
* @see ModuleEvent
|
||||||
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
||||||
*/
|
*/
|
||||||
public class ShutdownEvent extends ModuleEvent {
|
public class ShutdownEvent extends ModuleEvent {
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,6 @@ import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
|
||||||
* Represents a node in the dependency tree. This class is <strong>not</strong>
|
* Represents a node in the dependency tree. This class is <strong>not</strong>
|
||||||
* part of the public API.
|
* part of the public API.
|
||||||
*
|
*
|
||||||
|
|
@ -32,11 +31,26 @@ import java.util.Objects;
|
||||||
*/
|
*/
|
||||||
final class TreeNode {
|
final class TreeNode {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The module class of the module represented by this node.
|
||||||
|
*/
|
||||||
private CcmModule module;
|
private CcmModule module;
|
||||||
|
/**
|
||||||
|
* The module info for the module.
|
||||||
|
*/
|
||||||
private ModuleInfo moduleInfo;
|
private ModuleInfo moduleInfo;
|
||||||
|
/**
|
||||||
|
* The modules depending on the module represented by this tree node.
|
||||||
|
*/
|
||||||
private List<TreeNode> dependentModules;
|
private List<TreeNode> dependentModules;
|
||||||
|
/**
|
||||||
|
* The modules the module represented by this tree node depends on.
|
||||||
|
*/
|
||||||
private List<TreeNode> dependsOn;
|
private List<TreeNode> dependsOn;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new empty tree node.
|
||||||
|
*/
|
||||||
public TreeNode() {
|
public TreeNode() {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
|
|
@ -44,6 +58,12 @@ final class TreeNode {
|
||||||
dependsOn = new ArrayList<>();
|
dependsOn = new ArrayList<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a tree node for the provided module. Them module info for the
|
||||||
|
* module is loaded automatically.
|
||||||
|
*
|
||||||
|
* @param module
|
||||||
|
*/
|
||||||
public TreeNode(final CcmModule module) {
|
public TreeNode(final CcmModule module) {
|
||||||
this();
|
this();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,9 @@
|
||||||
package org.libreccm.modules;
|
package org.libreccm.modules;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Event object for the module deinstallation.
|
||||||
*
|
*
|
||||||
|
* @see ModuleEvent
|
||||||
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
||||||
*/
|
*/
|
||||||
public class UnInstallEvent extends ModuleEvent {
|
public class UnInstallEvent extends ModuleEvent {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue