de.viadee.bpm.vPAV.Runner Maven / Gradle / Ivy
Show all versions of viadeeProcessApplicationValidator Show documentation
/**
* BSD 3-Clause License
*
* Copyright © 2019, viadee Unternehmensberatung AG
* 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 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.*;
import de.viadee.bpm.vPAV.processing.BpmnModelDispatcher;
import de.viadee.bpm.vPAV.processing.ProcessVariablesScanner;
import de.viadee.bpm.vPAV.processing.code.flow.BpmnElement;
import de.viadee.bpm.vPAV.processing.dataflow.DataFlowRule;
import de.viadee.bpm.vPAV.processing.model.data.CheckerIssue;
import de.viadee.bpm.vPAV.processing.model.data.ModelDispatchResult;
import de.viadee.bpm.vPAV.processing.model.data.ProcessVariable;
import java.io.*;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;
public class Runner {
private static Logger logger = Logger.getLogger(Runner.class.getName());
private FileScanner fileScanner;
private ProcessVariablesScanner 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
*
* @param javaScanPath Main entry path. Normally src/main/java
*/
public void viadeeProcessApplicationValidator(final String javaScanPath) {
// 1
rules = readConfig();
// 2
setFileScanner(new FileScanner(rules, javaScanPath));
// 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, 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).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);
RuntimeConfig.getInstance().retrieveLocale(rules);
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
*/
protected Map> mergeRuleSet(final Map> parentRules,
final Map> childRules) {
final Map> finalRules = new HashMap<>();
finalRules.putAll(parentRules);
for (Map.Entry> entry : childRules.entrySet()) {
if (finalRules.containsKey(entry.getKey())) {
finalRules.get(entry.getKey()).putAll(entry.getValue());
} else {
finalRules.put(entry.getKey(), entry.getValue());
}
}
return finalRules;
}
/**
* Initializes the variableScanner to scan and read outer process variables with
* the current javaResources
*
* @param rules Rules defined in ruleSet
*/
protected void getProcessVariables(final Map> rules) {
if (oneCheckerIsActive(rules, "ProcessVariablesModelChecker")
|| oneCheckerIsActive(rules, "ProcessVariablesNameConventionChecker")
|| oneCheckerIsActive(rules, "DataFlowChecker")) {
variableScanner = new ProcessVariablesScanner(getFileScanner().getJavaResourcesFileInputStream());
readOuterProcessVariables(variableScanner);
setCheckProcessVariables(true);
} else {
setCheckProcessVariables(false);
}
}
public boolean oneCheckerIsActive(final Map> rules, String name) {
for (Rule r : rules.get(name).values()) {
if (r.isActive()) {
return true;
}
}
return 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 {
try {
final JsOutputWriter jsOutputWriter = new JsOutputWriter();
jsOutputWriter.prepareMaps(this.getWrongCheckersMap(), this.getIgnoredIssuesMap(), this.getModelPath());
jsOutputWriter.write(filteredIssues);
jsOutputWriter.writeVars(elements, processVariables);
} 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 validation folder
*/
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 (ConfigConstants.getInstance().isHtmlOutputEnabled(rules.get(ConfigConstants.CREATE_OUTPUT_RULE))) {
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 RuntimeException 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<>(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<>();
try (FileReader fileReader = new FileReader(ConfigConstants.IGNORE_FILE_OLD)) {
readIssues(ignoredIssuesMap, ignoredIssues, fileReader);
logger.warning("Usage of .ignoreIssues is deprecated. Please use ignoreIssues.txt to whitelist issues.");
} catch (IOException ex) {
logger.info(ex.getMessage());
}
try (FileReader fileReader = new FileReader(filePath)) {
readIssues(ignoredIssuesMap, ignoredIssues, fileReader);
} catch (IOException ex) {
logger.info(ex.getMessage());
}
return ignoredIssues;
}
/**
* Reads the file and appends issues to the map of ignored issues
*
* @param ignoredIssuesMap Map of issues to be ignored
* @param ignoredIssues Collection of ignored issues
* @param fileReader FileReader
*/
private void readIssues(final Map ignoredIssuesMap, final Collection ignoredIssues,
final FileReader fileReader) {
try (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();
}
} catch (IOException e) {
logger.info(e.getMessage());
}
}
/**
* Check consistency of all models
*
* @param rules all rules of ruleSet.xml
* @param fileScanner fileScanner
* @param variableScanner variableScanner
* @param dataFlowRules dataFlowRules
* @return foundIssues ConfigItem not found
*/
private Collection checkModels(final Map> rules, final FileScanner fileScanner,
final ProcessVariablesScanner 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 processDefinition
* processDefinition
* @param fileScanner
* fileScanner
* @param variableScanner
* variableScanner
* @return modelIssues
*/
private Collection checkModel(final Map> rules, final String processDefinition,
final FileScanner fileScanner, final ProcessVariablesScanner variableScanner,
Collection dataFlowRules) {
BpmnModelDispatcher bpmnModelDispatcher = new BpmnModelDispatcher();
ModelDispatchResult dispatchResult;
File bpmnfile = null;
String basepath = ConfigConstants.getInstance().getBasepath();
if (basepath.startsWith("file:/")) {
// Convert URI
try {
bpmnfile = new File(new URI(ConfigConstants.getInstance().getBasepath() + processDefinition));
} catch (URISyntaxException e) {
logger.log(Level.SEVERE, "URI of basedirectory seems to be malformed.", e);
}
} else {
bpmnfile = new File(basepath + processDefinition);
}
if (variableScanner != null) {
dispatchResult = bpmnModelDispatcher.dispatchWithVariables(fileScanner, bpmnfile, fileScanner.getDecisionRefToPathMap(),
fileScanner.getProcessIdToPathMap(), variableScanner, dataFlowRules,
fileScanner.getResourcesNewestVersions(), rules);
} else {
dispatchResult = bpmnModelDispatcher.dispatchWithoutVariables(bpmnfile, fileScanner.getDecisionRefToPathMap(),
fileScanner.getProcessIdToPathMap(), fileScanner.getResourcesNewestVersions(), rules);
}
elements.addAll(dispatchResult.getBpmnElements());
processVariables.addAll(dispatchResult.getProcessVariables());
setWrongCheckersMap(bpmnModelDispatcher.getIncorrectCheckers());
return dispatchResult.getIssues();
}
/**
* @param ignoredIssuesMap Map of ignored issues
* @param issues Collection of issues
* @param row row of file
* @param prevLine Previous line
*/
private void 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);
}
}
}
/**
* Scan process variables in external classes, which are not referenced from
* model
*
* @param scanner OuterProcessVariablesScanner
*/
private void readOuterProcessVariables(final ProcessVariablesScanner scanner) {
scanner.scanProcessVariables();
}
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;
}
}