diff --git a/modules/flowable-app-engine/src/main/java/org/flowable/app/engine/impl/db/AppDbSchemaManager.java b/modules/flowable-app-engine/src/main/java/org/flowable/app/engine/impl/db/AppDbSchemaManager.java index 2d11324c466..1af893bc553 100644 --- a/modules/flowable-app-engine/src/main/java/org/flowable/app/engine/impl/db/AppDbSchemaManager.java +++ b/modules/flowable-app-engine/src/main/java/org/flowable/app/engine/impl/db/AppDbSchemaManager.java @@ -17,7 +17,7 @@ import org.apache.commons.lang3.StringUtils; import org.flowable.app.engine.AppEngine; import org.flowable.app.engine.impl.util.CommandContextUtil; -import org.flowable.common.engine.impl.AbstractEngineConfiguration; +import org.flowable.common.engine.impl.db.EngineSchemaManagerLockConfiguration; import org.flowable.common.engine.impl.db.EngineSqlScriptBasedDbSchemaManager; public class AppDbSchemaManager extends EngineSqlScriptBasedDbSchemaManager { @@ -31,7 +31,7 @@ public class AppDbSchemaManager extends EngineSqlScriptBasedDbSchemaManager { ); public AppDbSchemaManager() { - super("app"); + super("app", new EngineSchemaManagerLockConfiguration(CommandContextUtil::getAppEngineConfiguration)); } @Override @@ -58,11 +58,6 @@ protected String getEngineTableName() { protected String getChangeLogTableName() { return "ACT_APP_DATABASECHANGELOG"; } - - @Override - protected String getChangeLogTablePrefixName() { - return "ACT_APP"; - } @Override protected String getDbVersionForChangelogVersion(String changeLogVersion) { @@ -72,10 +67,6 @@ protected String getDbVersionForChangelogVersion(String changeLogVersion) { return "6.3.0.1"; } - @Override - protected AbstractEngineConfiguration getEngineConfiguration() { - return CommandContextUtil.getAppEngineConfiguration(); - } @Override protected String getResourcesRootDirectory() { return "org/flowable/app/db/"; diff --git a/modules/flowable-bpmn-converter/src/main/java/org/flowable/bpmn/converter/BaseBpmnXMLConverter.java b/modules/flowable-bpmn-converter/src/main/java/org/flowable/bpmn/converter/BaseBpmnXMLConverter.java index 673822dfff0..d5430076ab7 100644 --- a/modules/flowable-bpmn-converter/src/main/java/org/flowable/bpmn/converter/BaseBpmnXMLConverter.java +++ b/modules/flowable-bpmn-converter/src/main/java/org/flowable/bpmn/converter/BaseBpmnXMLConverter.java @@ -164,13 +164,13 @@ public void convertToBpmnModel(XMLStreamReader xtr, BpmnModel model, Process act } } - public void convertToXML(XMLStreamWriter xtw, BaseElement baseElement, BpmnModel model) throws Exception { + public void convertToXML(XMLStreamWriter xtw, BaseElement baseElement, BpmnModel model, BpmnXMLConverterOptions options) throws Exception { xtw.writeStartElement(getXMLElementName()); boolean didWriteExtensionStartElement = false; writeDefaultAttribute(ATTRIBUTE_ID, baseElement.getId(), xtw); if (baseElement instanceof FlowElement) { String name = ((FlowElement) baseElement).getName(); - if (!BpmnXMLUtil.containsNewLine(name)) { + if (!(options.isSaveElementNameWithNewLineInExtensionElement() && BpmnXMLUtil.containsNewLine(name))) { writeDefaultAttribute(ATTRIBUTE_NAME, name, xtw); } } @@ -226,7 +226,9 @@ public void convertToXML(XMLStreamWriter xtw, BaseElement baseElement, BpmnModel xtw.writeEndElement(); } - didWriteExtensionStartElement = BpmnXMLUtil.writeElementNameExtensionElement(flowElement, didWriteExtensionStartElement, xtw); + if (options.isSaveElementNameWithNewLineInExtensionElement()) { + didWriteExtensionStartElement = BpmnXMLUtil.writeElementNameExtensionElement(flowElement, didWriteExtensionStartElement, xtw); + } } didWriteExtensionStartElement = writeExtensionChildElements(baseElement, didWriteExtensionStartElement, xtw); diff --git a/modules/flowable-bpmn-converter/src/main/java/org/flowable/bpmn/converter/BpmnXMLConverter.java b/modules/flowable-bpmn-converter/src/main/java/org/flowable/bpmn/converter/BpmnXMLConverter.java index 80d1c8ad8d8..387c5572b4e 100644 --- a/modules/flowable-bpmn-converter/src/main/java/org/flowable/bpmn/converter/BpmnXMLConverter.java +++ b/modules/flowable-bpmn-converter/src/main/java/org/flowable/bpmn/converter/BpmnXMLConverter.java @@ -116,6 +116,8 @@ public class BpmnXMLConverter implements BpmnXMLConstants { protected List userTaskFormTypes; protected List startEventFormTypes; + protected BpmnXMLConverterOptions options = new BpmnXMLConverterOptions(); + protected BpmnEdgeParser bpmnEdgeParser = new BpmnEdgeParser(); protected BpmnShapeParser bpmnShapeParser = new BpmnShapeParser(); protected DefinitionsParser definitionsParser = new DefinitionsParser(); @@ -204,6 +206,14 @@ public static void addConverter(BaseBpmnXMLConverter converter, Class elementConverters = new HashMap<>(); + protected CmmnXmlConverterOptions options = new CmmnXmlConverterOptions(); + protected ClassLoader classloader; static { @@ -295,7 +297,7 @@ public byte[] convertToXML(CmmnModel model, String encoding) { Stage planModel = caseModel.getPlanModel(); - PlanItemDefinitionExport.writePlanItemDefinition(model, planModel, xtw); + PlanItemDefinitionExport.writePlanItemDefinition(model, planModel, xtw, options); // end case element xtw.writeEndElement(); @@ -719,6 +721,14 @@ protected void processAssociations(CmmnModel cmmnModel) { } } + public CmmnXmlConverterOptions getCmmnXmlConverterOptions() { + return options; + } + + public void setCmmnXmlConverterOptions(CmmnXmlConverterOptions options) { + this.options = options; + } + public void setClassloader(ClassLoader classloader) { this.classloader = classloader; } diff --git a/modules/flowable-cmmn-converter/src/main/java/org/flowable/cmmn/converter/CmmnXmlConverterOptions.java b/modules/flowable-cmmn-converter/src/main/java/org/flowable/cmmn/converter/CmmnXmlConverterOptions.java new file mode 100644 index 00000000000..c202df55178 --- /dev/null +++ b/modules/flowable-cmmn-converter/src/main/java/org/flowable/cmmn/converter/CmmnXmlConverterOptions.java @@ -0,0 +1,27 @@ +/* Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.flowable.cmmn.converter; + +public class CmmnXmlConverterOptions { + + protected boolean saveElementNameWithNewLineInExtensionElement = false; + + public boolean isSaveElementNameWithNewLineInExtensionElement() { + return saveElementNameWithNewLineInExtensionElement; + } + + public void setSaveElementNameWithNewLineInExtensionElement(boolean saveElementNameWithNewLineInExtensionElement) { + this.saveElementNameWithNewLineInExtensionElement = saveElementNameWithNewLineInExtensionElement; + } + +} diff --git a/modules/flowable-cmmn-converter/src/main/java/org/flowable/cmmn/converter/export/AbstractPlanItemDefinitionExport.java b/modules/flowable-cmmn-converter/src/main/java/org/flowable/cmmn/converter/export/AbstractPlanItemDefinitionExport.java index 3752c48978b..8be0a37b7f2 100644 --- a/modules/flowable-cmmn-converter/src/main/java/org/flowable/cmmn/converter/export/AbstractPlanItemDefinitionExport.java +++ b/modules/flowable-cmmn-converter/src/main/java/org/flowable/cmmn/converter/export/AbstractPlanItemDefinitionExport.java @@ -16,6 +16,7 @@ import org.apache.commons.lang3.StringUtils; import org.flowable.cmmn.converter.CmmnXmlConstants; +import org.flowable.cmmn.converter.CmmnXmlConverterOptions; import org.flowable.cmmn.converter.util.CmmnXmlUtil; import org.flowable.cmmn.model.CmmnModel; import org.flowable.cmmn.model.PlanItemDefinition; @@ -35,20 +36,20 @@ public abstract class AbstractPlanItemDefinitionExport { diff --git a/modules/flowable-cmmn-converter/src/main/java/org/flowable/cmmn/converter/export/CaseTaskExport.java b/modules/flowable-cmmn-converter/src/main/java/org/flowable/cmmn/converter/export/CaseTaskExport.java index ec6d7778395..669a5284b1d 100644 --- a/modules/flowable-cmmn-converter/src/main/java/org/flowable/cmmn/converter/export/CaseTaskExport.java +++ b/modules/flowable-cmmn-converter/src/main/java/org/flowable/cmmn/converter/export/CaseTaskExport.java @@ -15,6 +15,7 @@ import javax.xml.stream.XMLStreamWriter; import org.apache.commons.lang3.StringUtils; +import org.flowable.cmmn.converter.CmmnXmlConverterOptions; import org.flowable.cmmn.model.CaseTask; import org.flowable.cmmn.model.CmmnModel; @@ -46,8 +47,8 @@ protected void writePlanItemDefinitionSpecificAttributes(CaseTask caseTask, XMLS } @Override - protected void writePlanItemDefinitionBody(CmmnModel model, CaseTask caseTask, XMLStreamWriter xtw) throws Exception { - super.writePlanItemDefinitionBody(model, caseTask, xtw); + protected void writePlanItemDefinitionBody(CmmnModel model, CaseTask caseTask, XMLStreamWriter xtw, CmmnXmlConverterOptions options) throws Exception { + super.writePlanItemDefinitionBody(model, caseTask, xtw, options); // Always export the case reference as an expression, even if the caseRef is set if (StringUtils.isNotEmpty(caseTask.getCaseRef()) || StringUtils.isNotEmpty(caseTask.getCaseRefExpression())) { diff --git a/modules/flowable-cmmn-converter/src/main/java/org/flowable/cmmn/converter/export/DecisionTaskExport.java b/modules/flowable-cmmn-converter/src/main/java/org/flowable/cmmn/converter/export/DecisionTaskExport.java index dcb8d418d0e..7fbf28951e1 100644 --- a/modules/flowable-cmmn-converter/src/main/java/org/flowable/cmmn/converter/export/DecisionTaskExport.java +++ b/modules/flowable-cmmn-converter/src/main/java/org/flowable/cmmn/converter/export/DecisionTaskExport.java @@ -15,6 +15,7 @@ import javax.xml.stream.XMLStreamWriter; import org.apache.commons.lang3.StringUtils; +import org.flowable.cmmn.converter.CmmnXmlConverterOptions; import org.flowable.cmmn.model.CmmnModel; import org.flowable.cmmn.model.DecisionTask; @@ -43,8 +44,8 @@ protected boolean writePlanItemDefinitionExtensionElements(CmmnModel model, Deci } @Override - protected void writePlanItemDefinitionBody(CmmnModel model, DecisionTask decisionTask, XMLStreamWriter xtw) throws Exception { - super.writePlanItemDefinitionBody(model, decisionTask, xtw); + protected void writePlanItemDefinitionBody(CmmnModel model, DecisionTask decisionTask, XMLStreamWriter xtw, CmmnXmlConverterOptions options) throws Exception { + super.writePlanItemDefinitionBody(model, decisionTask, xtw, options); if (StringUtils.isNotEmpty(decisionTask.getDecisionRef()) || StringUtils.isNotEmpty(decisionTask.getDecisionRefExpression())) { xtw.writeStartElement(ELEMENT_DECISION_REF_EXPRESSION); xtw.writeCData( diff --git a/modules/flowable-cmmn-converter/src/main/java/org/flowable/cmmn/converter/export/PlanFragmentExport.java b/modules/flowable-cmmn-converter/src/main/java/org/flowable/cmmn/converter/export/PlanFragmentExport.java index 7540b18c480..7782026f22a 100644 --- a/modules/flowable-cmmn-converter/src/main/java/org/flowable/cmmn/converter/export/PlanFragmentExport.java +++ b/modules/flowable-cmmn-converter/src/main/java/org/flowable/cmmn/converter/export/PlanFragmentExport.java @@ -14,6 +14,7 @@ import javax.xml.stream.XMLStreamWriter; +import org.flowable.cmmn.converter.CmmnXmlConverterOptions; import org.flowable.cmmn.model.CmmnModel; import org.flowable.cmmn.model.PlanFragment; import org.flowable.cmmn.model.PlanItem; @@ -49,8 +50,8 @@ protected void writePlanItemDefinitionSpecificAttributes(PlanFragment planFragme } @Override - protected void writePlanItemDefinitionBody(CmmnModel model, PlanFragment planFragment, XMLStreamWriter xtw) throws Exception { - super.writePlanItemDefinitionBody(model, planFragment, xtw); + protected void writePlanItemDefinitionBody(CmmnModel model, PlanFragment planFragment, XMLStreamWriter xtw, CmmnXmlConverterOptions options) throws Exception { + super.writePlanItemDefinitionBody(model, planFragment, xtw, options); for (PlanItem planItem : planFragment.getPlanItems()) { PlanItemExport.writePlanItem(model, planItem, xtw); } diff --git a/modules/flowable-cmmn-converter/src/main/java/org/flowable/cmmn/converter/export/PlanItemDefinitionExport.java b/modules/flowable-cmmn-converter/src/main/java/org/flowable/cmmn/converter/export/PlanItemDefinitionExport.java index 58b15008880..7fffc3d24b0 100644 --- a/modules/flowable-cmmn-converter/src/main/java/org/flowable/cmmn/converter/export/PlanItemDefinitionExport.java +++ b/modules/flowable-cmmn-converter/src/main/java/org/flowable/cmmn/converter/export/PlanItemDefinitionExport.java @@ -18,6 +18,7 @@ import javax.xml.stream.XMLStreamWriter; import org.flowable.cmmn.converter.CmmnXmlConstants; +import org.flowable.cmmn.converter.CmmnXmlConverterOptions; import org.flowable.cmmn.model.CmmnModel; import org.flowable.cmmn.model.PlanItemDefinition; import org.flowable.common.engine.api.FlowableException; @@ -51,12 +52,12 @@ public static void addPlanItemDefinitionExport(AbstractPlanItemDefinitionExport planItemDefinitionExporters.put(exporter.getExportablePlanItemDefinitionClass().getCanonicalName(), exporter); } - public static void writePlanItemDefinition(CmmnModel model, PlanItemDefinition planItemDefinition, XMLStreamWriter xtw) throws Exception { + public static void writePlanItemDefinition(CmmnModel model, PlanItemDefinition planItemDefinition, XMLStreamWriter xtw, CmmnXmlConverterOptions options) throws Exception { AbstractPlanItemDefinitionExport exporter = determineExporter(planItemDefinition); if (exporter == null) { throw new FlowableException("Cannot find a PlanItemDefinitionExporter for '" + planItemDefinition.getClass().getCanonicalName() + "'"); } - exporter.writePlanItemDefinition(model, planItemDefinition, xtw); + exporter.writePlanItemDefinition(model, planItemDefinition, xtw, options); } protected static AbstractPlanItemDefinitionExport determineExporter(PlanItemDefinition planItemDefinition) { diff --git a/modules/flowable-cmmn-converter/src/main/java/org/flowable/cmmn/converter/export/ProcessTaskExport.java b/modules/flowable-cmmn-converter/src/main/java/org/flowable/cmmn/converter/export/ProcessTaskExport.java index b31727efe2e..0e5f4358f70 100644 --- a/modules/flowable-cmmn-converter/src/main/java/org/flowable/cmmn/converter/export/ProcessTaskExport.java +++ b/modules/flowable-cmmn-converter/src/main/java/org/flowable/cmmn/converter/export/ProcessTaskExport.java @@ -15,6 +15,7 @@ import javax.xml.stream.XMLStreamWriter; import org.apache.commons.lang3.StringUtils; +import org.flowable.cmmn.converter.CmmnXmlConverterOptions; import org.flowable.cmmn.model.CmmnModel; import org.flowable.cmmn.model.ProcessTask; @@ -47,8 +48,8 @@ protected void writePlanItemDefinitionSpecificAttributes(ProcessTask processTask } @Override - protected void writePlanItemDefinitionBody(CmmnModel model, ProcessTask processTask, XMLStreamWriter xtw) throws Exception { - super.writePlanItemDefinitionBody(model, processTask, xtw); + protected void writePlanItemDefinitionBody(CmmnModel model, ProcessTask processTask, XMLStreamWriter xtw, CmmnXmlConverterOptions options) throws Exception { + super.writePlanItemDefinitionBody(model, processTask, xtw, options); if (StringUtils.isNotEmpty(processTask.getProcessRef()) || StringUtils.isNotEmpty(processTask.getProcessRefExpression())) { xtw.writeStartElement(ELEMENT_PROCESS_REF_EXPRESSION); diff --git a/modules/flowable-cmmn-converter/src/main/java/org/flowable/cmmn/converter/export/StageExport.java b/modules/flowable-cmmn-converter/src/main/java/org/flowable/cmmn/converter/export/StageExport.java index 77cd30a4e78..0160a489d3b 100644 --- a/modules/flowable-cmmn-converter/src/main/java/org/flowable/cmmn/converter/export/StageExport.java +++ b/modules/flowable-cmmn-converter/src/main/java/org/flowable/cmmn/converter/export/StageExport.java @@ -15,6 +15,7 @@ import javax.xml.stream.XMLStreamWriter; import org.apache.commons.lang3.StringUtils; +import org.flowable.cmmn.converter.CmmnXmlConverterOptions; import org.flowable.cmmn.model.CmmnModel; import org.flowable.cmmn.model.PlanItem; import org.flowable.cmmn.model.PlanItemDefinition; @@ -81,8 +82,8 @@ protected void writePlanItemDefinitionSpecificAttributes(Stage stage, XMLStreamW } @Override - protected void writePlanItemDefinitionBody(CmmnModel model, Stage stage, XMLStreamWriter xtw) throws Exception { - super.writePlanItemDefinitionBody(model, stage, xtw); + protected void writePlanItemDefinitionBody(CmmnModel model, Stage stage, XMLStreamWriter xtw, CmmnXmlConverterOptions options) throws Exception { + super.writePlanItemDefinitionBody(model, stage, xtw, options); for (PlanItem planItem : stage.getPlanItems()) { PlanItemExport.writePlanItem(model, planItem, xtw); } @@ -92,7 +93,7 @@ protected void writePlanItemDefinitionBody(CmmnModel model, Stage stage, XMLStre } for (PlanItemDefinition planItemDefinition : stage.getPlanItemDefinitions()) { - PlanItemDefinitionExport.writePlanItemDefinition(model, planItemDefinition, xtw); + PlanItemDefinitionExport.writePlanItemDefinition(model, planItemDefinition, xtw, options); } if (stage.isPlanModel() && stage.getExitCriteria() != null && !stage.getExitCriteria().isEmpty()) { diff --git a/modules/flowable-cmmn-converter/src/main/java/org/flowable/cmmn/converter/export/TimerEventListenerExport.java b/modules/flowable-cmmn-converter/src/main/java/org/flowable/cmmn/converter/export/TimerEventListenerExport.java index f1cb051178a..39483c50fc3 100644 --- a/modules/flowable-cmmn-converter/src/main/java/org/flowable/cmmn/converter/export/TimerEventListenerExport.java +++ b/modules/flowable-cmmn-converter/src/main/java/org/flowable/cmmn/converter/export/TimerEventListenerExport.java @@ -16,6 +16,7 @@ import org.apache.commons.lang3.StringUtils; import org.flowable.cmmn.converter.CmmnXmlConstants; +import org.flowable.cmmn.converter.CmmnXmlConverterOptions; import org.flowable.cmmn.model.CmmnModel; import org.flowable.cmmn.model.TimerEventListener; @@ -46,7 +47,7 @@ protected void writePlanItemDefinitionSpecificAttributes(TimerEventListener time } @Override - protected void writePlanItemDefinitionBody(CmmnModel model, TimerEventListener timerEventListener, XMLStreamWriter xtw) throws Exception { + protected void writePlanItemDefinitionBody(CmmnModel model, TimerEventListener timerEventListener, XMLStreamWriter xtw, CmmnXmlConverterOptions options) throws Exception { if (StringUtils.isNotEmpty(timerEventListener.getTimerExpression())) { xtw.writeStartElement(ELEMENT_TIMER_EXPRESSION); xtw.writeCData(timerEventListener.getTimerExpression()); diff --git a/modules/flowable-cmmn-converter/src/test/java/org/flowable/test/cmmn/converter/util/XmlTestUtils.java b/modules/flowable-cmmn-converter/src/test/java/org/flowable/test/cmmn/converter/util/XmlTestUtils.java index f174d25aa9a..7de1968e133 100644 --- a/modules/flowable-cmmn-converter/src/test/java/org/flowable/test/cmmn/converter/util/XmlTestUtils.java +++ b/modules/flowable-cmmn-converter/src/test/java/org/flowable/test/cmmn/converter/util/XmlTestUtils.java @@ -31,12 +31,18 @@ public static CmmnModel readXmlExportAndReadAgain(String resource) { } public static CmmnModel exportAndReadXMLFile(CmmnModel model) { - byte[] xml = new CmmnXmlConverter().convertToXML(model); - return new CmmnXmlConverter().convertToCmmnModel(new InputStreamSource(new ByteArrayInputStream(xml)), true, false, "UTF-8"); + byte[] xml = getCmmnXmlConverter().convertToXML(model); + return getCmmnXmlConverter().convertToCmmnModel(new InputStreamSource(new ByteArrayInputStream(xml)), true, false, "UTF-8"); } public static CmmnModel readXMLFile(String resource) { - return new CmmnXmlConverter().convertToCmmnModel(new ClasspathStreamResource(resource), true, false); + return getCmmnXmlConverter().convertToCmmnModel(new ClasspathStreamResource(resource), true, false); + } + + private static CmmnXmlConverter getCmmnXmlConverter() { + CmmnXmlConverter converter = new CmmnXmlConverter(); + converter.getCmmnXmlConverterOptions().setSaveElementNameWithNewLineInExtensionElement(true); + return converter; } protected static class ClasspathStreamResource implements InputStreamProvider { diff --git a/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/db/CmmnDbSchemaManager.java b/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/db/CmmnDbSchemaManager.java index d5c31604bdf..6acdb3b1f8b 100644 --- a/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/db/CmmnDbSchemaManager.java +++ b/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/db/CmmnDbSchemaManager.java @@ -17,7 +17,7 @@ import org.apache.commons.lang3.StringUtils; import org.flowable.cmmn.engine.CmmnEngine; import org.flowable.cmmn.engine.impl.util.CommandContextUtil; -import org.flowable.common.engine.impl.AbstractEngineConfiguration; +import org.flowable.common.engine.impl.db.EngineSchemaManagerLockConfiguration; import org.flowable.common.engine.impl.db.EngineSqlScriptBasedDbSchemaManager; public class CmmnDbSchemaManager extends EngineSqlScriptBasedDbSchemaManager { @@ -48,7 +48,7 @@ public class CmmnDbSchemaManager extends EngineSqlScriptBasedDbSchemaManager { ); public CmmnDbSchemaManager() { - super("cmmn"); + super("cmmn", new EngineSchemaManagerLockConfiguration(CommandContextUtil::getCmmnEngineConfiguration)); } @Override @@ -75,11 +75,6 @@ protected String getEngineTableName() { protected String getChangeLogTableName() { return "ACT_CMMN_DATABASECHANGELOG"; } - - @Override - protected String getChangeLogTablePrefixName() { - return "ACT_CMMN"; - } @Override protected String getDbVersionForChangelogVersion(String changeLogVersion) { @@ -89,11 +84,6 @@ protected String getDbVersionForChangelogVersion(String changeLogVersion) { return "6.1.2.0"; } - @Override - protected AbstractEngineConfiguration getEngineConfiguration() { - return CommandContextUtil.getCmmnEngineConfiguration(); - } - @Override protected String getResourcesRootDirectory() { return "org/flowable/cmmn/db/"; diff --git a/modules/flowable-cmmn-engine/src/main/resources/org/flowable/cmmn/db/mapping/mappings.xml b/modules/flowable-cmmn-engine/src/main/resources/org/flowable/cmmn/db/mapping/mappings.xml index 8563d1edf21..44767de5b23 100644 --- a/modules/flowable-cmmn-engine/src/main/resources/org/flowable/cmmn/db/mapping/mappings.xml +++ b/modules/flowable-cmmn-engine/src/main/resources/org/flowable/cmmn/db/mapping/mappings.xml @@ -27,7 +27,6 @@ - diff --git a/modules/flowable-cmmn-engine/src/main/resources/org/flowable/cmmn/db/upgrade/flowable.mssql.upgradestep.7010.to.7100.cmmn.sql b/modules/flowable-cmmn-engine/src/main/resources/org/flowable/cmmn/db/upgrade/flowable.mssql.upgradestep.7011.to.7100.cmmn.sql similarity index 100% rename from modules/flowable-cmmn-engine/src/main/resources/org/flowable/cmmn/db/upgrade/flowable.mssql.upgradestep.7010.to.7100.cmmn.sql rename to modules/flowable-cmmn-engine/src/main/resources/org/flowable/cmmn/db/upgrade/flowable.mssql.upgradestep.7011.to.7100.cmmn.sql diff --git a/modules/flowable-dmn-engine/src/main/java/org/flowable/dmn/engine/impl/db/DmnDbSchemaManager.java b/modules/flowable-dmn-engine/src/main/java/org/flowable/dmn/engine/impl/db/DmnDbSchemaManager.java index 832039cc4ce..e3ce6d89f65 100644 --- a/modules/flowable-dmn-engine/src/main/java/org/flowable/dmn/engine/impl/db/DmnDbSchemaManager.java +++ b/modules/flowable-dmn-engine/src/main/java/org/flowable/dmn/engine/impl/db/DmnDbSchemaManager.java @@ -16,7 +16,7 @@ import java.util.Map; import org.apache.commons.lang3.StringUtils; -import org.flowable.common.engine.impl.AbstractEngineConfiguration; +import org.flowable.common.engine.impl.db.EngineSchemaManagerLockConfiguration; import org.flowable.common.engine.impl.db.EngineSqlScriptBasedDbSchemaManager; import org.flowable.dmn.engine.DmnEngine; import org.flowable.dmn.engine.impl.util.CommandContextUtil; @@ -39,7 +39,7 @@ public class DmnDbSchemaManager extends EngineSqlScriptBasedDbSchemaManager { ); public DmnDbSchemaManager() { - super("dmn"); + super("dmn", new EngineSchemaManagerLockConfiguration(CommandContextUtil::getDmnEngineConfiguration)); } @Override @@ -66,11 +66,6 @@ protected String getEngineTableName() { protected String getChangeLogTableName() { return "ACT_DMN_DATABASECHANGELOG"; } - - @Override - protected String getChangeLogTablePrefixName() { - return "ACT_DMN"; - } @Override protected String getDbVersionForChangelogVersion(String changeLogVersion) { @@ -80,11 +75,6 @@ protected String getDbVersionForChangelogVersion(String changeLogVersion) { return "5.99.0.0"; } - @Override - protected AbstractEngineConfiguration getEngineConfiguration() { - return CommandContextUtil.getDmnEngineConfiguration(); - } - @Override protected String getResourcesRootDirectory() { return "org/flowable/dmn/db/"; diff --git a/modules/flowable-dmn-engine/src/main/resources/org/flowable/dmn/db/mapping/mappings.xml b/modules/flowable-dmn-engine/src/main/resources/org/flowable/dmn/db/mapping/mappings.xml index 293de0f36f3..a742e75b3fa 100644 --- a/modules/flowable-dmn-engine/src/main/resources/org/flowable/dmn/db/mapping/mappings.xml +++ b/modules/flowable-dmn-engine/src/main/resources/org/flowable/dmn/db/mapping/mappings.xml @@ -9,7 +9,6 @@ - diff --git a/modules/flowable-dmn-engine/src/main/resources/org/flowable/dmn/db/upgrade/flowable.mssql.upgradestep.7010.to.7100.dmn.sql b/modules/flowable-dmn-engine/src/main/resources/org/flowable/dmn/db/upgrade/flowable.mssql.upgradestep.7011.to.7100.dmn.sql similarity index 100% rename from modules/flowable-dmn-engine/src/main/resources/org/flowable/dmn/db/upgrade/flowable.mssql.upgradestep.7010.to.7100.dmn.sql rename to modules/flowable-dmn-engine/src/main/resources/org/flowable/dmn/db/upgrade/flowable.mssql.upgradestep.7011.to.7100.dmn.sql diff --git a/modules/flowable-engine-common/src/main/java/org/flowable/common/engine/impl/AbstractEngineConfiguration.java b/modules/flowable-engine-common/src/main/java/org/flowable/common/engine/impl/AbstractEngineConfiguration.java index ee0de1306c4..cc50a597431 100755 --- a/modules/flowable-engine-common/src/main/java/org/flowable/common/engine/impl/AbstractEngineConfiguration.java +++ b/modules/flowable-engine-common/src/main/java/org/flowable/common/engine/impl/AbstractEngineConfiguration.java @@ -87,6 +87,8 @@ import org.flowable.common.engine.impl.db.MybatisTypeAliasConfigurator; import org.flowable.common.engine.impl.db.MybatisTypeHandlerConfigurator; import org.flowable.common.engine.impl.db.SchemaManager; +import org.flowable.common.engine.impl.db.SchemaManagerDatabaseConfiguration; +import org.flowable.common.engine.impl.db.SchemaManagerDatabaseConfigurationSessionFactory; import org.flowable.common.engine.impl.db.SchemaOperationsEngineBuild; import org.flowable.common.engine.impl.event.EventDispatchAction; import org.flowable.common.engine.impl.event.FlowableEventDispatcherImpl; @@ -122,6 +124,7 @@ import org.flowable.common.engine.impl.persistence.entity.data.impl.MybatisPropertyDataManager; import org.flowable.common.engine.impl.runtime.Clock; import org.flowable.common.engine.impl.service.CommonEngineServiceImpl; +import org.flowable.common.engine.impl.util.DbUtil; import org.flowable.common.engine.impl.util.DefaultClockImpl; import org.flowable.common.engine.impl.util.IoUtil; import org.flowable.common.engine.impl.util.ReflectUtil; @@ -382,38 +385,7 @@ public abstract class AbstractEngineConfiguration { public static final String DATABASE_TYPE_COCKROACHDB = "cockroachdb"; public static Properties getDefaultDatabaseTypeMappings() { - Properties databaseTypeMappings = new Properties(); - databaseTypeMappings.setProperty("H2", DATABASE_TYPE_H2); - databaseTypeMappings.setProperty("HSQL Database Engine", DATABASE_TYPE_HSQL); - databaseTypeMappings.setProperty("MySQL", DATABASE_TYPE_MYSQL); - databaseTypeMappings.setProperty("MariaDB", DATABASE_TYPE_MYSQL); - databaseTypeMappings.setProperty("Oracle", DATABASE_TYPE_ORACLE); - databaseTypeMappings.setProperty(PRODUCT_NAME_POSTGRES, DATABASE_TYPE_POSTGRES); - databaseTypeMappings.setProperty("Microsoft SQL Server", DATABASE_TYPE_MSSQL); - databaseTypeMappings.setProperty(DATABASE_TYPE_DB2, DATABASE_TYPE_DB2); - databaseTypeMappings.setProperty("DB2", DATABASE_TYPE_DB2); - databaseTypeMappings.setProperty("DB2/NT", DATABASE_TYPE_DB2); - databaseTypeMappings.setProperty("DB2/NT64", DATABASE_TYPE_DB2); - databaseTypeMappings.setProperty("DB2 UDP", DATABASE_TYPE_DB2); - databaseTypeMappings.setProperty("DB2/LINUX", DATABASE_TYPE_DB2); - databaseTypeMappings.setProperty("DB2/LINUX390", DATABASE_TYPE_DB2); - databaseTypeMappings.setProperty("DB2/LINUXX8664", DATABASE_TYPE_DB2); - databaseTypeMappings.setProperty("DB2/LINUXZ64", DATABASE_TYPE_DB2); - databaseTypeMappings.setProperty("DB2/LINUXPPC64", DATABASE_TYPE_DB2); - databaseTypeMappings.setProperty("DB2/LINUXPPC64LE", DATABASE_TYPE_DB2); - databaseTypeMappings.setProperty("DB2/400 SQL", DATABASE_TYPE_DB2); - databaseTypeMappings.setProperty("DB2/6000", DATABASE_TYPE_DB2); - databaseTypeMappings.setProperty("DB2 UDB iSeries", DATABASE_TYPE_DB2); - databaseTypeMappings.setProperty("DB2/AIX64", DATABASE_TYPE_DB2); - databaseTypeMappings.setProperty("DB2/HPUX", DATABASE_TYPE_DB2); - databaseTypeMappings.setProperty("DB2/HP64", DATABASE_TYPE_DB2); - databaseTypeMappings.setProperty("DB2/SUN", DATABASE_TYPE_DB2); - databaseTypeMappings.setProperty("DB2/SUN64", DATABASE_TYPE_DB2); - databaseTypeMappings.setProperty("DB2/PTX", DATABASE_TYPE_DB2); - databaseTypeMappings.setProperty("DB2/2", DATABASE_TYPE_DB2); - databaseTypeMappings.setProperty("DB2 UDB AS400", DATABASE_TYPE_DB2); - databaseTypeMappings.setProperty(PRODUCT_NAME_CRDB, DATABASE_TYPE_COCKROACHDB); - return databaseTypeMappings; + return DbUtil.getDefaultDatabaseTypeMappings(); } protected Map beans; @@ -498,46 +470,7 @@ protected void initDataSource() { } public void initDatabaseType() { - Connection connection = null; - try { - connection = dataSource.getConnection(); - DatabaseMetaData databaseMetaData = connection.getMetaData(); - String databaseProductName = databaseMetaData.getDatabaseProductName(); - logger.debug("database product name: '{}'", databaseProductName); - - // CRDB does not expose the version through the jdbc driver, so we need to fetch it through version(). - if (PRODUCT_NAME_POSTGRES.equalsIgnoreCase(databaseProductName)) { - try (PreparedStatement preparedStatement = connection.prepareStatement("select version() as version;"); - ResultSet resultSet = preparedStatement.executeQuery()) { - String version = null; - if (resultSet.next()) { - version = resultSet.getString("version"); - } - - if (StringUtils.isNotEmpty(version) && version.toLowerCase().startsWith(PRODUCT_NAME_CRDB.toLowerCase())) { - databaseProductName = PRODUCT_NAME_CRDB; - logger.info("CockroachDB version '{}' detected", version); - } - } - } - - databaseType = databaseTypeMappings.getProperty(databaseProductName); - if (databaseType == null) { - throw new FlowableException("couldn't deduct database type from database product name '" + databaseProductName + "'"); - } - logger.debug("using database type: {}", databaseType); - - } catch (SQLException e) { - throw new RuntimeException("Exception while initializing Database connection", e); - } finally { - try { - if (connection != null) { - connection.close(); - } - } catch (SQLException e) { - logger.error("Exception while closing the Database connection", e); - } - } + databaseType = DbUtil.determineDatabaseType(dataSource, logger, databaseTypeMappings); // Special care for MSSQL, as it has a hard limit of 2000 params per statement (incl bulk statement). // Especially with executions, with 100 as default, this limit is passed. @@ -765,6 +698,7 @@ public void initSessionFactories() { if (usingRelationalDatabase) { initDbSqlSessionFactory(); + initSchemaManagerDatabaseConfigurationSessionFactory(); } addSessionFactory(new GenericManagerFactory(EntityCache.class, EntityCacheImpl::new)); @@ -811,6 +745,12 @@ public void initDbSqlSessionFactory() { addSessionFactory(dbSqlSessionFactory); } + protected void initSchemaManagerDatabaseConfigurationSessionFactory() { + if (!sessionFactories.containsKey(SchemaManagerDatabaseConfiguration.class)) { + addSessionFactory(new SchemaManagerDatabaseConfigurationSessionFactory()); + } + } + public DbSqlSessionFactory createDbSqlSessionFactory() { return new DbSqlSessionFactory(usePrefixId); } diff --git a/modules/flowable-engine-common/src/main/java/org/flowable/common/engine/impl/db/AbstractSqlScriptBasedDbSchemaManager.java b/modules/flowable-engine-common/src/main/java/org/flowable/common/engine/impl/db/AbstractSqlScriptBasedDbSchemaManager.java index 840ac31a90d..59e5d7021f6 100644 --- a/modules/flowable-engine-common/src/main/java/org/flowable/common/engine/impl/db/AbstractSqlScriptBasedDbSchemaManager.java +++ b/modules/flowable-engine-common/src/main/java/org/flowable/common/engine/impl/db/AbstractSqlScriptBasedDbSchemaManager.java @@ -24,7 +24,6 @@ import java.sql.SQLException; import java.sql.Statement; -import org.apache.commons.lang3.StringUtils; import org.flowable.common.engine.api.FlowableException; import org.flowable.common.engine.impl.FlowableVersion; import org.flowable.common.engine.impl.FlowableVersions; @@ -45,13 +44,16 @@ public abstract class AbstractSqlScriptBasedDbSchemaManager implements SchemaMan protected static final String PROPERTY_TABLE = "ACT_GE_PROPERTY"; - protected static final String SCHEMA_VERSION_PROPERTY = "schema.version"; - + protected SchemaManagerDatabaseConfiguration getDatabaseConfiguration() { + return Context.getCommandContext().getSession(SchemaManagerDatabaseConfiguration.class); + } + protected void dbSchemaUpgradeUntil6120(final String component, final int currentDatabaseVersionsIndex, final String dbVersion) { FlowableVersion version = FlowableVersions.FLOWABLE_VERSIONS.get(currentDatabaseVersionsIndex); String currentVersion = version.getMainVersion(); logger.info("upgrading flowable {} schema from {} to {}", component, currentVersion, FlowableVersions.LAST_V6_VERSION_BEFORE_SERVICES); + SchemaManagerDatabaseConfiguration databaseConfiguration = getDatabaseConfiguration(); // Actual execution of schema DDL SQL for (int i = currentDatabaseVersionsIndex + 1; i < FlowableVersions.getFlowableVersionIndexForDbVersion(FlowableVersions.LAST_V6_VERSION_BEFORE_SERVICES); i++) { String nextVersion = FlowableVersions.FLOWABLE_VERSIONS.get(i).getMainVersion(); @@ -69,7 +71,7 @@ protected void dbSchemaUpgradeUntil6120(final String component, final int curren nextVersion = nextVersion.replace(".", ""); logger.info("Upgrade needed: {} -> {}. Looking for schema update resource for component '{}'", currentVersion, nextVersion, component); - String databaseType = getDbSqlSession().getDbSqlSessionFactory().getDatabaseType(); + String databaseType = databaseConfiguration.getDatabaseType(); executeSchemaResource("upgrade", component, getResourceForDbOperation("upgrade", "upgradestep." + currentVersion + ".to." + nextVersion, component, databaseType), true); // To avoid having too much similar scripts, for upgrades the 'all' database is supported and executed for every database type @@ -101,7 +103,7 @@ protected void dbSchemaUpgrade(final String component, final int currentDatabase nextVersion = nextVersion.replace(".", ""); logger.info("Upgrade needed: {} -> {}. Looking for schema update resource for component '{}'", currentVersion, nextVersion, component); - String databaseType = getDbSqlSession().getDbSqlSessionFactory().getDatabaseType(); + String databaseType = getDatabaseConfiguration().getDatabaseType(); executeSchemaResource("upgrade", component, getResourceForDbOperation("upgrade", "upgradestep." + currentVersion + ".to." + nextVersion, component, databaseType), true); @@ -115,34 +117,22 @@ protected void dbSchemaUpgrade(final String component, final int currentDatabase public boolean isTablePresent(String tableName) { // ACT-1610: in case the prefix IS the schema itself, we don't add the // prefix, since the check is already aware of the schema - DbSqlSession dbSqlSession = getDbSqlSession(); - DbSqlSessionFactory dbSqlSessionFactory = dbSqlSession.getDbSqlSessionFactory(); - if (!dbSqlSession.getDbSqlSessionFactory().isTablePrefixIsSchema()) { + SchemaManagerDatabaseConfiguration databaseConfiguration = getDatabaseConfiguration(); + if (!databaseConfiguration.isTablePrefixIsSchema()) { tableName = prependDatabaseTablePrefix(tableName); } Connection connection = null; try { - connection = dbSqlSession.getSqlSession().getConnection(); + connection = databaseConfiguration.getConnection(); DatabaseMetaData databaseMetaData = connection.getMetaData(); ResultSet tables = null; - String catalog = dbSqlSession.getConnectionMetadataDefaultCatalog(); - if (dbSqlSessionFactory.getDatabaseCatalog() != null && dbSqlSessionFactory.getDatabaseCatalog().length() > 0) { - catalog = dbSqlSessionFactory.getDatabaseCatalog(); - } + String catalog = databaseConfiguration.getDatabaseCatalog(); - String schema = dbSqlSession.getConnectionMetadataDefaultSchema(); - if (dbSqlSessionFactory.getDatabaseSchema() != null && dbSqlSessionFactory.getDatabaseSchema().length() > 0) { - schema = dbSqlSessionFactory.getDatabaseSchema(); - } else if (dbSqlSessionFactory.isTablePrefixIsSchema() && StringUtils.isNotEmpty(dbSqlSessionFactory.getDatabaseTablePrefix())) { - schema = dbSqlSessionFactory.getDatabaseTablePrefix(); - if (StringUtils.isNotEmpty(schema) && schema.endsWith(".")) { - schema = schema.substring(0, schema.length() - 1); - } - } + String schema = databaseConfiguration.getDatabaseSchema(); - String databaseType = dbSqlSessionFactory.getDatabaseType(); + String databaseType = databaseConfiguration.getDatabaseType(); if ("postgres".equals(databaseType)) { tableName = tableName.toLowerCase(); @@ -178,24 +168,26 @@ public boolean isTablePresent(String tableName) { } protected String prependDatabaseTablePrefix(String tableName) { - return getDbSqlSession().getDbSqlSessionFactory().getDatabaseTablePrefix() + tableName; - } - - public DbSqlSession getDbSqlSession() { - return Context.getCommandContext().getSession(DbSqlSession.class); + return getDatabaseConfiguration().getDatabaseTablePrefix() + tableName; } public String getProperty(String propertyName) { + return getProperty(propertyName, true); + } + + public String getProperty(String propertyName, boolean checkPropertyTablePresent) { String tableName = getPropertyTable(); - if (!isTablePresent(tableName)) { // isTablePresent will add the prefix, so adding it later + if (checkPropertyTablePresent && !isTablePresent(tableName)) { // isTablePresent will add the prefix, so adding it later return null; } - if (!getDbSqlSession().getDbSqlSessionFactory().isTablePrefixIsSchema()) { + SchemaManagerDatabaseConfiguration databaseConfiguration = getDatabaseConfiguration(); + + if (!databaseConfiguration.isTablePrefixIsSchema()) { tableName = prependDatabaseTablePrefix(tableName); } - try (PreparedStatement statement = getDbSqlSession().getSqlSession().getConnection() + try (PreparedStatement statement = databaseConfiguration.getConnection() .prepareStatement("select VALUE_ from " + tableName + " where NAME_ = ?")) { statement.setString(1, propertyName); @@ -206,6 +198,9 @@ public String getProperty(String propertyName) { return null; } } catch (SQLException e) { + if (!checkPropertyTablePresent && isMissingTablesException(e)) { + throw new FlowableException("SQL Exception when getting value. Message: " + e.getMessage(), e); + } logger.error("Could not get property from table {}", tableName, e); return null; } @@ -222,7 +217,7 @@ public String getResourceForDbOperation(String directory, String operation, Stri protected abstract String getResourcesRootDirectory(); public void executeMandatorySchemaResource(String operation, String component) { - String databaseType = getDbSqlSession().getDbSqlSessionFactory().getDatabaseType(); + String databaseType = getDatabaseConfiguration().getDatabaseType(); executeSchemaResource(operation, component, getResourceForDbOperation(operation, operation, component, databaseType), false); } @@ -247,16 +242,17 @@ protected void executeSchemaResource(String operation, String component, String logger.info("performing {} on {} with resource {}", operation, component, resourceName); String sqlStatement = null; String exceptionSqlStatement = null; - DbSqlSession dbSqlSession = getDbSqlSession(); try { - Connection connection = dbSqlSession.getSqlSession().getConnection(); + SchemaManagerDatabaseConfiguration databaseConfiguration = getDatabaseConfiguration(); + Connection connection = databaseConfiguration.getConnection(); Exception exception = null; byte[] bytes = IoUtil.readInputStream(inputStream, resourceName); String ddlStatements = new String(bytes, StandardCharsets.UTF_8); + String databaseType = databaseConfiguration.getDatabaseType(); // Special DDL handling for certain databases try { - if (dbSqlSession.getDbSqlSessionFactory().isMysql()) { + if ("mysql".equals(databaseType)) { DatabaseMetaData databaseMetaData = connection.getMetaData(); int majorVersion = databaseMetaData.getDatabaseMajorVersion(); int minorVersion = databaseMetaData.getDatabaseMinorVersion(); @@ -306,7 +302,7 @@ protected void executeSchemaResource(String operation, String component, String } else if (line.length() > 0) { - if (dbSqlSession.getDbSqlSessionFactory().isOracle() && line.startsWith("begin")) { + if ("oracle".equals(databaseType) && line.startsWith("begin")) { inOraclePlsqlBlock = true; sqlStatement = addSqlStatementPiece(sqlStatement, line); @@ -386,4 +382,25 @@ protected String readNextTrimmedLine(BufferedReader reader) throws IOException { return line; } + protected boolean isMissingTablesException(Exception e) { + String exceptionMessage = e.getMessage(); + if (e.getMessage() != null) { + // Matches message returned from H2 + if ((exceptionMessage.contains("Table")) && (exceptionMessage.contains("not found"))) { + return true; + } + + // Message returned from MySQL and Oracle + if ((exceptionMessage.contains("Table") || exceptionMessage.contains("table")) && (exceptionMessage.contains("doesn't exist"))) { + return true; + } + + // Message returned from Postgres + if ((exceptionMessage.contains("relation") || exceptionMessage.contains("table")) && (exceptionMessage.contains("does not exist"))) { + return true; + } + } + return false; + } + } diff --git a/modules/flowable-engine-common/src/main/java/org/flowable/common/engine/impl/db/DbSqlSessionSchemaManagerConfiguration.java b/modules/flowable-engine-common/src/main/java/org/flowable/common/engine/impl/db/DbSqlSessionSchemaManagerConfiguration.java new file mode 100644 index 00000000000..162bbf8043d --- /dev/null +++ b/modules/flowable-engine-common/src/main/java/org/flowable/common/engine/impl/db/DbSqlSessionSchemaManagerConfiguration.java @@ -0,0 +1,85 @@ +/* Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.flowable.common.engine.impl.db; + +import java.sql.Connection; + +import org.apache.commons.lang3.StringUtils; +import org.flowable.common.engine.impl.interceptor.Session; + +/** + * @author Filip Hrisafov + */ +public class DbSqlSessionSchemaManagerConfiguration implements SchemaManagerDatabaseConfiguration, Session { + + protected final DbSqlSession dbSqlSession; + + public DbSqlSessionSchemaManagerConfiguration(DbSqlSession dbSqlSession) { + this.dbSqlSession = dbSqlSession; + } + + @Override + public void flush() { + // Nothing to do + } + + @Override + public void close() { + // Nothing to do + } + + @Override + public String getDatabaseType() { + return dbSqlSession.getDbSqlSessionFactory().getDatabaseType(); + } + + @Override + public String getDatabaseTablePrefix() { + return dbSqlSession.getDbSqlSessionFactory().getDatabaseTablePrefix(); + } + + @Override + public boolean isTablePrefixIsSchema() { + return dbSqlSession.getDbSqlSessionFactory().isTablePrefixIsSchema(); + } + + @Override + public String getDatabaseCatalog() { + String catalog = dbSqlSession.getConnectionMetadataDefaultCatalog(); + DbSqlSessionFactory dbSqlSessionFactory = dbSqlSession.getDbSqlSessionFactory(); + if (dbSqlSessionFactory.getDatabaseCatalog() != null && dbSqlSessionFactory.getDatabaseCatalog().length() > 0) { + catalog = dbSqlSessionFactory.getDatabaseCatalog(); + } + return catalog; + } + + @Override + public String getDatabaseSchema() { + String schema = dbSqlSession.getConnectionMetadataDefaultSchema(); + DbSqlSessionFactory dbSqlSessionFactory = dbSqlSession.getDbSqlSessionFactory(); + if (dbSqlSessionFactory.getDatabaseSchema() != null && dbSqlSessionFactory.getDatabaseSchema().length() > 0) { + schema = dbSqlSessionFactory.getDatabaseSchema(); + } else if (dbSqlSessionFactory.isTablePrefixIsSchema() && StringUtils.isNotEmpty(dbSqlSessionFactory.getDatabaseTablePrefix())) { + schema = dbSqlSessionFactory.getDatabaseTablePrefix(); + if (StringUtils.isNotEmpty(schema) && schema.endsWith(".")) { + schema = schema.substring(0, schema.length() - 1); + } + } + return schema; + } + + @Override + public Connection getConnection() { + return dbSqlSession.getSqlSession().getConnection(); + } +} diff --git a/modules/flowable-engine-common/src/main/java/org/flowable/common/engine/impl/db/EngineSchemaManagerLockConfiguration.java b/modules/flowable-engine-common/src/main/java/org/flowable/common/engine/impl/db/EngineSchemaManagerLockConfiguration.java new file mode 100644 index 00000000000..839b90dcd84 --- /dev/null +++ b/modules/flowable-engine-common/src/main/java/org/flowable/common/engine/impl/db/EngineSchemaManagerLockConfiguration.java @@ -0,0 +1,50 @@ +/* Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.flowable.common.engine.impl.db; + +import java.time.Duration; +import java.util.function.Supplier; + +import org.flowable.common.engine.api.lock.LockManager; +import org.flowable.common.engine.impl.AbstractEngineConfiguration; + +/** + * @author Filip Hrisafov + */ +public class EngineSchemaManagerLockConfiguration implements SchemaManagerLockConfiguration { + + protected final Supplier engineConfigurationSupplier; + + public EngineSchemaManagerLockConfiguration(Supplier engineConfigurationSupplier) { + this.engineConfigurationSupplier = engineConfigurationSupplier; + } + + protected AbstractEngineConfiguration getEngineConfiguration() { + return engineConfigurationSupplier.get(); + } + + @Override + public boolean isUseLockForDatabaseSchemaUpdate() { + return getEngineConfiguration().isUseLockForDatabaseSchemaUpdate(); + } + + @Override + public LockManager getLockManager(String lockName) { + return getEngineConfiguration().getLockManager(lockName); + } + + @Override + public Duration getSchemaLockWaitTime() { + return getEngineConfiguration().getSchemaLockWaitTime(); + } +} diff --git a/modules/flowable-engine-common/src/main/java/org/flowable/common/engine/impl/db/EngineSqlScriptBasedDbSchemaManager.java b/modules/flowable-engine-common/src/main/java/org/flowable/common/engine/impl/db/EngineSqlScriptBasedDbSchemaManager.java index 3e51d5a8d95..945e6c53344 100644 --- a/modules/flowable-engine-common/src/main/java/org/flowable/common/engine/impl/db/EngineSqlScriptBasedDbSchemaManager.java +++ b/modules/flowable-engine-common/src/main/java/org/flowable/common/engine/impl/db/EngineSqlScriptBasedDbSchemaManager.java @@ -12,22 +12,23 @@ */ package org.flowable.common.engine.impl.db; -import java.util.List; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; import org.flowable.common.engine.api.FlowableException; import org.flowable.common.engine.api.FlowableWrongDbException; import org.flowable.common.engine.api.lock.LockManager; -import org.flowable.common.engine.impl.AbstractEngineConfiguration; import org.flowable.common.engine.impl.FlowableVersions; -import org.flowable.common.engine.impl.persistence.entity.PropertyEntity; -import org.flowable.common.engine.impl.persistence.entity.PropertyEntityImpl; public abstract class EngineSqlScriptBasedDbSchemaManager extends AbstractSqlScriptBasedDbSchemaManager { protected final String context; + protected final SchemaManagerLockConfiguration lockConfiguration; - protected EngineSqlScriptBasedDbSchemaManager(String context) { + protected EngineSqlScriptBasedDbSchemaManager(String context, SchemaManagerLockConfiguration lockConfiguration) { this.context = context; + this.lockConfiguration = lockConfiguration; } protected abstract String getEngineVersion(); @@ -39,13 +40,9 @@ protected EngineSqlScriptBasedDbSchemaManager(String context) { protected abstract String getEngineTableName(); protected abstract String getChangeLogTableName(); - - protected abstract String getChangeLogTablePrefixName(); protected abstract String getDbVersionForChangelogVersion(String changeLogVersion); - protected abstract AbstractEngineConfiguration getEngineConfiguration(); - @Override public void schemaCheckVersion() { try { @@ -84,10 +81,9 @@ public void schemaCheckVersion() { @Override public void schemaCreate() { - AbstractEngineConfiguration engineConfiguration = getEngineConfiguration(); - if (engineConfiguration.isUseLockForDatabaseSchemaUpdate()) { - LockManager lockManager = engineConfiguration.getLockManager(getDbSchemaLockName()); - lockManager.waitForLockRunAndRelease(engineConfiguration.getSchemaLockWaitTime(), () -> { + if (lockConfiguration.isUseLockForDatabaseSchemaUpdate()) { + LockManager lockManager = lockConfiguration.getLockManager(getDbSchemaLockName()); + lockManager.waitForLockRunAndRelease(lockConfiguration.getSchemaLockWaitTime(), () -> { schemaCreateInLock(); return null; }); @@ -126,60 +122,48 @@ public void schemaDrop() { @Override public String schemaUpdate() { + if (lockConfiguration.isUseLockForDatabaseSchemaUpdate()) { + LockManager lockManager = lockConfiguration.getLockManager(getDbSchemaLockName()); + return lockManager.waitForLockRunAndRelease(lockConfiguration.getSchemaLockWaitTime(), this::schemaUpdateInLock); + } else { + return schemaUpdateInLock(); + } + } - PropertyEntity dbVersionProperty = null; + protected String schemaUpdateInLock() { String feedback = null; boolean isUpgradeNeeded = false; int matchingVersionIndex = -1; - DbSqlSession dbSqlSession = getDbSqlSession(); boolean isEngineTablePresent = isEngineTablePresent(); ChangeLogVersion changeLogVersion = null; String dbVersion = null; if (isEngineTablePresent) { - dbVersionProperty = dbSqlSession.selectById(PropertyEntityImpl.class, getSchemaVersionPropertyName()); - if (dbVersionProperty != null) { - dbVersion = dbVersionProperty.getValue(); - } else { + dbVersion = getDbVersion(); + if (dbVersion == null) { changeLogVersion = getChangeLogVersion(); dbVersion = changeLogVersion.dbVersion(); } } - AbstractEngineConfiguration engineConfiguration = getEngineConfiguration(); - LockManager lockManager; - if (engineConfiguration.isUseLockForDatabaseSchemaUpdate()) { - lockManager = engineConfiguration.getLockManager(getDbSchemaLockName()); - lockManager.waitForLock(engineConfiguration.getSchemaLockWaitTime()); - } else { - lockManager = null; + if (isEngineTablePresent) { + matchingVersionIndex = FlowableVersions.getFlowableVersionIndexForDbVersion(dbVersion); + isUpgradeNeeded = (matchingVersionIndex != (FlowableVersions.FLOWABLE_VERSIONS.size() - 1)); } - try { - if (isEngineTablePresent) { - matchingVersionIndex = FlowableVersions.getFlowableVersionIndexForDbVersion(dbVersion); - isUpgradeNeeded = (matchingVersionIndex != (FlowableVersions.FLOWABLE_VERSIONS.size() - 1)); - } - - if (isUpgradeNeeded) { - // Engine upgrade - dbSchemaUpgrade(context, matchingVersionIndex, dbVersion); - dbSchemaUpgraded(changeLogVersion); + if (isUpgradeNeeded) { + // Engine upgrade + dbSchemaUpgrade(context, matchingVersionIndex, dbVersion); + dbSchemaUpgraded(changeLogVersion); - feedback = "upgraded Flowable from " + dbVersion + " to " + getEngineVersion(); + feedback = "upgraded Flowable from " + dbVersion + " to " + getEngineVersion(); - } else if (!isEngineTablePresent) { - dbSchemaCreateEngine(); - } - - return feedback; - - } finally { - if (lockManager != null) { - lockManager.releaseLock(); - } + } else if (!isEngineTablePresent) { + dbSchemaCreateEngine(); } + + return feedback; } @Override @@ -203,48 +187,35 @@ protected String addMissingComponent(String missingComponents, String component) } protected String getDbVersion() { - DbSqlSession dbSqlSession = getDbSqlSession(); - String selectSchemaVersionStatement = dbSqlSession.getDbSqlSessionFactory() - .mapStatement("org.flowable.common.engine.impl.persistence.entity.PropertyEntityImpl.selectPropertyValue"); - return dbSqlSession.getSqlSession().selectOne(selectSchemaVersionStatement, getSchemaVersionPropertyName()); + return getProperty(getSchemaVersionPropertyName(), false); } protected ChangeLogVersion getChangeLogVersion() { String changeLogTableName = getChangeLogTableName(); if (changeLogTableName != null && isTablePresent(changeLogTableName)) { - DbSqlSession dbSqlSession = getDbSqlSession(); - String selectChangeLogVersionsStatement = dbSqlSession.getDbSqlSessionFactory().mapStatement("org.flowable.common.engine.impl.persistence.change.ChangeLog.selectFlowableChangeLogVersions"); - List changeLogIds = dbSqlSession.getSqlSession().selectList(selectChangeLogVersionsStatement, getChangeLogTablePrefixName()); - if (changeLogIds != null && !changeLogIds.isEmpty()) { - String changeLogVersion = changeLogIds.get(changeLogIds.size() - 1); - return new ChangeLogVersion(changeLogVersion, getDbVersionForChangelogVersion(changeLogVersion)); + SchemaManagerDatabaseConfiguration databaseConfiguration = getDatabaseConfiguration(); + if (!databaseConfiguration.isTablePrefixIsSchema()) { + changeLogTableName = prependDatabaseTablePrefix(changeLogTableName); + } + try (PreparedStatement statement = databaseConfiguration.getConnection() + .prepareStatement("select ID from " + changeLogTableName + " order by DATEEXECUTED")) { + String changeLogVersion = null; + try (ResultSet resultSet = statement.executeQuery()) { + while (resultSet.next()) { + changeLogVersion = resultSet.getString(1); + } + } + if (changeLogVersion != null) { + return new ChangeLogVersion(changeLogVersion, getDbVersionForChangelogVersion(changeLogVersion)); + } + } catch (SQLException e) { + throw new RuntimeException("Failed to get change log version from " + changeLogTableName, e); } } return new ChangeLogVersion(null, getDbVersionForChangelogVersion(null)); } - protected boolean isMissingTablesException(Exception e) { - String exceptionMessage = e.getMessage(); - if (e.getMessage() != null) { - // Matches message returned from H2 - if ((exceptionMessage.contains("Table")) && (exceptionMessage.contains("not found"))) { - return true; - } - - // Message returned from MySQL and Oracle - if ((exceptionMessage.contains("Table") || exceptionMessage.contains("table")) && (exceptionMessage.contains("doesn't exist"))) { - return true; - } - - // Message returned from Postgres - if ((exceptionMessage.contains("relation") || exceptionMessage.contains("table")) && (exceptionMessage.contains("does not exist"))) { - return true; - } - } - return false; - } - public record ChangeLogVersion(String version, String dbVersion) { } diff --git a/modules/flowable-engine-common/src/main/java/org/flowable/common/engine/impl/db/SchemaManagerDatabaseConfiguration.java b/modules/flowable-engine-common/src/main/java/org/flowable/common/engine/impl/db/SchemaManagerDatabaseConfiguration.java new file mode 100644 index 00000000000..72cc735aa31 --- /dev/null +++ b/modules/flowable-engine-common/src/main/java/org/flowable/common/engine/impl/db/SchemaManagerDatabaseConfiguration.java @@ -0,0 +1,34 @@ +/* Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.flowable.common.engine.impl.db; + +import java.sql.Connection; + +/** + * @author Filip Hrisafov + */ +public interface SchemaManagerDatabaseConfiguration { + + String getDatabaseType(); + + String getDatabaseTablePrefix(); + + boolean isTablePrefixIsSchema(); + + String getDatabaseCatalog(); + + String getDatabaseSchema(); + + Connection getConnection(); + +} diff --git a/modules/flowable-engine-common/src/main/java/org/flowable/common/engine/impl/db/SchemaManagerDatabaseConfigurationSessionFactory.java b/modules/flowable-engine-common/src/main/java/org/flowable/common/engine/impl/db/SchemaManagerDatabaseConfigurationSessionFactory.java new file mode 100644 index 00000000000..77a315c5b6f --- /dev/null +++ b/modules/flowable-engine-common/src/main/java/org/flowable/common/engine/impl/db/SchemaManagerDatabaseConfigurationSessionFactory.java @@ -0,0 +1,33 @@ +/* Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.flowable.common.engine.impl.db; + +import org.flowable.common.engine.impl.interceptor.CommandContext; +import org.flowable.common.engine.impl.interceptor.Session; +import org.flowable.common.engine.impl.interceptor.SessionFactory; + +/** + * @author Filip Hrisafov + */ +public class SchemaManagerDatabaseConfigurationSessionFactory implements SessionFactory { + + @Override + public Class getSessionType() { + return SchemaManagerDatabaseConfiguration.class; + } + + @Override + public Session openSession(CommandContext commandContext) { + return new DbSqlSessionSchemaManagerConfiguration(commandContext.getSession(DbSqlSession.class)); + } +} diff --git a/modules/flowable-engine-common/src/main/java/org/flowable/common/engine/impl/db/SchemaManagerLockConfiguration.java b/modules/flowable-engine-common/src/main/java/org/flowable/common/engine/impl/db/SchemaManagerLockConfiguration.java new file mode 100644 index 00000000000..4500b21eafb --- /dev/null +++ b/modules/flowable-engine-common/src/main/java/org/flowable/common/engine/impl/db/SchemaManagerLockConfiguration.java @@ -0,0 +1,29 @@ +/* Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.flowable.common.engine.impl.db; + +import java.time.Duration; + +import org.flowable.common.engine.api.lock.LockManager; + +/** + * @author Filip Hrisafov + */ +public interface SchemaManagerLockConfiguration { + + boolean isUseLockForDatabaseSchemaUpdate(); + + LockManager getLockManager(String lockName); + + Duration getSchemaLockWaitTime(); +} diff --git a/modules/flowable-engine-common/src/main/java/org/flowable/common/engine/impl/query/BaseNativeQuery.java b/modules/flowable-engine-common/src/main/java/org/flowable/common/engine/impl/query/BaseNativeQuery.java index 343ec5f8fe0..2542b88db14 100644 --- a/modules/flowable-engine-common/src/main/java/org/flowable/common/engine/impl/query/BaseNativeQuery.java +++ b/modules/flowable-engine-common/src/main/java/org/flowable/common/engine/impl/query/BaseNativeQuery.java @@ -56,15 +56,13 @@ protected Map generateParameterMap() { parameterMap.put("needsPaging", firstResult >= 0); String orderBy = (String) parameterMap.get("orderBy"); if (orderBy != null && !"".equals(orderBy)) { - String columns = "RES." + orderBy; - parameterMap.put("orderBy", columns); - parameterMap.put("orderByColumns", columns); - parameterMap.put("orderByForWindow", columns); + orderBy = "RES." + orderBy; } else { - parameterMap.put("orderBy", "order by RES.ID_ asc"); - parameterMap.put("orderByForWindow", "order by RES.ID_ asc"); - parameterMap.put("orderByColumns", "RES.ID_ asc"); + orderBy = "RES.ID_ asc"; } + parameterMap.put("orderBy", "order by " + orderBy); + parameterMap.put("orderByForWindow", "order by " + orderBy); + parameterMap.put("orderByColumns", orderBy); int firstRow = firstResult + 1; parameterMap.put("firstRow", firstRow); diff --git a/modules/flowable-engine-common/src/main/java/org/flowable/common/engine/impl/util/DbUtil.java b/modules/flowable-engine-common/src/main/java/org/flowable/common/engine/impl/util/DbUtil.java new file mode 100644 index 00000000000..7d6ba521af7 --- /dev/null +++ b/modules/flowable-engine-common/src/main/java/org/flowable/common/engine/impl/util/DbUtil.java @@ -0,0 +1,131 @@ +/* Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.flowable.common.engine.impl.util; + +import java.sql.Connection; +import java.sql.DatabaseMetaData; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.Map; +import java.util.Properties; + +import javax.sql.DataSource; + +import org.apache.commons.lang3.StringUtils; +import org.flowable.common.engine.api.FlowableException; +import org.slf4j.Logger; + +/** + * @author Filip Hrisafov + */ +public class DbUtil { + + public static final String PRODUCT_NAME_POSTGRES = "PostgreSQL"; + public static final String PRODUCT_NAME_CRDB = "CockroachDB"; + + public static final String DATABASE_TYPE_H2 = "h2"; + public static final String DATABASE_TYPE_HSQL = "hsql"; + public static final String DATABASE_TYPE_MYSQL = "mysql"; + public static final String DATABASE_TYPE_ORACLE = "oracle"; + public static final String DATABASE_TYPE_POSTGRES = "postgres"; + public static final String DATABASE_TYPE_MSSQL = "mssql"; + public static final String DATABASE_TYPE_DB2 = "db2"; + public static final String DATABASE_TYPE_COCKROACHDB = "cockroachdb"; + + public static Properties getDefaultDatabaseTypeMappings() { + Properties databaseTypeMappings = new Properties(); + databaseTypeMappings.setProperty("H2", DATABASE_TYPE_H2); + databaseTypeMappings.setProperty("HSQL Database Engine", DATABASE_TYPE_HSQL); + databaseTypeMappings.setProperty("MySQL", DATABASE_TYPE_MYSQL); + databaseTypeMappings.setProperty("MariaDB", DATABASE_TYPE_MYSQL); + databaseTypeMappings.setProperty("Oracle", DATABASE_TYPE_ORACLE); + databaseTypeMappings.setProperty(PRODUCT_NAME_POSTGRES, DATABASE_TYPE_POSTGRES); + databaseTypeMappings.setProperty("Microsoft SQL Server", DATABASE_TYPE_MSSQL); + databaseTypeMappings.setProperty(DATABASE_TYPE_DB2, DATABASE_TYPE_DB2); + databaseTypeMappings.setProperty("DB2", DATABASE_TYPE_DB2); + databaseTypeMappings.setProperty("DB2/NT", DATABASE_TYPE_DB2); + databaseTypeMappings.setProperty("DB2/NT64", DATABASE_TYPE_DB2); + databaseTypeMappings.setProperty("DB2 UDP", DATABASE_TYPE_DB2); + databaseTypeMappings.setProperty("DB2/LINUX", DATABASE_TYPE_DB2); + databaseTypeMappings.setProperty("DB2/LINUX390", DATABASE_TYPE_DB2); + databaseTypeMappings.setProperty("DB2/LINUXX8664", DATABASE_TYPE_DB2); + databaseTypeMappings.setProperty("DB2/LINUXZ64", DATABASE_TYPE_DB2); + databaseTypeMappings.setProperty("DB2/LINUXPPC64", DATABASE_TYPE_DB2); + databaseTypeMappings.setProperty("DB2/LINUXPPC64LE", DATABASE_TYPE_DB2); + databaseTypeMappings.setProperty("DB2/400 SQL", DATABASE_TYPE_DB2); + databaseTypeMappings.setProperty("DB2/6000", DATABASE_TYPE_DB2); + databaseTypeMappings.setProperty("DB2 UDB iSeries", DATABASE_TYPE_DB2); + databaseTypeMappings.setProperty("DB2/AIX64", DATABASE_TYPE_DB2); + databaseTypeMappings.setProperty("DB2/HPUX", DATABASE_TYPE_DB2); + databaseTypeMappings.setProperty("DB2/HP64", DATABASE_TYPE_DB2); + databaseTypeMappings.setProperty("DB2/SUN", DATABASE_TYPE_DB2); + databaseTypeMappings.setProperty("DB2/SUN64", DATABASE_TYPE_DB2); + databaseTypeMappings.setProperty("DB2/PTX", DATABASE_TYPE_DB2); + databaseTypeMappings.setProperty("DB2/2", DATABASE_TYPE_DB2); + databaseTypeMappings.setProperty("DB2 UDB AS400", DATABASE_TYPE_DB2); + databaseTypeMappings.setProperty(PRODUCT_NAME_CRDB, DATABASE_TYPE_COCKROACHDB); + return databaseTypeMappings; + } + + public static String determineDatabaseType(DataSource dataSource, Logger logger) { + return determineDatabaseType(dataSource, logger, getDefaultDatabaseTypeMappings()); + } + + public static String determineDatabaseType(DataSource dataSource, Logger logger, Properties databaseTypeMappings) { + Connection connection = null; + String databaseType = null; + try { + connection = dataSource.getConnection(); + DatabaseMetaData databaseMetaData = connection.getMetaData(); + String databaseProductName = databaseMetaData.getDatabaseProductName(); + logger.debug("database product name: '{}'", databaseProductName); + + // CRDB does not expose the version through the jdbc driver, so we need to fetch it through version(). + if (PRODUCT_NAME_POSTGRES.equalsIgnoreCase(databaseProductName)) { + try (PreparedStatement preparedStatement = connection.prepareStatement("select version() as version;"); + ResultSet resultSet = preparedStatement.executeQuery()) { + String version = null; + if (resultSet.next()) { + version = resultSet.getString("version"); + } + + if (StringUtils.isNotEmpty(version) && version.toLowerCase().startsWith(PRODUCT_NAME_CRDB.toLowerCase())) { + databaseProductName = PRODUCT_NAME_CRDB; + logger.info("CockroachDB version '{}' detected", version); + } + } + } + + databaseType = databaseTypeMappings.getProperty(databaseProductName); + if (databaseType == null) { + throw new FlowableException("couldn't deduct database type from database product name '" + databaseProductName + "'"); + } + logger.debug("using database type: {}", databaseType); + + } catch (SQLException e) { + throw new RuntimeException("Exception while initializing Database connection", e); + } finally { + try { + if (connection != null) { + connection.close(); + } + } catch (SQLException e) { + logger.error("Exception while closing the Database connection", e); + } + } + + return databaseType; + } + +} diff --git a/modules/flowable-engine-common/src/main/resources/org/flowable/common/db/mapping/ChangeLog.xml b/modules/flowable-engine-common/src/main/resources/org/flowable/common/db/mapping/ChangeLog.xml deleted file mode 100644 index 02c17b226b8..00000000000 --- a/modules/flowable-engine-common/src/main/resources/org/flowable/common/db/mapping/ChangeLog.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - diff --git a/modules/flowable-engine-common/src/main/resources/org/flowable/common/db/upgrade/flowable.oracle.upgradestep.6400.to.6410.common.sql b/modules/flowable-engine-common/src/main/resources/org/flowable/common/db/upgrade/flowable.oracle.upgradestep.6400.to.6410.common.sql index 29a517cb51a..cd2ef03a3a5 100644 --- a/modules/flowable-engine-common/src/main/resources/org/flowable/common/db/upgrade/flowable.oracle.upgradestep.6400.to.6410.common.sql +++ b/modules/flowable-engine-common/src/main/resources/org/flowable/common/db/upgrade/flowable.oracle.upgradestep.6400.to.6410.common.sql @@ -27,7 +27,6 @@ create table ACT_HI_ENTITYLINK ( REF_SCOPE_ID_ NVARCHAR2(255), REF_SCOPE_TYPE_ NVARCHAR2(255), REF_SCOPE_DEFINITION_ID_ NVARCHAR2(255), - HIERARCHY_TYPE_ NVARCHAR2(255), primary key (ID_) ); diff --git a/modules/flowable-engine/src/main/java/org/flowable/engine/impl/bpmn/behavior/ServiceTaskDelegateExpressionActivityBehavior.java b/modules/flowable-engine/src/main/java/org/flowable/engine/impl/bpmn/behavior/ServiceTaskDelegateExpressionActivityBehavior.java index 0cb228c8243..d31d912b475 100644 --- a/modules/flowable-engine/src/main/java/org/flowable/engine/impl/bpmn/behavior/ServiceTaskDelegateExpressionActivityBehavior.java +++ b/modules/flowable-engine/src/main/java/org/flowable/engine/impl/bpmn/behavior/ServiceTaskDelegateExpressionActivityBehavior.java @@ -36,6 +36,8 @@ import org.flowable.engine.impl.delegate.ActivityBehavior; import org.flowable.engine.impl.delegate.ActivityBehaviorInvocation; import org.flowable.engine.impl.delegate.TriggerableActivityBehavior; +import org.flowable.engine.impl.delegate.TriggerableJavaDelegate; +import org.flowable.engine.impl.delegate.TriggerableJavaDelegateContextImpl; import org.flowable.engine.impl.delegate.invocation.DelegateInvocation; import org.flowable.engine.impl.delegate.invocation.FutureJavaDelegateInvocation; import org.flowable.engine.impl.delegate.invocation.JavaDelegateInvocation; @@ -79,6 +81,7 @@ public void trigger(DelegateExecution execution, String signalName, Object signa Object delegate = DelegateExpressionUtil.resolveDelegateExpression(expression, execution, fieldDeclarations); ProcessEngineConfigurationImpl processEngineConfiguration = CommandContextUtil.getProcessEngineConfiguration(); boolean loggingSessionEnabled = processEngineConfiguration.isLoggingSessionEnabled(); + TriggerableJavaDelegateContextImpl triggerableJavaDelegateContext = null; try { if (triggerable && delegate instanceof TriggerableActivityBehavior) { if (loggingSessionEnabled) { @@ -92,7 +95,17 @@ public void trigger(DelegateExecution execution, String signalName, Object signa BpmnLoggingSessionUtil.addLoggingData(LoggingSessionConstants.TYPE_SERVICE_TASK_AFTER_TRIGGER, "Triggered service task with delegate " + delegate, execution); } - + } else if (triggerable && delegate instanceof TriggerableJavaDelegate triggerableJavaDelegate) { + if (loggingSessionEnabled) { + BpmnLoggingSessionUtil.addLoggingData(LoggingSessionConstants.TYPE_SERVICE_TASK_BEFORE_TRIGGER, + "Triggering service task with java delegate " + delegate, execution); + } + triggerableJavaDelegateContext = new TriggerableJavaDelegateContextImpl(execution, signalName, signalData); + triggerableJavaDelegate.trigger(triggerableJavaDelegateContext); + if (loggingSessionEnabled) { + BpmnLoggingSessionUtil.addLoggingData(LoggingSessionConstants.TYPE_SERVICE_TASK_AFTER_TRIGGER, + "Triggered service task with java delegate " + delegate, execution); + } } else if (loggingSessionEnabled) { if (!triggerable) { BpmnLoggingSessionUtil.addLoggingData(LoggingSessionConstants.TYPE_SERVICE_TASK_WRONG_TRIGGER, @@ -103,7 +116,10 @@ public void trigger(DelegateExecution execution, String signalName, Object signa } } - leave(execution); + + if (triggerableJavaDelegateContext == null || triggerableJavaDelegateContext.shouldLeave()) { + leave(execution); + } } catch (Exception exc) { handleException(exc, execution, loggingSessionEnabled); } diff --git a/modules/flowable-engine/src/main/java/org/flowable/engine/impl/bpmn/behavior/ServiceTaskFutureJavaDelegateActivityBehavior.java b/modules/flowable-engine/src/main/java/org/flowable/engine/impl/bpmn/behavior/ServiceTaskFutureJavaDelegateActivityBehavior.java index f1d4335edc1..1ee2a2f8725 100644 --- a/modules/flowable-engine/src/main/java/org/flowable/engine/impl/bpmn/behavior/ServiceTaskFutureJavaDelegateActivityBehavior.java +++ b/modules/flowable-engine/src/main/java/org/flowable/engine/impl/bpmn/behavior/ServiceTaskFutureJavaDelegateActivityBehavior.java @@ -32,6 +32,8 @@ import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl; import org.flowable.engine.impl.delegate.ActivityBehavior; import org.flowable.engine.impl.delegate.TriggerableActivityBehavior; +import org.flowable.engine.impl.delegate.TriggerableJavaDelegate; +import org.flowable.engine.impl.delegate.TriggerableJavaDelegateContextImpl; import org.flowable.engine.impl.delegate.invocation.FutureJavaDelegateInvocation; import org.flowable.engine.impl.persistence.entity.ExecutionEntity; import org.flowable.engine.impl.util.BpmnLoggingSessionUtil; @@ -40,7 +42,7 @@ /** * @author Filip Hrisafov */ -public class ServiceTaskFutureJavaDelegateActivityBehavior extends TaskActivityBehavior implements ActivityBehavior { +public class ServiceTaskFutureJavaDelegateActivityBehavior extends TaskActivityBehavior implements ActivityBehavior, TriggerableJavaDelegate { private static final long serialVersionUID = 1L; @@ -77,7 +79,18 @@ public void trigger(DelegateExecution execution, String signalName, Object signa "Triggered service task with java class " + futureJavaDelegate.getClass().getName(), execution); } - leave(execution); + } else if (triggerable && futureJavaDelegate instanceof TriggerableJavaDelegate triggerableJavaDelegate) { + if (processEngineConfiguration.isLoggingSessionEnabled()) { + BpmnLoggingSessionUtil.addLoggingData(LoggingSessionConstants.TYPE_SERVICE_TASK_BEFORE_TRIGGER, + "Triggering service task with java class " + futureJavaDelegate.getClass().getName(), execution); + } + TriggerableJavaDelegateContextImpl context = new TriggerableJavaDelegateContextImpl(execution, null, null); + triggerableJavaDelegate.trigger(context); + + if (processEngineConfiguration.isLoggingSessionEnabled()) { + BpmnLoggingSessionUtil.addLoggingData(LoggingSessionConstants.TYPE_SERVICE_TASK_AFTER_TRIGGER, + "Triggered service task with java class " + futureJavaDelegate.getClass().getName(), execution); + } } else { if (processEngineConfiguration.isLoggingSessionEnabled()) { @@ -147,7 +160,49 @@ public void execute(DelegateExecution execution) { leave(execution); } } + } + + @Override + public void trigger(Context context) { + CommandContext commandContext = CommandContextUtil.getCommandContext(); + ProcessEngineConfigurationImpl processEngineConfiguration = CommandContextUtil.getProcessEngineConfiguration(commandContext); + if (triggerable && futureJavaDelegate instanceof TriggerableJavaDelegate triggerableJavaDelegate) { + if (processEngineConfiguration.isLoggingSessionEnabled()) { + BpmnLoggingSessionUtil.addLoggingData(LoggingSessionConstants.TYPE_SERVICE_TASK_BEFORE_TRIGGER, + "Triggering service task with java class " + futureJavaDelegate.getClass().getName(), context.getExecution()); + } + triggerableJavaDelegate.trigger(context); + + if (processEngineConfiguration.isLoggingSessionEnabled()) { + BpmnLoggingSessionUtil.addLoggingData(LoggingSessionConstants.TYPE_SERVICE_TASK_AFTER_TRIGGER, + "Triggered service task with java class " + futureJavaDelegate.getClass().getName(), context.getExecution()); + } + } else if (triggerable && futureJavaDelegate instanceof TriggerableActivityBehavior) { + if (processEngineConfiguration.isLoggingSessionEnabled()) { + BpmnLoggingSessionUtil.addLoggingData(LoggingSessionConstants.TYPE_SERVICE_TASK_BEFORE_TRIGGER, + "Triggering service task with java class " + futureJavaDelegate.getClass().getName(), context.getExecution()); + } + + ((TriggerableActivityBehavior) futureJavaDelegate).trigger(context.getExecution(), context.getSignalName(), context.getSignalData()); + + if (processEngineConfiguration.isLoggingSessionEnabled()) { + BpmnLoggingSessionUtil.addLoggingData(LoggingSessionConstants.TYPE_SERVICE_TASK_AFTER_TRIGGER, + "Triggered service task with java class " + futureJavaDelegate.getClass().getName(), context.getExecution()); + } + + } else { + if (processEngineConfiguration.isLoggingSessionEnabled()) { + if (!triggerable) { + BpmnLoggingSessionUtil.addLoggingData(LoggingSessionConstants.TYPE_SERVICE_TASK_WRONG_TRIGGER, + "Service task with java class triggered but not triggerable " + futureJavaDelegate.getClass().getName(), context.getExecution()); + } else { + BpmnLoggingSessionUtil.addLoggingData(LoggingSessionConstants.TYPE_SERVICE_TASK_WRONG_TRIGGER, + "Service task with java class triggered but not implementing TriggerableActivityBehavior " + futureJavaDelegate.getClass() + .getName(), context.getExecution()); + } + } + } } protected void handleException(Throwable throwable, DelegateExecution execution, boolean loggingSessionEnabled) { diff --git a/modules/flowable-engine/src/main/java/org/flowable/engine/impl/bpmn/behavior/ServiceTaskJavaDelegateActivityBehavior.java b/modules/flowable-engine/src/main/java/org/flowable/engine/impl/bpmn/behavior/ServiceTaskJavaDelegateActivityBehavior.java index 3920e1b1b11..64528c96862 100644 --- a/modules/flowable-engine/src/main/java/org/flowable/engine/impl/bpmn/behavior/ServiceTaskJavaDelegateActivityBehavior.java +++ b/modules/flowable-engine/src/main/java/org/flowable/engine/impl/bpmn/behavior/ServiceTaskJavaDelegateActivityBehavior.java @@ -23,6 +23,8 @@ import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl; import org.flowable.engine.impl.delegate.ActivityBehavior; import org.flowable.engine.impl.delegate.TriggerableActivityBehavior; +import org.flowable.engine.impl.delegate.TriggerableJavaDelegate; +import org.flowable.engine.impl.delegate.TriggerableJavaDelegateContextImpl; import org.flowable.engine.impl.delegate.invocation.JavaDelegateInvocation; import org.flowable.engine.impl.util.BpmnLoggingSessionUtil; import org.flowable.engine.impl.util.CommandContextUtil; @@ -30,7 +32,7 @@ /** * @author Tom Baeyens */ -public class ServiceTaskJavaDelegateActivityBehavior extends TaskActivityBehavior implements ActivityBehavior, ExecutionListener { +public class ServiceTaskJavaDelegateActivityBehavior extends TaskActivityBehavior implements ActivityBehavior, ExecutionListener, TriggerableJavaDelegate { private static final long serialVersionUID = 1L; @@ -65,8 +67,19 @@ public void trigger(DelegateExecution execution, String signalName, Object signa "Triggered service task with java class " + javaDelegate.getClass().getName(), execution); } - leave(execution); - + } else if (triggerable && javaDelegate instanceof TriggerableJavaDelegate triggerableJavaDelegate) { + TriggerableJavaDelegateContextImpl triggerableJavaDelegateContext = null; + if (processEngineConfiguration.isLoggingSessionEnabled()) { + BpmnLoggingSessionUtil.addLoggingData(LoggingSessionConstants.TYPE_SERVICE_TASK_BEFORE_TRIGGER, + "Triggering service task with java delegate " + triggerableJavaDelegate, execution); + } + triggerableJavaDelegateContext = new TriggerableJavaDelegateContextImpl(execution, signalName, signalData); + triggerableJavaDelegate.trigger(triggerableJavaDelegateContext); + if (processEngineConfiguration.isLoggingSessionEnabled()) { + BpmnLoggingSessionUtil.addLoggingData(LoggingSessionConstants.TYPE_SERVICE_TASK_AFTER_TRIGGER, + "Triggered service task with delegate " + triggerableJavaDelegate, execution); + } + } else { if (processEngineConfiguration.isLoggingSessionEnabled()) { if (!triggerable) { @@ -132,4 +145,46 @@ public void execute(DelegateExecution execution) { public void notify(DelegateExecution execution) { execute(execution); } + + @Override + public void trigger(Context context) { + CommandContext commandContext = CommandContextUtil.getCommandContext(); + ProcessEngineConfigurationImpl processEngineConfiguration = CommandContextUtil.getProcessEngineConfiguration(commandContext); + + if (triggerable && javaDelegate instanceof TriggerableJavaDelegate triggerableJavaDelegate) { + if (processEngineConfiguration.isLoggingSessionEnabled()) { + BpmnLoggingSessionUtil.addLoggingData(LoggingSessionConstants.TYPE_SERVICE_TASK_BEFORE_TRIGGER, + "Triggering service task with java delegate " + triggerableJavaDelegate, context.getExecution()); + } + triggerableJavaDelegate.trigger(context); + if (processEngineConfiguration.isLoggingSessionEnabled()) { + BpmnLoggingSessionUtil.addLoggingData(LoggingSessionConstants.TYPE_SERVICE_TASK_AFTER_TRIGGER, + "Triggered service task with delegate " + triggerableJavaDelegate, context.getExecution()); + } + } else if (triggerable && javaDelegate instanceof TriggerableActivityBehavior) { + if (processEngineConfiguration.isLoggingSessionEnabled()) { + BpmnLoggingSessionUtil.addLoggingData(LoggingSessionConstants.TYPE_SERVICE_TASK_BEFORE_TRIGGER, + "Triggering service task with java class " + javaDelegate.getClass().getName(), context.getExecution()); + } + + ((TriggerableActivityBehavior) javaDelegate).trigger(context.getExecution(), context.getSignalName(), context.getSignalData()); + + if (processEngineConfiguration.isLoggingSessionEnabled()) { + BpmnLoggingSessionUtil.addLoggingData(LoggingSessionConstants.TYPE_SERVICE_TASK_AFTER_TRIGGER, + "Triggered service task with java class " + javaDelegate.getClass().getName(), context.getExecution()); + } + } else { + if (processEngineConfiguration.isLoggingSessionEnabled()) { + if (!triggerable) { + BpmnLoggingSessionUtil.addLoggingData(LoggingSessionConstants.TYPE_SERVICE_TASK_WRONG_TRIGGER, + "Service task with java class triggered but not triggerable " + javaDelegate.getClass().getName(), context.getExecution()); + + } else { + BpmnLoggingSessionUtil.addLoggingData(LoggingSessionConstants.TYPE_SERVICE_TASK_WRONG_TRIGGER, + "Service task with java class triggered but not implementing TriggerableActivityBehavior " + javaDelegate.getClass().getName(), + context.getExecution()); + } + } + } + } } diff --git a/modules/flowable-engine/src/main/java/org/flowable/engine/impl/bpmn/helper/ClassDelegate.java b/modules/flowable-engine/src/main/java/org/flowable/engine/impl/bpmn/helper/ClassDelegate.java index 16b40fbacb4..36c93fb14e4 100644 --- a/modules/flowable-engine/src/main/java/org/flowable/engine/impl/bpmn/helper/ClassDelegate.java +++ b/modules/flowable-engine/src/main/java/org/flowable/engine/impl/bpmn/helper/ClassDelegate.java @@ -42,6 +42,8 @@ import org.flowable.engine.impl.delegate.ActivityBehavior; import org.flowable.engine.impl.delegate.SubProcessActivityBehavior; import org.flowable.engine.impl.delegate.TriggerableActivityBehavior; +import org.flowable.engine.impl.delegate.TriggerableJavaDelegate; +import org.flowable.engine.impl.delegate.TriggerableJavaDelegateContextImpl; import org.flowable.engine.impl.delegate.invocation.TaskListenerInvocation; import org.flowable.engine.impl.persistence.entity.ExecutionEntity; import org.flowable.engine.impl.util.CommandContextUtil; @@ -208,7 +210,21 @@ public void trigger(DelegateExecution execution, String signalName, Object signa if (activityBehaviorInstance == null) { activityBehaviorInstance = getActivityBehaviorInstance(); } - if (activityBehaviorInstance instanceof TriggerableActivityBehavior) { + if (activityBehaviorInstance instanceof TriggerableJavaDelegate triggerableJavaDelegate) { + try { + TriggerableJavaDelegateContextImpl context = new TriggerableJavaDelegateContextImpl(execution, null, null); + triggerableJavaDelegate.trigger(context); + if (triggerable && context.shouldLeave()) { + leave(execution); + } + } catch (BpmnError error) { + ErrorPropagation.propagateError(error, execution); + } catch (RuntimeException e) { + if (!ErrorPropagation.mapException(e, (ExecutionEntity) execution, mapExceptions)) { + throw e; + } + } + } else if (activityBehaviorInstance instanceof TriggerableActivityBehavior) { try { ((TriggerableActivityBehavior) activityBehaviorInstance).trigger(execution, signalName, signalData); if (triggerable) { diff --git a/modules/flowable-engine/src/main/java/org/flowable/engine/impl/cfg/ProcessEngineConfigurationImpl.java b/modules/flowable-engine/src/main/java/org/flowable/engine/impl/cfg/ProcessEngineConfigurationImpl.java index 69feae70ceb..d180eaa8bce 100755 --- a/modules/flowable-engine/src/main/java/org/flowable/engine/impl/cfg/ProcessEngineConfigurationImpl.java +++ b/modules/flowable-engine/src/main/java/org/flowable/engine/impl/cfg/ProcessEngineConfigurationImpl.java @@ -1252,6 +1252,7 @@ public void initSessionFactories() { if (usingRelationalDatabase) { initDbSqlSessionFactory(); + initSchemaManagerDatabaseConfigurationSessionFactory(); } if (agendaFactory != null) { diff --git a/modules/flowable-engine/src/main/java/org/flowable/engine/impl/db/ProcessDbSchemaManager.java b/modules/flowable-engine/src/main/java/org/flowable/engine/impl/db/ProcessDbSchemaManager.java index 9d3d4db5da5..0374aaa4cb1 100644 --- a/modules/flowable-engine/src/main/java/org/flowable/engine/impl/db/ProcessDbSchemaManager.java +++ b/modules/flowable-engine/src/main/java/org/flowable/engine/impl/db/ProcessDbSchemaManager.java @@ -262,27 +262,6 @@ protected String getCleanVersion(String versionString) { } } - protected boolean isMissingTablesException(Exception e) { - String exceptionMessage = e.getMessage(); - if (e.getMessage() != null) { - // Matches message returned from H2 - if ((exceptionMessage.contains("Table")) && (exceptionMessage.contains("not found"))) { - return true; - } - - // Message returned from MySQL and Oracle - if ((exceptionMessage.contains("Table") || exceptionMessage.contains("table")) && (exceptionMessage.contains("doesn't exist"))) { - return true; - } - - // Message returned from Postgres - if ((exceptionMessage.contains("relation") || exceptionMessage.contains("table")) && (exceptionMessage.contains("does not exist"))) { - return true; - } - } - return false; - } - protected ProcessEngineConfigurationImpl getProcessEngineConfiguration() { return CommandContextUtil.getProcessEngineConfiguration(); } diff --git a/modules/flowable-engine/src/main/java/org/flowable/engine/impl/delegate/TriggerableJavaDelegate.java b/modules/flowable-engine/src/main/java/org/flowable/engine/impl/delegate/TriggerableJavaDelegate.java new file mode 100644 index 00000000000..feab2b2f53a --- /dev/null +++ b/modules/flowable-engine/src/main/java/org/flowable/engine/impl/delegate/TriggerableJavaDelegate.java @@ -0,0 +1,36 @@ +/* Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.flowable.engine.impl.delegate; + +import org.flowable.engine.delegate.DelegateExecution; + +/** + * Similar to the {@link TriggerableActivityBehavior} but with a context that allows the implementing class + * to decide if the execution should be left after the trigger or not + * @author Christopher Welsch + */ +public interface TriggerableJavaDelegate { + + void trigger(TriggerableJavaDelegate.Context context); + + interface Context { + + void doNotLeave(); + + Object getSignalData(); + + DelegateExecution getExecution(); + + String getSignalName(); + } +} diff --git a/modules/flowable-engine/src/main/java/org/flowable/engine/impl/delegate/TriggerableJavaDelegateContextImpl.java b/modules/flowable-engine/src/main/java/org/flowable/engine/impl/delegate/TriggerableJavaDelegateContextImpl.java new file mode 100644 index 00000000000..b6b61e57bc9 --- /dev/null +++ b/modules/flowable-engine/src/main/java/org/flowable/engine/impl/delegate/TriggerableJavaDelegateContextImpl.java @@ -0,0 +1,67 @@ +/* Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.flowable.engine.impl.delegate; + +import org.flowable.engine.delegate.DelegateExecution; + +/** + * @author Christopher Welsch + */ +public class TriggerableJavaDelegateContextImpl implements TriggerableJavaDelegate.Context { + + protected DelegateExecution execution; + protected String signalName; + protected Object signalData; + + protected boolean shouldLeave = true; + + public TriggerableJavaDelegateContextImpl(DelegateExecution execution, String signalName, Object signalData) { + this.execution = execution; + this.signalName = signalName; + this.signalData = signalData; + } + @Override + public DelegateExecution getExecution() { + return execution; + } + + public void setExecution(DelegateExecution execution) { + this.execution = execution; + } + + @Override + public String getSignalName() { + return signalName; + } + + public void setSignalName(String signalName) { + this.signalName = signalName; + } + @Override + public Object getSignalData() { + return signalData; + } + + public void setSignalData(Object signalData) { + this.signalData = signalData; + } + + @Override + public void doNotLeave() { + shouldLeave = false; + } + + public boolean shouldLeave() { + return shouldLeave; + } +} diff --git a/modules/flowable-engine/src/test/java/org/flowable/engine/test/api/identity/GroupQueryTest.java b/modules/flowable-engine/src/test/java/org/flowable/engine/test/api/identity/GroupQueryTest.java index f6fa9020a09..b459fabe1f4 100644 --- a/modules/flowable-engine/src/test/java/org/flowable/engine/test/api/identity/GroupQueryTest.java +++ b/modules/flowable-engine/src/test/java/org/flowable/engine/test/api/identity/GroupQueryTest.java @@ -20,6 +20,7 @@ import org.flowable.common.engine.api.FlowableException; import org.flowable.common.engine.api.FlowableIllegalArgumentException; +import org.flowable.common.engine.impl.AbstractEngineConfiguration; import org.flowable.engine.impl.test.PluggableFlowableTestCase; import org.flowable.idm.api.Group; import org.flowable.idm.api.GroupQuery; @@ -258,4 +259,27 @@ public void testNativeQuery() { assertThat(identityService.createNativeGroupQuery().sql(baseQuerySql + " where ID_ = #{id}").parameter("id", "admin").listPage(0, 1)).hasSize(1); } + @Test + public void testNativeQueryOrder() { + StringBuilder baseQueryBuilder = new StringBuilder(); + baseQueryBuilder.append("SELECT "); + if (AbstractEngineConfiguration.DATABASE_TYPE_MSSQL.equals(processEngineConfiguration.getDatabaseType())) { + baseQueryBuilder.append("top 100 percent "); + } + baseQueryBuilder.append("* FROM ") + .append(IdentityTestUtil.getTableName("ACT_ID_GROUP", processEngineConfiguration)) + .append(" order by ID_ desc"); + String baseQuerySql = baseQueryBuilder.toString(); + + assertThat(identityService.createNativeGroupQuery().sql(baseQuerySql).list()) + .extracting(Group::getId) + .containsExactly("muppets", "mammals", "frogs", "admin"); + + assertThat(identityService.createNativeGroupQuery().sql(baseQuerySql) + .parameter("orderBy", "ID_ desc") + .listPage(0, 2)) + .extracting(Group::getId) + .containsExactly("muppets", "mammals"); + } + } diff --git a/modules/flowable-engine/src/test/java/org/flowable/engine/test/bpmn/servicetask/LeavingFutureJavaDelegateServiceTask.java b/modules/flowable-engine/src/test/java/org/flowable/engine/test/bpmn/servicetask/LeavingFutureJavaDelegateServiceTask.java new file mode 100644 index 00000000000..bb2167c5a7a --- /dev/null +++ b/modules/flowable-engine/src/test/java/org/flowable/engine/test/bpmn/servicetask/LeavingFutureJavaDelegateServiceTask.java @@ -0,0 +1,44 @@ +/* Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.flowable.engine.test.bpmn.servicetask; + +import java.io.Serializable; +import java.util.Map; +import java.util.concurrent.CompletableFuture; + +import org.flowable.common.engine.api.async.AsyncTaskInvoker; +import org.flowable.engine.delegate.DelegateExecution; +import org.flowable.engine.delegate.FutureJavaDelegate; +import org.flowable.engine.delegate.MapBasedFlowableFutureJavaDelegate; +import org.flowable.engine.delegate.ReadOnlyDelegateExecution; +import org.flowable.engine.impl.delegate.TriggerableJavaDelegate; + +public class LeavingFutureJavaDelegateServiceTask + implements TriggerableJavaDelegate, MapBasedFlowableFutureJavaDelegate, Serializable { + + + public static int count = 0; + + @Override + public void trigger(Context context) { + count++; + } + + @Override + public Map execute(ReadOnlyDelegateExecution inputData) { + count++; + return Map.of("SomeKey", "SomeValue"); + } + +} diff --git a/modules/flowable-engine/src/test/java/org/flowable/engine/test/bpmn/servicetask/LeavingJavaDelegate.java b/modules/flowable-engine/src/test/java/org/flowable/engine/test/bpmn/servicetask/LeavingJavaDelegate.java new file mode 100644 index 00000000000..d93ed8c900c --- /dev/null +++ b/modules/flowable-engine/src/test/java/org/flowable/engine/test/bpmn/servicetask/LeavingJavaDelegate.java @@ -0,0 +1,43 @@ +/* Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.flowable.engine.test.bpmn.servicetask; + +import java.io.Serializable; + +import org.flowable.engine.delegate.DelegateExecution; +import org.flowable.engine.delegate.JavaDelegate; +import org.flowable.engine.impl.delegate.TriggerableJavaDelegate; + +public class LeavingJavaDelegate implements JavaDelegate, TriggerableJavaDelegate, Serializable { + + @Override + public void execute(DelegateExecution execution) { + incrementCount(execution); + } + + public void incrementCount(DelegateExecution execution) { + String variableName = "count"; + int count = 0; + if (execution.hasVariable(variableName)) { + count = (int) execution.getVariable(variableName); + } + count++; + execution.setVariable(variableName, count); + } + + @Override + public void trigger(Context context) { + incrementCount(context.getExecution()); + } +} diff --git a/modules/flowable-engine/src/test/java/org/flowable/engine/test/bpmn/servicetask/NotLeavingFutureJavaDelegateServiceTask.java b/modules/flowable-engine/src/test/java/org/flowable/engine/test/bpmn/servicetask/NotLeavingFutureJavaDelegateServiceTask.java new file mode 100644 index 00000000000..0103c6f3461 --- /dev/null +++ b/modules/flowable-engine/src/test/java/org/flowable/engine/test/bpmn/servicetask/NotLeavingFutureJavaDelegateServiceTask.java @@ -0,0 +1,40 @@ +/* Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.flowable.engine.test.bpmn.servicetask; + +import java.io.Serializable; +import java.util.Map; + +import org.flowable.engine.delegate.MapBasedFlowableFutureJavaDelegate; +import org.flowable.engine.delegate.ReadOnlyDelegateExecution; +import org.flowable.engine.impl.delegate.TriggerableJavaDelegate; + +public class NotLeavingFutureJavaDelegateServiceTask + implements TriggerableJavaDelegate, MapBasedFlowableFutureJavaDelegate, Serializable { + + public static int count = 0; + + @Override + public void trigger(Context context) { + count++; + context.doNotLeave(); + } + + @Override + public Map execute(ReadOnlyDelegateExecution inputData) { + count++; + return Map.of("SomeKey", "SomeValue"); + } + +} diff --git a/modules/flowable-engine/src/test/java/org/flowable/engine/test/bpmn/servicetask/NotLeavingJavaDelegate.java b/modules/flowable-engine/src/test/java/org/flowable/engine/test/bpmn/servicetask/NotLeavingJavaDelegate.java new file mode 100644 index 00000000000..b6f9687c47c --- /dev/null +++ b/modules/flowable-engine/src/test/java/org/flowable/engine/test/bpmn/servicetask/NotLeavingJavaDelegate.java @@ -0,0 +1,44 @@ +/* Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.flowable.engine.test.bpmn.servicetask; + +import java.io.Serializable; + +import org.flowable.engine.delegate.DelegateExecution; +import org.flowable.engine.delegate.JavaDelegate; +import org.flowable.engine.impl.delegate.TriggerableJavaDelegate; + +public class NotLeavingJavaDelegate implements JavaDelegate, TriggerableJavaDelegate, Serializable { + + @Override + public void execute(DelegateExecution execution) { + incrementCount(execution); + } + + public void incrementCount(DelegateExecution execution) { + String variableName = "count"; + int count = 0; + if (execution.hasVariable(variableName)) { + count = (int) execution.getVariable(variableName); + } + count++; + execution.setVariable(variableName, count); + } + + @Override + public void trigger(Context context) { + incrementCount(context.getExecution()); + context.doNotLeave(); + } +} diff --git a/modules/flowable-engine/src/test/java/org/flowable/engine/test/bpmn/servicetask/ThrowBpmnErrorTriggerableJavaDelegateServiceTask.java b/modules/flowable-engine/src/test/java/org/flowable/engine/test/bpmn/servicetask/ThrowBpmnErrorTriggerableJavaDelegateServiceTask.java new file mode 100644 index 00000000000..7e3a84445cf --- /dev/null +++ b/modules/flowable-engine/src/test/java/org/flowable/engine/test/bpmn/servicetask/ThrowBpmnErrorTriggerableJavaDelegateServiceTask.java @@ -0,0 +1,33 @@ +/* Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.flowable.engine.test.bpmn.servicetask; + +import java.io.Serializable; + +import org.flowable.engine.delegate.BpmnError; +import org.flowable.engine.delegate.DelegateExecution; +import org.flowable.engine.delegate.JavaDelegate; +import org.flowable.engine.impl.delegate.TriggerableJavaDelegate; + +public class ThrowBpmnErrorTriggerableJavaDelegateServiceTask implements JavaDelegate, TriggerableJavaDelegate, Serializable { + + @Override + public void execute(DelegateExecution execution) { + } + @Override + public void trigger(Context context) { + throw new BpmnError("testErrorCode"); + } + +} diff --git a/modules/flowable-engine/src/test/java/org/flowable/engine/test/bpmn/servicetask/TriggerableJavaDelegateServiceTask.java b/modules/flowable-engine/src/test/java/org/flowable/engine/test/bpmn/servicetask/TriggerableJavaDelegateServiceTask.java new file mode 100644 index 00000000000..fcf700aca52 --- /dev/null +++ b/modules/flowable-engine/src/test/java/org/flowable/engine/test/bpmn/servicetask/TriggerableJavaDelegateServiceTask.java @@ -0,0 +1,55 @@ +/* Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.flowable.engine.test.bpmn.servicetask; + +import java.io.Serializable; + +import org.flowable.engine.delegate.DelegateExecution; +import org.flowable.engine.delegate.JavaDelegate; +import org.flowable.engine.impl.delegate.TriggerableJavaDelegate; + +public class TriggerableJavaDelegateServiceTask implements TriggerableJavaDelegate, JavaDelegate, Serializable { + + protected boolean doNotLeave = false; + + public TriggerableJavaDelegateServiceTask() { + } + + public TriggerableJavaDelegateServiceTask(boolean doNotLeave) { + this.doNotLeave = doNotLeave; + } + + @Override + public void execute(DelegateExecution execution) { + incrementCount(execution); + } + + public void incrementCount(DelegateExecution execution) { + String variableName = "count"; + int count = 0; + if (execution.hasVariable(variableName)) { + count = (int) execution.getVariable(variableName); + } + count++; + execution.setVariable(variableName, count); + } + + @Override + public void trigger(TriggerableJavaDelegate.Context context) { + incrementCount(context.getExecution()); + if (doNotLeave) { + context.doNotLeave(); + } + } +} diff --git a/modules/flowable-engine/src/test/java/org/flowable/engine/test/bpmn/servicetask/TriggerableJavaDelegateServiceTaskTest.java b/modules/flowable-engine/src/test/java/org/flowable/engine/test/bpmn/servicetask/TriggerableJavaDelegateServiceTaskTest.java new file mode 100644 index 00000000000..6c704ee0b44 --- /dev/null +++ b/modules/flowable-engine/src/test/java/org/flowable/engine/test/bpmn/servicetask/TriggerableJavaDelegateServiceTaskTest.java @@ -0,0 +1,265 @@ +/* Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.flowable.engine.test.bpmn.servicetask; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.flowable.engine.impl.jobexecutor.AsyncContinuationJobHandler; +import org.flowable.engine.impl.jobexecutor.AsyncTriggerJobHandler; +import org.flowable.engine.impl.test.PluggableFlowableTestCase; +import org.flowable.engine.runtime.Execution; +import org.flowable.engine.test.Deployment; +import org.flowable.job.api.Job; +import org.junit.jupiter.api.Test; + +public class TriggerableJavaDelegateServiceTaskTest extends PluggableFlowableTestCase { + + @Test + @Deployment + public void testClassDelegate() { + String processId = runtimeService.startProcessInstanceByKey("process").getProcessInstanceId(); + + Execution execution = runtimeService.createExecutionQuery().processInstanceId(processId).activityId("service1").singleResult(); + assertThat(execution).isNotNull(); + int count = (int) runtimeService.getVariable(processId, "count"); + assertThat(count).isEqualTo(1); + + Map processVariables = new HashMap<>(); + processVariables.put("count", ++count); + runtimeService.trigger(execution.getId(), processVariables, null); + + execution = runtimeService.createExecutionQuery().processInstanceId(processId).activityId("usertask1").singleResult(); + assertThat(execution).isNotNull(); + assertThat(runtimeService.getVariable(processId, "count")).isEqualTo(3); + } + + @Test + @Deployment(resources = "org/flowable/engine/test/bpmn/servicetask/TriggerableJavaDelegateServiceTaskTest.testClassDelegateTriggerException.bpmn20.xml") + public void classDelegateTriggerBpmnException() { + String processId = runtimeService.startProcessInstanceByKey("process").getProcessInstanceId(); + Execution execution = runtimeService.createExecutionQuery().processInstanceId(processId).activityId("service1").singleResult(); + runtimeService.trigger(execution.getId()); + + assertThatBpmnSubProcessActive(processId); + } + + @Test + @Deployment + void classDelegateTriggerExceptionWithoutWaitStateInCatch() { + String processId = runtimeService.startProcessInstanceByKey("process").getProcessInstanceId(); + Execution execution = runtimeService.createExecutionQuery().processInstanceId(processId).activityId("service1").singleResult(); + runtimeService.trigger(execution.getId()); + + assertThat(runtimeService.createProcessInstanceQuery().processInstanceId(processId).count()).isZero(); + } + + @Test + @Deployment + public void testDelegateExpression() { + Map varMap = new HashMap<>(); + varMap.put("triggerableServiceTask", new TriggerableJavaDelegateServiceTask()); + + String processId = runtimeService.startProcessInstanceByKey("process", varMap).getProcessInstanceId(); + + Execution execution = runtimeService.createExecutionQuery().processInstanceId(processId).activityId("service1").singleResult(); + assertThat(execution).isNotNull(); + int count = (int) runtimeService.getVariable(processId, "count"); + assertThat(count).isEqualTo(1); + + Map processVariables = new HashMap<>(); + processVariables.put("count", ++count); + runtimeService.trigger(execution.getId(), processVariables, null); + + execution = runtimeService.createExecutionQuery().processInstanceId(processId).activityId("usertask1").singleResult(); + assertThat(execution).isNotNull(); + assertThat(runtimeService.getVariable(processId, "count")).isEqualTo(3); + } + + @Test + @Deployment(resources = "org/flowable/engine/test/bpmn/servicetask/TriggerableJavaDelegateServiceTaskTest.testDelegateExpression.bpmn20.xml") + public void delegateExpressionTriggerBmnError() { + Map varMap = new HashMap<>(); + varMap.put("triggerableServiceTask", new ThrowBpmnErrorTriggerableJavaDelegateServiceTask()); + + String processId = runtimeService.startProcessInstanceByKey("process", varMap).getProcessInstanceId(); + Execution execution = runtimeService.createExecutionQuery().processInstanceId(processId).activityId("service1").singleResult(); + runtimeService.trigger(execution.getId()); + + assertThatBpmnSubProcessActive(processId); + } + + @Test + @Deployment + public void testAsyncJobs() { + String processId = runtimeService.startProcessInstanceByKey("process").getProcessInstanceId(); + + List jobs = managementService.createJobQuery().processInstanceId(processId).list(); + assertThat(jobs).hasSize(1); + assertThat(jobs.get(0).getJobHandlerType()).isEqualTo(AsyncContinuationJobHandler.TYPE); + + waitForJobExecutorToProcessAllJobs(7000L, 250L); + + Execution execution = runtimeService.createExecutionQuery().processInstanceId(processId).activityId("service1").singleResult(); + assertThat(execution).isNotNull(); + int count = (int) runtimeService.getVariable(processId, "count"); + assertThat(count).isEqualTo(1); + + Map processVariables = new HashMap<>(); + processVariables.put("count", ++count); + runtimeService.triggerAsync(execution.getId(), processVariables); + + jobs = managementService.createJobQuery().processInstanceId(processId).list(); + assertThat(jobs).hasSize(1); + assertThat(jobs.get(0).getJobHandlerType()).isEqualTo(AsyncTriggerJobHandler.TYPE); + + waitForJobExecutorToProcessAllJobs(7000L, 250L); + + execution = runtimeService.createExecutionQuery().processInstanceId(processId).activityId("usertask1").singleResult(); + assertThat(execution).isNotNull(); + assertThat(runtimeService.getVariable(processId, "count")).isEqualTo(3); + } + + @Test + @Deployment + public void throwBpmnErrorInTrigger() { + Map varMap = new HashMap<>(); + varMap.put("triggerableServiceTask", new ThrowBpmnErrorTriggerableJavaDelegateServiceTask()); + + String processId = runtimeService.startProcessInstanceByKey("process", varMap).getProcessInstanceId(); + + Execution execution = runtimeService.createExecutionQuery().processInstanceId(processId).activityId("service1").singleResult(); + assertThat(execution).isNotNull(); + runtimeService.trigger(execution.getId(), Collections.emptyMap(), null); + + execution = runtimeService.createExecutionQuery().processInstanceId(processId).activityId("taskAfterErrorCatch").singleResult(); + assertThat(execution).isNotNull(); + } + + @Test + @Deployment(resources = "org/flowable/engine/test/bpmn/servicetask/TriggerableJavaDelegateServiceTaskTest.testDelegateExpression.bpmn20.xml") + public void testActivityIsNotLeaving() { + Map varMap = new HashMap<>(); + varMap.put("triggerableServiceTask", new TriggerableJavaDelegateServiceTask(true)); + + String processId = runtimeService.startProcessInstanceByKey("process", varMap).getProcessInstanceId(); + + Execution execution = runtimeService.createExecutionQuery().processInstanceId(processId).activityId("service1").singleResult(); + assertThat(execution).isNotNull(); + runtimeService.trigger(execution.getId(), Collections.emptyMap(), null); + + execution = runtimeService.createExecutionQuery().processInstanceId(processId).activityId("service1").singleResult(); + assertThat(execution).isNotNull(); + } + + @Test + @Deployment(resources = "org/flowable/engine/test/bpmn/servicetask/TriggerableJavaDelegateServiceTaskTest.testDelegateExpression.bpmn20.xml") + public void testActivityIsLeaving() { + Map varMap = new HashMap<>(); + varMap.put("triggerableServiceTask", new TriggerableJavaDelegateServiceTask()); + + String processId = runtimeService.startProcessInstanceByKey("process", varMap).getProcessInstanceId(); + + Execution execution = runtimeService.createExecutionQuery().processInstanceId(processId).activityId("service1").singleResult(); + assertThat(execution).isNotNull(); + runtimeService.trigger(execution.getId(), Collections.emptyMap(), null); + + execution = runtimeService.createExecutionQuery().processInstanceId(processId).activityId("usertask1").singleResult(); + assertThat(execution).isNotNull(); + } + + @Test + @Deployment(resources = "org/flowable/engine/test/bpmn/servicetask/TriggerableJavaDelegateServiceTaskTest.testJavaDelegateNotLeaving.bpmn20.xml") + public void testClassDelegateIsNotLeaving() { + String processId = runtimeService.startProcessInstanceByKey("process").getProcessInstanceId(); + + Execution execution = runtimeService.createExecutionQuery().processInstanceId(processId).activityId("service1").singleResult(); + assertThat(execution).isNotNull(); + int count = (int) runtimeService.getVariable(processId, "count"); + assertThat(count).isEqualTo(1); + + Map processVariables = new HashMap<>(); + processVariables.put("count", ++count); + runtimeService.trigger(execution.getId(), processVariables); + + execution = runtimeService.createExecutionQuery().processInstanceId(processId).activityId("service1").singleResult(); + assertThat(execution).isNotNull(); + count = (int) runtimeService.getVariable(processId, "count"); + assertThat(count).isEqualTo(3); + } + @Test + @Deployment(resources = "org/flowable/engine/test/bpmn/servicetask/TriggerableJavaDelegateServiceTaskTest.testLeavingJavaDelegate.bpmn20.xml") + public void testClassDelegateIsLeaving() { + String processId = runtimeService.startProcessInstanceByKey("process").getProcessInstanceId(); + + Execution execution = runtimeService.createExecutionQuery().processInstanceId(processId).activityId("service1").singleResult(); + assertThat(execution).isNotNull(); + int count = (int) runtimeService.getVariable(processId, "count"); + assertThat(count).isEqualTo(1); + + Map processVariables = new HashMap<>(); + processVariables.put("count", ++count); + runtimeService.trigger(execution.getId(), processVariables); + + execution = runtimeService.createExecutionQuery().processInstanceId(processId).activityId("usertask1").singleResult(); + assertThat(execution).isNotNull(); + count = (int) runtimeService.getVariable(processId, "count"); + assertThat(count).isEqualTo(3); + + } + + @Test + @Deployment + public void testFutureJavaDelegateNotLeaving() { + NotLeavingFutureJavaDelegateServiceTask.count = 0; + String processId = runtimeService.startProcessInstanceByKey("process").getProcessInstanceId(); + + Execution execution = runtimeService.createExecutionQuery().processInstanceId(processId).activityId("service1").singleResult(); + assertThat(execution).isNotNull(); + assertThat(NotLeavingFutureJavaDelegateServiceTask.count).isEqualTo(1); + + Map processVariables = new HashMap<>(); + processVariables.put("count", NotLeavingFutureJavaDelegateServiceTask.count++); + runtimeService.trigger(execution.getId(), processVariables); + + execution = runtimeService.createExecutionQuery().processInstanceId(processId).activityId("service1").singleResult(); + assertThat(execution).isNotNull(); + assertThat(NotLeavingFutureJavaDelegateServiceTask.count).isEqualTo(3); + } + + @Test + @Deployment + public void testFutureJavaDelegateIsLeaving() { + String processId = runtimeService.startProcessInstanceByKey("process").getProcessInstanceId(); + + Execution execution = runtimeService.createExecutionQuery().processInstanceId(processId).activityId("service1").singleResult(); + assertThat(execution).isNotNull(); + assertThat(LeavingFutureJavaDelegateServiceTask.count).isEqualTo(1); + + Map processVariables = new HashMap<>(); + runtimeService.trigger(execution.getId(), processVariables); + + execution = runtimeService.createExecutionQuery().processInstanceId(processId).activityId("usertask1").singleResult(); + assertThat(execution).isNotNull(); + assertThat(LeavingFutureJavaDelegateServiceTask.count).isEqualTo(2); + } + + protected void assertThatBpmnSubProcessActive(String processId) { + assertThat(taskService.createTaskQuery().processInstanceId(processId).taskName("Escalated Task").singleResult()).isNotNull(); + } +} diff --git a/modules/flowable-engine/src/test/java/org/flowable/engine/test/logging/ServiceTaskLoggingTest.java b/modules/flowable-engine/src/test/java/org/flowable/engine/test/logging/ServiceTaskLoggingTest.java index c0414609870..272b8c595c0 100644 --- a/modules/flowable-engine/src/test/java/org/flowable/engine/test/logging/ServiceTaskLoggingTest.java +++ b/modules/flowable-engine/src/test/java/org/flowable/engine/test/logging/ServiceTaskLoggingTest.java @@ -35,6 +35,7 @@ import org.flowable.engine.runtime.Execution; import org.flowable.engine.runtime.ProcessInstance; import org.flowable.engine.test.Deployment; +import org.flowable.engine.test.bpmn.servicetask.TriggerableJavaDelegateServiceTask; import org.flowable.engine.test.bpmn.servicetask.TriggerableServiceTask; import org.flowable.job.api.Job; import org.flowable.task.api.Task; @@ -1705,6 +1706,167 @@ void testTriggerableServiceTaskWithDelegateExpression() { } + @Test + @Deployment(resources = "org/flowable/engine/test/bpmn/servicetask/TriggerableServiceTaskTest.testDelegateExpression.bpmn20.xml") + void testTriggerableJavaDelegateServiceTaskWithDelegateExpression() { + FlowableLoggingListener.clear(); + + TriggerableJavaDelegateServiceTask delegate = new TriggerableJavaDelegateServiceTask(); + ProcessInstance processInstance = runtimeService.createProcessInstanceBuilder() + .processDefinitionKey("process") + .transientVariable("triggerableServiceTask", delegate) + .start(); + + String processId = processInstance.getId(); + String processDefinitionId = processInstance.getProcessDefinitionId(); + + List loggingNodes = FlowableLoggingListener.TEST_LOGGING_NODES; + assertThat(loggingNodes) + .extracting(node -> node.path("type").asText("__invalid")) + .containsExactly( + LoggingSessionConstants.TYPE_PROCESS_STARTED, + LoggingSessionConstants.TYPE_ACTIVITY_BEHAVIOR_EXECUTE, + LoggingSessionConstants.TYPE_SEQUENCE_FLOW_TAKE, + LoggingSessionConstants.TYPE_ACTIVITY_BEHAVIOR_EXECUTE, + LoggingSessionConstants.TYPE_SERVICE_TASK_ENTER, + LoggingSessionConstants.TYPE_VARIABLE_CREATE, + LoggingSessionConstants.TYPE_SERVICE_TASK_EXIT, + LoggingSessionConstants.TYPE_COMMAND_CONTEXT_CLOSE + ); + + assertThatJson(loggingNodes.get(0)) + .isEqualTo("{" + + " type: 'processStarted'," + + " message: 'Started process instance with id " + processId + "'," + + " scopeId: '" + processId + "'," + + " subScopeId: '${json-unit.any-string}'," + + " scopeType: 'bpmn'," + + " scopeDefinitionId: '" + processDefinitionId + "'," + + " scopeDefinitionKey: 'process'," + + " scopeDefinitionName: null," + + " __id: '${json-unit.any-string}'," + + " __timeStamp: '${json-unit.any-string}'," + + " __transactionId: '${json-unit.any-string}'," + + " __logNumber: 1" + + "}"); + + assertThatJson(loggingNodes.get(3)) + .isEqualTo("{" + + " type: 'activityBehaviorExecute'," + + " message: 'In ServiceTask, executing ServiceTaskDelegateExpressionActivityBehavior'," + + " scopeId: '" + processId + "'," + + " subScopeId: '${json-unit.any-string}'," + + " scopeType: 'bpmn'," + + " scopeDefinitionId: '" + processDefinitionId + "'," + + " scopeDefinitionKey: 'process'," + + " scopeDefinitionName: null," + + " elementId: 'service1'," + + " elementType: 'ServiceTask'," + + " elementSubType: '${triggerableServiceTask}'," + + " activityBehavior: 'ServiceTaskDelegateExpressionActivityBehavior'," + + " __id: '${json-unit.any-string}'," + + " __timeStamp: '${json-unit.any-string}'," + + " __transactionId: '${json-unit.any-string}'," + + " __logNumber: 4" + + "}"); + + assertThatJson(loggingNodes.get(4)) + .isEqualTo("{" + + " type: 'serviceTaskEnter'," + + " message: 'Executing service task with delegate " + delegate + "'," + + " scopeId: '" + processId + "'," + + " subScopeId: '${json-unit.any-string}'," + + " scopeType: 'bpmn'," + + " scopeDefinitionId: '" + processDefinitionId + "'," + + " scopeDefinitionKey: 'process'," + + " scopeDefinitionName: null," + + " elementId: 'service1'," + + " elementType: 'ServiceTask'," + + " elementSubType: '${triggerableServiceTask}'," + + " __id: '${json-unit.any-string}'," + + " __timeStamp: '${json-unit.any-string}'," + + " __transactionId: '${json-unit.any-string}'," + + " __logNumber: 5" + + "}"); + + assertThatJson(loggingNodes.get(6)) + .isEqualTo("{" + + " type: 'serviceTaskExit'," + + " message: 'Executed service task with delegate " + delegate + "'," + + " scopeId: '" + processId + "'," + + " subScopeId: '${json-unit.any-string}'," + + " scopeType: 'bpmn'," + + " scopeDefinitionId: '" + processDefinitionId + "'," + + " scopeDefinitionKey: 'process'," + + " scopeDefinitionName: null," + + " elementId: 'service1'," + + " elementType: 'ServiceTask'," + + " elementSubType: '${triggerableServiceTask}'," + + " __id: '${json-unit.any-string}'," + + " __timeStamp: '${json-unit.any-string}'," + + " __transactionId: '${json-unit.any-string}'," + + " __logNumber: 7" + + "}"); + + FlowableLoggingListener.clear(); + + Execution execution = runtimeService.createExecutionQuery().processInstanceId(processId).activityId("service1").singleResult(); + runtimeService.trigger(execution.getId(), Collections.emptyMap(), Collections.singletonMap("triggerableServiceTask", delegate)); + + loggingNodes = FlowableLoggingListener.TEST_LOGGING_NODES; + + assertThat(loggingNodes) + .extracting(node -> node.path("type").asText("__invalid")) + .containsExactly( + LoggingSessionConstants.TYPE_SERVICE_TASK_BEFORE_TRIGGER, + LoggingSessionConstants.TYPE_VARIABLE_UPDATE, + LoggingSessionConstants.TYPE_SERVICE_TASK_AFTER_TRIGGER, + LoggingSessionConstants.TYPE_SEQUENCE_FLOW_TAKE, + LoggingSessionConstants.TYPE_ACTIVITY_BEHAVIOR_EXECUTE, + LoggingSessionConstants.TYPE_USER_TASK_CREATE, + LoggingSessionConstants.TYPE_COMMAND_CONTEXT_CLOSE + ); + + assertThatJson(loggingNodes.get(0)) + .isEqualTo("{" + + " type: 'serviceTaskBeforeTrigger'," + + " message: 'Triggering service task with java delegate " + delegate + "'," + + " scopeId: '" + processId + "'," + + " subScopeId: '${json-unit.any-string}'," + + " scopeType: 'bpmn'," + + " scopeDefinitionId: '" + processDefinitionId + "'," + + " scopeDefinitionKey: 'process'," + + " scopeDefinitionName: null," + + " elementId: 'service1'," + + " elementType: 'ServiceTask'," + + " elementSubType: '${triggerableServiceTask}'," + + " __id: '${json-unit.any-string}'," + + " __timeStamp: '${json-unit.any-string}'," + + " __transactionId: '${json-unit.any-string}'," + + " __logNumber: 1" + + "}"); + + assertThatJson(loggingNodes.get(2)) + .isEqualTo("{" + + " type: 'serviceTaskAfterTrigger'," + + " message: 'Triggered service task with java delegate " + delegate + "'," + + " scopeId: '" + processId + "'," + + " subScopeId: '${json-unit.any-string}'," + + " scopeType: 'bpmn'," + + " scopeDefinitionId: '" + processDefinitionId + "'," + + " scopeDefinitionKey: 'process'," + + " scopeDefinitionName: null," + + " elementId: 'service1'," + + " elementType: 'ServiceTask'," + + " elementSubType: '${triggerableServiceTask}'," + + " __id: '${json-unit.any-string}'," + + " __timeStamp: '${json-unit.any-string}'," + + " __transactionId: '${json-unit.any-string}'," + + " __logNumber: 3" + + "}"); + + } + public static class ExceptionServiceTaskDelegate implements JavaDelegate { @Override diff --git a/modules/flowable-engine/src/test/resources/org/flowable/engine/test/bpmn/servicetask/TriggerableJavaDelegateServiceTaskTest.classDelegateTriggerExceptionWithoutWaitStateInCatch.bpmn20.xml b/modules/flowable-engine/src/test/resources/org/flowable/engine/test/bpmn/servicetask/TriggerableJavaDelegateServiceTaskTest.classDelegateTriggerExceptionWithoutWaitStateInCatch.bpmn20.xml new file mode 100644 index 00000000000..32a3116b941 --- /dev/null +++ b/modules/flowable-engine/src/test/resources/org/flowable/engine/test/bpmn/servicetask/TriggerableJavaDelegateServiceTaskTest.classDelegateTriggerExceptionWithoutWaitStateInCatch.bpmn20.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/modules/flowable-engine/src/test/resources/org/flowable/engine/test/bpmn/servicetask/TriggerableJavaDelegateServiceTaskTest.testAsyncJobs.bpmn20.xml b/modules/flowable-engine/src/test/resources/org/flowable/engine/test/bpmn/servicetask/TriggerableJavaDelegateServiceTaskTest.testAsyncJobs.bpmn20.xml new file mode 100644 index 00000000000..00e616e03d3 --- /dev/null +++ b/modules/flowable-engine/src/test/resources/org/flowable/engine/test/bpmn/servicetask/TriggerableJavaDelegateServiceTaskTest.testAsyncJobs.bpmn20.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + diff --git a/modules/flowable-engine/src/test/resources/org/flowable/engine/test/bpmn/servicetask/TriggerableJavaDelegateServiceTaskTest.testClassDelegate.bpmn20.xml b/modules/flowable-engine/src/test/resources/org/flowable/engine/test/bpmn/servicetask/TriggerableJavaDelegateServiceTaskTest.testClassDelegate.bpmn20.xml new file mode 100644 index 00000000000..e02ab171a52 --- /dev/null +++ b/modules/flowable-engine/src/test/resources/org/flowable/engine/test/bpmn/servicetask/TriggerableJavaDelegateServiceTaskTest.testClassDelegate.bpmn20.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + diff --git a/modules/flowable-engine/src/test/resources/org/flowable/engine/test/bpmn/servicetask/TriggerableJavaDelegateServiceTaskTest.testClassDelegateTriggerException.bpmn20.xml b/modules/flowable-engine/src/test/resources/org/flowable/engine/test/bpmn/servicetask/TriggerableJavaDelegateServiceTaskTest.testClassDelegateTriggerException.bpmn20.xml new file mode 100644 index 00000000000..4d068563681 --- /dev/null +++ b/modules/flowable-engine/src/test/resources/org/flowable/engine/test/bpmn/servicetask/TriggerableJavaDelegateServiceTaskTest.testClassDelegateTriggerException.bpmn20.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/modules/flowable-engine/src/test/resources/org/flowable/engine/test/bpmn/servicetask/TriggerableJavaDelegateServiceTaskTest.testDelegateExpression.bpmn20.xml b/modules/flowable-engine/src/test/resources/org/flowable/engine/test/bpmn/servicetask/TriggerableJavaDelegateServiceTaskTest.testDelegateExpression.bpmn20.xml new file mode 100644 index 00000000000..c719dd48726 --- /dev/null +++ b/modules/flowable-engine/src/test/resources/org/flowable/engine/test/bpmn/servicetask/TriggerableJavaDelegateServiceTaskTest.testDelegateExpression.bpmn20.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/modules/flowable-engine/src/test/resources/org/flowable/engine/test/bpmn/servicetask/TriggerableJavaDelegateServiceTaskTest.testFutureJavaDelegateIsLeaving.bpmn20.xml b/modules/flowable-engine/src/test/resources/org/flowable/engine/test/bpmn/servicetask/TriggerableJavaDelegateServiceTaskTest.testFutureJavaDelegateIsLeaving.bpmn20.xml new file mode 100644 index 00000000000..c63cbd0398a --- /dev/null +++ b/modules/flowable-engine/src/test/resources/org/flowable/engine/test/bpmn/servicetask/TriggerableJavaDelegateServiceTaskTest.testFutureJavaDelegateIsLeaving.bpmn20.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + diff --git a/modules/flowable-engine/src/test/resources/org/flowable/engine/test/bpmn/servicetask/TriggerableJavaDelegateServiceTaskTest.testFutureJavaDelegateNotLeaving.bpmn20.xml b/modules/flowable-engine/src/test/resources/org/flowable/engine/test/bpmn/servicetask/TriggerableJavaDelegateServiceTaskTest.testFutureJavaDelegateNotLeaving.bpmn20.xml new file mode 100644 index 00000000000..5378a5a084f --- /dev/null +++ b/modules/flowable-engine/src/test/resources/org/flowable/engine/test/bpmn/servicetask/TriggerableJavaDelegateServiceTaskTest.testFutureJavaDelegateNotLeaving.bpmn20.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + diff --git a/modules/flowable-engine/src/test/resources/org/flowable/engine/test/bpmn/servicetask/TriggerableJavaDelegateServiceTaskTest.testJavaDelegateNotLeaving.bpmn20.xml b/modules/flowable-engine/src/test/resources/org/flowable/engine/test/bpmn/servicetask/TriggerableJavaDelegateServiceTaskTest.testJavaDelegateNotLeaving.bpmn20.xml new file mode 100644 index 00000000000..124d35dd6fa --- /dev/null +++ b/modules/flowable-engine/src/test/resources/org/flowable/engine/test/bpmn/servicetask/TriggerableJavaDelegateServiceTaskTest.testJavaDelegateNotLeaving.bpmn20.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + diff --git a/modules/flowable-engine/src/test/resources/org/flowable/engine/test/bpmn/servicetask/TriggerableJavaDelegateServiceTaskTest.testLeavingJavaDelegate.bpmn20.xml b/modules/flowable-engine/src/test/resources/org/flowable/engine/test/bpmn/servicetask/TriggerableJavaDelegateServiceTaskTest.testLeavingJavaDelegate.bpmn20.xml new file mode 100644 index 00000000000..f1f2a4c4853 --- /dev/null +++ b/modules/flowable-engine/src/test/resources/org/flowable/engine/test/bpmn/servicetask/TriggerableJavaDelegateServiceTaskTest.testLeavingJavaDelegate.bpmn20.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + diff --git a/modules/flowable-engine/src/test/resources/org/flowable/engine/test/bpmn/servicetask/TriggerableJavaDelegateServiceTaskTest.throwBpmnErrorInTrigger.bpmn20.xml b/modules/flowable-engine/src/test/resources/org/flowable/engine/test/bpmn/servicetask/TriggerableJavaDelegateServiceTaskTest.throwBpmnErrorInTrigger.bpmn20.xml new file mode 100644 index 00000000000..47ad2f384d8 --- /dev/null +++ b/modules/flowable-engine/src/test/resources/org/flowable/engine/test/bpmn/servicetask/TriggerableJavaDelegateServiceTaskTest.throwBpmnErrorInTrigger.bpmn20.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/modules/flowable-engine/src/test/resources/org/flowable/engine/test/bpmn/servicetask/TriggerableServiceTaskTest.testNoneActivityBehaviorClassDelegate.bpmn20.xml b/modules/flowable-engine/src/test/resources/org/flowable/engine/test/bpmn/servicetask/TriggerableServiceTaskTest.testNoneActivityBehaviorClassDelegate.bpmn20.xml new file mode 100644 index 00000000000..1ad955df35e --- /dev/null +++ b/modules/flowable-engine/src/test/resources/org/flowable/engine/test/bpmn/servicetask/TriggerableServiceTaskTest.testNoneActivityBehaviorClassDelegate.bpmn20.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + diff --git a/modules/flowable-event-registry/src/main/java/org/flowable/eventregistry/impl/db/EventDbSchemaManager.java b/modules/flowable-event-registry/src/main/java/org/flowable/eventregistry/impl/db/EventDbSchemaManager.java index 8ab96d07722..b4558f6977f 100644 --- a/modules/flowable-event-registry/src/main/java/org/flowable/eventregistry/impl/db/EventDbSchemaManager.java +++ b/modules/flowable-event-registry/src/main/java/org/flowable/eventregistry/impl/db/EventDbSchemaManager.java @@ -16,6 +16,7 @@ import org.apache.commons.lang3.StringUtils; import org.flowable.common.engine.impl.AbstractEngineConfiguration; +import org.flowable.common.engine.impl.db.EngineSchemaManagerLockConfiguration; import org.flowable.common.engine.impl.db.EngineSqlScriptBasedDbSchemaManager; import org.flowable.eventregistry.impl.EventRegistryEngine; import org.flowable.eventregistry.impl.cmd.UpdateChannelDefinitionTypeAndImplementationForAllChannelDefinitionsCmd; @@ -34,7 +35,7 @@ public class EventDbSchemaManager extends EngineSqlScriptBasedDbSchemaManager { ); public EventDbSchemaManager() { - super("eventregistry"); + super("eventregistry", new EngineSchemaManagerLockConfiguration(CommandContextUtil::getEventRegistryConfiguration)); } @Override @@ -61,11 +62,6 @@ protected String getEngineTableName() { protected String getChangeLogTableName() { return "FLW_EV_DATABASECHANGELOG"; } - - @Override - protected String getChangeLogTablePrefixName() { - return "FLW_EV"; - } @Override protected String getDbVersionForChangelogVersion(String changeLogVersion) { @@ -75,7 +71,6 @@ protected String getDbVersionForChangelogVersion(String changeLogVersion) { return "6.5.0.0"; } - @Override protected AbstractEngineConfiguration getEngineConfiguration() { return CommandContextUtil.getEventRegistryConfiguration(); } diff --git a/modules/flowable-event-registry/src/main/resources/org/flowable/eventregistry/db/mapping/mappings.xml b/modules/flowable-event-registry/src/main/resources/org/flowable/eventregistry/db/mapping/mappings.xml index 93f594dda49..afd26fcafe7 100644 --- a/modules/flowable-event-registry/src/main/resources/org/flowable/eventregistry/db/mapping/mappings.xml +++ b/modules/flowable-event-registry/src/main/resources/org/flowable/eventregistry/db/mapping/mappings.xml @@ -18,7 +18,6 @@ - diff --git a/modules/flowable-idm-engine/src/main/java/org/flowable/idm/engine/impl/db/IdmDbSchemaManager.java b/modules/flowable-idm-engine/src/main/java/org/flowable/idm/engine/impl/db/IdmDbSchemaManager.java index 1f2e86818ae..ce4cdb70cbb 100644 --- a/modules/flowable-idm-engine/src/main/java/org/flowable/idm/engine/impl/db/IdmDbSchemaManager.java +++ b/modules/flowable-idm-engine/src/main/java/org/flowable/idm/engine/impl/db/IdmDbSchemaManager.java @@ -108,27 +108,6 @@ protected String addMissingComponent(String missingComponents, String component) return missingComponents + ", " + component; } - protected boolean isMissingTablesException(Exception e) { - String exceptionMessage = e.getMessage(); - if (e.getMessage() != null) { - // Matches message returned from H2 - if ((exceptionMessage.contains("Table")) && (exceptionMessage.contains("not found"))) { - return true; - } - - // Message returned from MySQL and Oracle - if ((exceptionMessage.contains("Table") || exceptionMessage.contains("table")) && (exceptionMessage.contains("doesn't exist"))) { - return true; - } - - // Message returned from Postgres - if ((exceptionMessage.contains("relation") || exceptionMessage.contains("table")) && (exceptionMessage.contains("does not exist"))) { - return true; - } - } - return false; - } - @Override public String getContext() { return SCHEMA_COMPONENT;