de.viadee.bpm.vPAV.Runner Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of viadeeProcessApplicationValidator Show documentation
Show all versions of viadeeProcessApplicationValidator Show documentation
The tool checks Camunda projects for consistency and discovers errors in process-driven applications.
Called as a Maven plugin or JUnit test, it discovers esp. inconsistencies of a given BPMN model in the classpath and the
sourcecode of an underlying java project, such as a delegate reference to a non-existing java class or a non-existing Spring bean.
/**
* BSD 3-Clause License
*
* Copyright © 2018, viadee Unternehmensberatung GmbH
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package de.viadee.bpm.vPAV;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Logger;
import de.viadee.bpm.vPAV.processing.dataflow.DataFlowRule;
import de.viadee.bpm.vPAV.processing.model.data.ProcessVariable;
import de.viadee.bpm.vPAV.processing.model.data.BpmnElement;
import de.viadee.bpm.vPAV.processing.model.data.ModelDispatchResult;
import de.viadee.bpm.vPAV.config.model.Rule;
import de.viadee.bpm.vPAV.config.reader.ConfigReaderException;
import de.viadee.bpm.vPAV.config.reader.XmlConfigReader;
import de.viadee.bpm.vPAV.constants.ConfigConstants;
import de.viadee.bpm.vPAV.output.IssueOutputWriter;
import de.viadee.bpm.vPAV.output.JsOutputWriter;
import de.viadee.bpm.vPAV.output.JsonOutputWriter;
import de.viadee.bpm.vPAV.output.OutputWriterException;
import de.viadee.bpm.vPAV.output.RuleSetOutputWriter;
import de.viadee.bpm.vPAV.output.XmlOutputWriter;
import de.viadee.bpm.vPAV.processing.BpmnModelDispatcher;
import de.viadee.bpm.vPAV.processing.ConfigItemNotFoundException;
import de.viadee.bpm.vPAV.processing.model.data.CheckerIssue;
public class Runner {
private static Logger logger = Logger.getLogger(Runner.class.getName());
private FileScanner fileScanner;
private OuterProcessVariablesScanner variableScanner;
private Collection issues;
private Collection filteredIssues;
private Map rules = new HashMap();
private Map ignoredIssuesMap = new HashMap();
private Map fileMapping = createFileFolderMapping();
private Map wrongCheckersMap = new HashMap<>();
private ArrayList allOutputFilesArray = createAllOutputFilesArray();
private Collection elements = new ArrayList<>();
private Collection processVariables = new ArrayList<>();
private Collection dataFlowRules = new ArrayList<>();
private boolean checkProcessVariables = false;
/**
* Main method which represents lifecycle of the validation process Calls main
* functions
*/
public void viadeeProcessApplicationValidator() {
// 0
processVariables = new ArrayList<>();
elements = new ArrayList<>();
// 1
rules = readConfig();
// 2
scanClassPath(rules);
// 3
getProcessVariables(rules);
// 4
createIssues(rules, dataFlowRules);
// 5
removeIgnoredIssues();
// 6
writeOutput(filteredIssues, elements, processVariables);
// 7
copyFiles();
logger.info("BPMN validation successfully completed");
}
/**
* 1) If local_ruleSet doesn't exist, then load default_RuleSet 2) If
* local_ruleSet exist and parent is deactivated then override deactivatedRules
* with local_ruleSet 3) If local_ruleSet exist and parent is activated then
* override deactivatedRules with parent_ruleSet and then override with
* local_ruleSet
*
* write effectiveRuleSet to vPAV folder
*
* @return Map(String, Rule) ruleSet
*/
private Map readConfig() {
prepareOutputFolder();
rules = new XmlConfigReader().getDeactivatedRuleSet();
final RuleSetOutputWriter ruleSetOutputWriter = new RuleSetOutputWriter();
try {
if (new File(ConfigConstants.TEST_BASEPATH + ConfigConstants.RULESET).exists()) {
Map localRule = new XmlConfigReader().read(ConfigConstants.RULESET);
if (localRule.containsKey(ConfigConstants.HASPARENTRULESET)
&& localRule.get(ConfigConstants.HASPARENTRULESET).isActive()) {
rules = mergeRuleSet(rules, new XmlConfigReader().read(ConfigConstants.RULESETPARENT));
rules = mergeRuleSet(rules, localRule);
} else {
rules = mergeRuleSet(rules, localRule);
}
} else {
rules = new XmlConfigReader().read(ConfigConstants.RULESETDEFAULT);
}
ruleSetOutputWriter.write(rules);
RuntimeConfig.getInstance().addActiveRules(rules);
} catch (final ConfigReaderException | OutputWriterException e) {
throw new RuntimeException(e);
}
rules.remove(ConfigConstants.HASPARENTRULESET);
try {
RuntimeConfig.getInstance().retrieveLocale(rules);
} catch (MalformedURLException e) {
logger.warning("Could not read language files. No localization available");
}
return rules;
}
/**
* Delete old output and create new output folder
*/
private void prepareOutputFolder() {
deleteFiles();
createvPAVFolder();
try {
Files.createDirectory(Paths.get(ConfigConstants.JS_FOLDER));
Files.createDirectory(Paths.get(ConfigConstants.CSS_FOLDER));
Files.createDirectory(Paths.get(ConfigConstants.IMG_FOLDER));
} catch (IOException e) {
logger.warning("Could not create either output folder for JS, CSS or IMG");
}
}
/**
* merges ruleSets according to inheritance hierarchy (Deactivated < global <
* default < local)
*
* @param parentRules
* Basis RuleSet which will be overwritten
* @param childRules
* New RuleSet
* @return Map(String, Rule) finalRules merged ruleSet
*/
private Map mergeRuleSet(final Map parentRules,
final Map childRules) {
final Map finalRules = new HashMap<>();
finalRules.putAll(parentRules);
finalRules.putAll(childRules);
return finalRules;
}
/**
* Initializes the fileScanner with the current set of rules
*
* @param rules
* Map of rules
*/
private void scanClassPath(Map rules) {
setFileScanner(new FileScanner(rules));
}
/**
* Initializes the variableScanner to scan and read outer process variables with
* the current javaResources
*/
private void getProcessVariables(Map rules) {
if (rules.get("ProcessVariablesModelChecker").isActive() ||
rules.get("ProcessVariablesNameConventionChecker").isActive() ||
rules.get("DataFlowChecker").isActive()) {
variableScanner = new OuterProcessVariablesScanner(getFileScanner().getJavaResourcesFileInputStream());
readOuterProcessVariables(variableScanner);
setCheckProcessVariables(true);
} else {
setCheckProcessVariables(false);
}
}
/**
* Creates the list of issues found for a given model and ruleSet Throws a
* RuntimeException if errors are found, so automated builds in a CI/CD pipeline
* will fail
*
* @param rules
* Map of rules
* @throws RuntimeException
* Config item couldn't be read
*/
private void createIssues(Map rules,
Collection dataFlowRules) throws RuntimeException {
issues = checkModels(rules, getFileScanner(), variableScanner, dataFlowRules);
}
/**
* Removes whitelisted issues from the list of issues found
*
* @throws RuntimeException
* Ignored issues couldn't be read successfully
*/
private void removeIgnoredIssues() throws RuntimeException {
filteredIssues = filterIssues(issues);
}
/**
* Write output files (xml / json / js)
*
* @param filteredIssues
* List of filteredIssues
* @param elements
* List of BPMN element across all models
* @param processVariables
* List of process variables across all models
* @throws RuntimeException
* Abort if writer can not be instantiated
*/
private void writeOutput(final Collection filteredIssues,
final Collection elements,
final Collection processVariables) throws RuntimeException {
if (filteredIssues.size() > 0) {
final IssueOutputWriter xmlOutputWriter = new XmlOutputWriter();
final IssueOutputWriter jsonOutputWriter = new JsonOutputWriter();
final JsOutputWriter jsOutputWriter = new JsOutputWriter();
try {
jsOutputWriter.prepareMaps(this.getWrongCheckersMap(), this.getIgnoredIssuesMap(), this.getModelPath());
xmlOutputWriter.write(filteredIssues);
jsonOutputWriter.write(filteredIssues);
jsOutputWriter.write(filteredIssues);
jsOutputWriter.writeVars(elements, processVariables);
} catch (final OutputWriterException e) {
throw new RuntimeException("Output couldn't be written", e);
}
} else {
final IssueOutputWriter jsOutputWriter = new JsOutputWriter();
try {
jsOutputWriter.write(filteredIssues);
} catch (OutputWriterException e) {
throw new RuntimeException("JavaScript File couldn't be written", e);
}
}
}
/**
* Create vPAV folder
*
*/
private void createvPAVFolder() {
File vPavDir = new File(ConfigConstants.VALIDATION_FOLDER);
if (!vPavDir.exists()) {
boolean success = vPavDir.mkdirs();
if (!success) {
throw new RuntimeException("vPAV directory does not exist and could not be created");
}
}
}
/**
* Delete files from destinations
*
* @param destinations
* List of destinations who will be deleted
*/
private void deleteFiles() {
File index = new File(ConfigConstants.VALIDATION_FOLDER);
if (index.exists()) {
String[] entries = index.list();
for (String entry : entries) {
File currentFile = new File(index.getPath(), entry);
if (currentFile.isDirectory()) {
String[] subEntries = currentFile.list();
for (String subentry : subEntries) {
File file = new File(currentFile.getPath(), subentry);
file.delete();
}
}
currentFile.delete();
}
}
}
/**
* Copies all necessary files and deletes outputFiles
*
* @throws RuntimeException
* Files couldn't be copied
*/
private void copyFiles() throws RuntimeException {
ArrayList outputFiles = new ArrayList();
for (String file : allOutputFilesArray)
outputFiles.add(Paths.get(fileMapping.get(file), file));
if (rules.get(ConfigConstants.CREATE_OUTPUT_RULE).isActive()) {
for (String file : allOutputFilesArray)
copyFileToVPAVFolder(file);
}
}
/**
* Creates ArrayList to hold output files
*
* @return ArrayList allFiles
*/
private ArrayList createAllOutputFilesArray() {
ArrayList allFiles = new ArrayList();
allFiles.add("bootstrap.min.js");
allFiles.add("bpmn-navigated-viewer.js");
allFiles.add("bpmn.io.viewer.app.js");
allFiles.add("jquery-3.2.1.min.js");
allFiles.add("popper.min.js");
allFiles.add("infoPOM.js");
allFiles.add("download.js");
allFiles.add("bootstrap.min.css");
allFiles.add("viadee.css");
allFiles.add("MarkerStyle.css");
allFiles.add("vPAV.png");
allFiles.add("viadee_weiss.png");
allFiles.add("GitHub.png");
allFiles.add("error.png");
allFiles.add("warning.png");
allFiles.add("info.png");
allFiles.add("success.png");
allFiles.add("dl_button.png");
allFiles.add("validationResult.html");
return allFiles;
}
/**
* Creates Map for files and corresponding folders
*
* @return Map fMap
*/
private Map createFileFolderMapping() {
Map fMap = new HashMap();
fMap.put("bootstrap.min.js", ConfigConstants.JS_FOLDER);
fMap.put("bpmn-navigated-viewer.js", ConfigConstants.JS_FOLDER);
fMap.put("bpmn.io.viewer.app.js", ConfigConstants.JS_FOLDER);
fMap.put("jquery-3.2.1.min.js", ConfigConstants.JS_FOLDER);
fMap.put("popper.min.js", ConfigConstants.JS_FOLDER);
fMap.put("infoPOM.js", ConfigConstants.JS_FOLDER);
fMap.put("download.js", ConfigConstants.JS_FOLDER);
fMap.put("bootstrap.min.css", ConfigConstants.CSS_FOLDER);
fMap.put("viadee.css", ConfigConstants.CSS_FOLDER);
fMap.put("MarkerStyle.css", ConfigConstants.CSS_FOLDER);
fMap.put("vPAV.png", ConfigConstants.IMG_FOLDER);
fMap.put("viadee_weiss.png", ConfigConstants.IMG_FOLDER);
fMap.put("GitHub.png", ConfigConstants.IMG_FOLDER);
fMap.put("error.png", ConfigConstants.IMG_FOLDER);
fMap.put("warning.png", ConfigConstants.IMG_FOLDER);
fMap.put("info.png", ConfigConstants.IMG_FOLDER);
fMap.put("success.png", ConfigConstants.IMG_FOLDER);
fMap.put("dl_button.png", ConfigConstants.IMG_FOLDER);
fMap.put("validationResult.html", ConfigConstants.VALIDATION_FOLDER);
return fMap;
}
/**
* Copies files to vPAV folder
*
* @param file
* File who will be copied to vPAV folder
* @throws RuntimeException
* Files couldn't be written
*/
private void copyFileToVPAVFolder(String file) throws RuntimeException {
InputStream source = Runner.class.getClassLoader().getResourceAsStream(file);
Path destination = Paths.get(fileMapping.get(file) + file);
try {
Files.copy(source, destination, StandardCopyOption.REPLACE_EXISTING);
} catch (IOException e) {
throw new RuntimeException("Files couldn't be written");
}
}
/**
* filter issues based on black list
*
* @param issues
* all found issues
* @return filtered issues
* @throws IOException
* Ignored issues couldn't be read successfully
*/
private Collection filterIssues(final Collection issues)
throws RuntimeException {
Collection filteredIssues;
try {
filteredIssues = getFilteredIssues(issues);
} catch (final IOException e) {
throw new RuntimeException("Ignored issues couldn't be read successfully", e);
}
Collections.sort((List) filteredIssues);
return filteredIssues;
}
/**
* remove false positives from issue collection
*
* @param issues
* collection of issues
* @return filteredIssues
* @throws IOException
* ignoreIssues file doesn't exist
*/
private Collection getFilteredIssues(Collection issues) throws IOException {
// all issues
final HashMap issuesMap = new HashMap<>();
// transform Collection into a HashMap
for (final CheckerIssue issue : issues) {
if (!issuesMap.containsKey(issue.getId())) {
issuesMap.put(issue.getId(), issue);
}
}
// all issues to be ignored
final Collection ignoredIssues = collectIgnoredIssues(ConfigConstants.IGNORE_FILE);
final HashMap filteredIssues = new HashMap<>();
filteredIssues.putAll(issuesMap);
// remove issues that are listed in ignore file
for (Map.Entry entry : issuesMap.entrySet()) {
if (ignoredIssues.contains(entry.getKey())) {
filteredIssues.remove(entry.getKey());
}
}
// transform back into collection
final Collection finalFilteredIssues = new ArrayList();
for (Map.Entry entry : filteredIssues.entrySet()) {
finalFilteredIssues.add(entry.getValue());
}
return finalFilteredIssues;
}
/**
* Read issue ids, that should be ignored
*
* Assumption: Each row is an issue id
*
* @param filePath
* Path of ignoredIssues-file
* @return issue ids
* @throws IOException
* ignoreIssues file doesn't exist
*/
private Collection collectIgnoredIssues(final String filePath) throws IOException {
final Map ignoredIssuesMap = getIgnoredIssuesMap();
final Collection ignoredIssues = new ArrayList();
FileReader fileReader = createFileReader(filePath);
if (fileReader != null) {
final BufferedReader bufferedReader = new BufferedReader(fileReader);
String zeile = bufferedReader.readLine();
String prevLine = zeile;
while (zeile != null) {
addIgnoredIssue(ignoredIssuesMap, ignoredIssues, zeile, prevLine);
prevLine = zeile;
zeile = bufferedReader.readLine();
}
bufferedReader.close();
fileReader.close();
}
return ignoredIssues;
}
/**
*
* @param filePath
* Path to ignoreIssues file
* @return FileReader ignoreIssue file
*/
private FileReader createFileReader(final String filePath) {
FileReader fileReader = null;
try {
fileReader = new FileReader(ConfigConstants.IGNORE_FILE_OLD);
if (fileReader != null) {
logger.warning(
"Usage of .ignoreIssues is deprecated. Please use ignoreIssues.txt to whitelist issues.");
}
} catch (final FileNotFoundException ex) {
}
try {
fileReader = new FileReader(filePath);
} catch (final FileNotFoundException ex) {
logger.info(ex.getMessage());
}
return fileReader;
}
/**
* Check consistency of all models
*
* @param rules
* all rules of ruleSet.xml
* @param beanMapping
* beanMapping if spring context is available
* @param fileScanner
* fileScanner
* @param variableScanner
* variablenScanner
* @return foundIssues
* @throws ConfigItemNotFoundException
* ConfigItem not found
*/
private Collection checkModels(final Map rules, final FileScanner fileScanner,
final OuterProcessVariablesScanner variableScanner, Collection dataFlowRules) throws RuntimeException {
final Collection issues = new ArrayList();
for (final String pathToModel : fileScanner.getProcessdefinitions()) {
issues.addAll(checkModel(rules, pathToModel, fileScanner, variableScanner, dataFlowRules));
}
return issues;
}
/**
* Check consistency of a model
*
* @param rules
* all rules of ruleSet.xml
* @param beanMapping
* beanMapping if spring context is available
* @param processdef
* processdefintion
* @param fileScanner
* fileScanner
* @param variableScanner
* variableScanner
* @return modelIssues
*/
private Collection checkModel(final Map rules, final String processdef,
final FileScanner fileScanner, final OuterProcessVariablesScanner variableScanner,
Collection dataFlowRules) {
BpmnModelDispatcher bpmnModelDispatcher = new BpmnModelDispatcher();
ModelDispatchResult dispatchResult;
if (variableScanner != null) {
dispatchResult = bpmnModelDispatcher.dispatchWithVariables(new File(ConfigConstants.BASEPATH + processdef),
fileScanner.getDecisionRefToPathMap(), fileScanner.getProcessIdToPathMap(),
variableScanner.getMessageIdToVariableMap(), variableScanner.getProcessIdToVariableMap(),
dataFlowRules, fileScanner.getResourcesNewestVersions(), rules);
} else {
dispatchResult = bpmnModelDispatcher.dispatchWithoutVariables(
new File(ConfigConstants.BASEPATH + processdef), fileScanner.getDecisionRefToPathMap(),
fileScanner.getProcessIdToPathMap(), fileScanner.getResourcesNewestVersions(), rules);
}
elements.addAll(dispatchResult.getBpmnElements());
processVariables.addAll(dispatchResult.getProcessVariables());
setWrongCheckersMap(bpmnModelDispatcher.getIncorrectCheckers());
return dispatchResult.getIssues();
}
/**
* Scan process variables in external classes, which are not referenced from
* model
*
* @param scanner
* OuterProcessVariablesScanner
* @throws IOException
* Outer process variables couldn't be read
*/
private void readOuterProcessVariables(final OuterProcessVariablesScanner scanner) throws RuntimeException {
try {
scanner.scanProcessVariables();
} catch (final IOException e) {
throw new RuntimeException("Outer process variables couldn't be read: " + e.getMessage());
}
}
/**
* Add ignored issue
*
* @param issues
* Collection of issues
* @param row
* row of file
*/
private Map addIgnoredIssue(final Map ignoredIssuesMap,
final Collection issues, final String row, final String prevLine) {
if (row != null && !row.isEmpty()) {
if (!row.trim().startsWith("#")) {
ignoredIssuesMap.put(row, prevLine);
issues.add(row);
}
}
return ignoredIssuesMap;
}
public Set getModelPath() {
return getFileScanner().getProcessdefinitions();
}
public Collection getfilteredIssues() {
return filteredIssues;
}
public Map getIgnoredIssuesMap() {
return ignoredIssuesMap;
}
public void setIgnoredIssuesMap(Map ignoredIssuesMap) {
this.ignoredIssuesMap = ignoredIssuesMap;
}
public boolean isCheckProcessVariables() {
return checkProcessVariables;
}
public void setCheckProcessVariables(boolean checkProcessVariables) {
this.checkProcessVariables = checkProcessVariables;
}
public void setDataFlowRules(Collection dataFlowRules) {
this.dataFlowRules = dataFlowRules;
}
public FileScanner getFileScanner() {
return fileScanner;
}
public void setFileScanner(FileScanner fileScanner) {
this.fileScanner = fileScanner;
}
public Map getWrongCheckersMap() {
return wrongCheckersMap;
}
public void setWrongCheckersMap(Map wrongCheckersMap) {
this.wrongCheckersMap = wrongCheckersMap;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy