Skip to content

Commit

Permalink
RHPAM-4889: using a feature flag to enable/disable strict mode. (kieg…
Browse files Browse the repository at this point in the history
…roup#1606)

* RHPAM-4889: using a feature flag (jbpm.wb.querymode values DEFAULT or STRICT) to enable/disable strict mode. by default it'll continue behave as before this change.

* RHPAM-4889: reducing duplication.
  • Loading branch information
porcelli committed Feb 21, 2024
1 parent fe99ec9 commit 65fd1dc
Show file tree
Hide file tree
Showing 4 changed files with 198 additions and 32 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import javax.annotation.PostConstruct;
import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.event.Event;
Expand All @@ -41,54 +44,77 @@ public class KieServerQueryDefinitionLoader {

private static final Logger LOGGER = LoggerFactory.getLogger(KieServerQueryDefinitionLoader.class);

static final String JBPM_WB_QUERY_MODE = "jbpm.wb.querymode";
enum QueryMode {
DEFAULT,
STRICT;

static QueryMode convert(final String mode) {
try {
return QueryMode.valueOf(mode.toUpperCase());
} catch (IllegalArgumentException ignored) {
}
return QueryMode.DEFAULT;
}
}

@Inject
Event<QueryDefinitionLoaded> event;

@PostConstruct
public void init() {
loadDefaultQueryDefinitions();
init(System.getProperties());
}

protected void loadDefaultQueryDefinitions() {
// load any default query definitions
try (InputStream qdStream = this.getClass().getResourceAsStream("/default-query-definitions.json")) {
if (qdStream == null) {
LOGGER.info("Default query definitions file default-query-definitions.json not found");
return;
void init(final Properties properties) {
loadDefaultQueryDefinitions(QueryMode.convert(properties.getProperty(JBPM_WB_QUERY_MODE, QueryMode.DEFAULT.toString())));
}

protected void loadDefaultQueryDefinitions(final QueryMode queryMode) {
final Map<String, String> applyStrict = new HashMap<>();

if (QueryMode.STRICT.equals(queryMode)) {
QueryDefinition[] queries = loadQueryDefinitions("/default-query-definitions-strict.json");
for (QueryDefinition q : queries) {
applyStrict.put(q.getName(), q.getTarget());
}
loadQueryDefinitions(qdStream,
MarshallerFactory.getMarshaller(MarshallingFormat.JSON,
this.getClass().getClassLoader()));
} catch (Exception e) {
LOGGER.error("Error when loading default query definitions from default-query-definitions.json",
e);
}

final QueryDefinition[] queries = loadQueryDefinitions("/default-query-definitions.json");
for (QueryDefinition q : queries) {
if (applyStrict.containsKey(q.getName())){
q.setTarget(applyStrict.get(q.getName()));
}
LOGGER.info("Loaded query definition: {}", q);
event.fire(new QueryDefinitionLoaded(q));
}
}

protected void loadQueryDefinitions(final InputStream qdStream,
final Marshaller marshaller) throws IOException {
final String qdString = IOUtils.toString(qdStream,
Charset.forName("UTF-8"));
protected QueryDefinition[] loadQueryDefinitions(String resourceName) {

try {
QueryDefinition[] queries = marshaller.unmarshall(qdString,
QueryDefinition[].class);
try (InputStream qdStream = this.getClass().getResourceAsStream(resourceName)) {
if (qdStream == null) {
LOGGER.info("Default query definitions file " + resourceName + " not found");
return new QueryDefinition[0];
}

LOGGER.info("Found {} query definitions",
queries == null ? 0 : queries.length);
final String qdString = IOUtils.toString(qdStream, Charset.forName("UTF-8"));

if (queries == null) {
return;
}
for (QueryDefinition q :
queries) {
LOGGER.info("Loaded query definition: {}",
q);
event.fire(new QueryDefinitionLoaded(q));
QueryDefinition[] queries = MarshallerFactory.getMarshaller(MarshallingFormat.JSON, this.getClass().getClassLoader()).unmarshall(qdString, QueryDefinition[].class);

LOGGER.info("Found {} query definitions", queries == null ? 0 : queries.length);

if (queries == null){
return new QueryDefinition[0];
}

return queries;
} catch (MarshallingException e) {
LOGGER.error("Error when unmarshalling query definitions from stream.",
e);
LOGGER.error("Error when unmarshalling query definitions from stream.", e);
} catch (Exception e) {
LOGGER.error("Error when loading default query definitions from " + resourceName, e);
}

return new QueryDefinition[0];
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[
{
"query-name": "jbpmProcessInstances",
"query-target": "FILTERED_PROCESS"
},
{
"query-name": "processesMonitoring",
"query-target": "FILTERED_PROCESS"
},
{
"query-name": "tasksMonitoring",
"query-target": "FILTERED_PROCESS"
}
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
/*
* Copyright 2021 Red Hat, Inc. and/or its affiliates.
*
* 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.jbpm.workbench.ks.integration;

import org.jbpm.workbench.ks.integration.event.QueryDefinitionLoaded;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.kie.server.api.model.definition.QueryDefinition;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;

import javax.enterprise.event.Event;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

import static org.junit.Assert.*;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.MockitoAnnotations.openMocks;

/**
* This test class has been created to make contributors aware about the importance of the queries defined in the
* "default-query-definitions.json" file. These queries has been tested and optimized due to some important performance
* issues found on a very important and critical customer.
* Please, consider running a performance test when one of them is changed.
*
* @see <a href="https://issues.redhat.com/browse/JBPM-9099">JBPM-9099</a>
*/
@RunWith(MockitoJUnitRunner.class)
public class KieServerQueryDefinitionLoaderStrictModeTest {

private final Map<String, QueryDefinition> receivedEvents = new HashMap<>();

private static final int TOTAL_QUERY_DEFINITIONS_EXPECTED = 12;

@Mock
private Event<QueryDefinitionLoaded> event;

@InjectMocks
private KieServerQueryDefinitionLoader kieServerQueryDefinitionLoader;

@Before
public void setup() {
openMocks(this);
doAnswer(invocation -> {
QueryDefinitionLoaded queryDefinitionLoaded = invocation.getArgument(0);
receivedEvents.put(queryDefinitionLoaded.getDefinition().getName(), queryDefinitionLoaded.getDefinition());
return null;
}).when(event).fire(any(QueryDefinitionLoaded.class));
this.kieServerQueryDefinitionLoader.init(new Properties(){{
put(KieServerQueryDefinitionLoader.JBPM_WB_QUERY_MODE, "STRICT");
}});
}

@Test
public void testJbpmProcessInstances() {
QueryDefinition expectedQuery = QueryDefinition.builder()
.name("jbpmProcessInstances")
.expression("SELECT LOG.PROCESSINSTANCEID, LOG.PROCESSID, LOG.START_DATE, LOG.END_DATE, LOG.STATUS, LOG.PARENTPROCESSINSTANCEID, LOG.OUTCOME, LOG.DURATION, LOG.USER_IDENTITY, LOG.PROCESSVERSION, LOG.PROCESSNAME, LOG.CORRELATIONKEY, LOG.EXTERNALID, LOG.PROCESSINSTANCEDESCRIPTION, LOG.SLA_DUE_DATE, LOG.SLACOMPLIANCE, COALESCE ( INFO.LASTMODIFICATIONDATE, LOG.END_DATE ) AS LASTMODIFICATIONDATE, COUNT( ERRINFO.ID ) ERRORCOUNT FROM ProcessInstanceLog LOG LEFT JOIN ExecutionErrorInfo ERRINFO ON ERRINFO.PROCESS_INST_ID=LOG.PROCESSINSTANCEID AND ERRINFO.ERROR_ACK=0 LEFT JOIN ProcessInstanceInfo INFO ON INFO.INSTANCEID=LOG.PROCESSINSTANCEID GROUP BY LOG.PROCESSINSTANCEID, LOG.PROCESSID, LOG.START_DATE, LOG.END_DATE, LOG.STATUS, LOG.PARENTPROCESSINSTANCEID, LOG.OUTCOME, LOG.DURATION, LOG.USER_IDENTITY, LOG.PROCESSVERSION, LOG.PROCESSNAME, LOG.CORRELATIONKEY, LOG.EXTERNALID, LOG.PROCESSINSTANCEDESCRIPTION, LOG.SLA_DUE_DATE, LOG.SLACOMPLIANCE, COALESCE ( INFO.LASTMODIFICATIONDATE, LOG.END_DATE )")
.target("FILTERED_PROCESS")
.build();
testQueryDefinitionLoaded(expectedQuery);
}

@Test
public void testProcessesMonitoring() {
QueryDefinition expectedQuery = QueryDefinition.builder()
.name("processesMonitoring")
.expression("select log.processInstanceId, log.processId, log.start_date, log.end_date, log.status, " +
"log.duration, log.user_identity, log.processVersion, log.processName, " +
"log.externalId from ProcessInstanceLog log")
.target("FILTERED_PROCESS")
.build();
testQueryDefinitionLoaded(expectedQuery);
}

@Test
public void testTasksMonitoring() {
QueryDefinition expectedQuery = QueryDefinition.builder()
.name("tasksMonitoring")
.expression("select p.processName, p.externalId, t.taskId, t.taskName, t.status, t.createdDate, " +
"t.startDate, t.endDate, t.processInstanceId, t.userId, t.duration " +
"from ProcessInstanceLog p inner join BAMTaskSummary t on " +
"(t.processInstanceId = p.processInstanceId) inner join (select min(pk) as pk " +
"from BAMTaskSummary group by taskId) d on t.pk = d.pk")
.target("FILTERED_PROCESS")
.build();
testQueryDefinitionLoaded(expectedQuery);
}

private void testQueryDefinitionLoaded(QueryDefinition expectedQueryDefinition) {
assertTrue("No named-queries were found", receivedEvents.size() > 0);
assertNotNull("No query definition found for " + expectedQueryDefinition.getName(), receivedEvents.get(expectedQueryDefinition.getName()));
assertEquals(expectedQueryDefinition.getExpression(), receivedEvents.get(expectedQueryDefinition.getName()).getExpression());
assertEquals(expectedQueryDefinition.getTarget(), receivedEvents.get(expectedQueryDefinition.getName()).getTarget());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ public void testJbpmProcessInstances() {
QueryDefinition expectedQuery = QueryDefinition.builder()
.name("jbpmProcessInstances")
.expression("SELECT LOG.PROCESSINSTANCEID, LOG.PROCESSID, LOG.START_DATE, LOG.END_DATE, LOG.STATUS, LOG.PARENTPROCESSINSTANCEID, LOG.OUTCOME, LOG.DURATION, LOG.USER_IDENTITY, LOG.PROCESSVERSION, LOG.PROCESSNAME, LOG.CORRELATIONKEY, LOG.EXTERNALID, LOG.PROCESSINSTANCEDESCRIPTION, LOG.SLA_DUE_DATE, LOG.SLACOMPLIANCE, COALESCE ( INFO.LASTMODIFICATIONDATE, LOG.END_DATE ) AS LASTMODIFICATIONDATE, COUNT( ERRINFO.ID ) ERRORCOUNT FROM ProcessInstanceLog LOG LEFT JOIN ExecutionErrorInfo ERRINFO ON ERRINFO.PROCESS_INST_ID=LOG.PROCESSINSTANCEID AND ERRINFO.ERROR_ACK=0 LEFT JOIN ProcessInstanceInfo INFO ON INFO.INSTANCEID=LOG.PROCESSINSTANCEID GROUP BY LOG.PROCESSINSTANCEID, LOG.PROCESSID, LOG.START_DATE, LOG.END_DATE, LOG.STATUS, LOG.PARENTPROCESSINSTANCEID, LOG.OUTCOME, LOG.DURATION, LOG.USER_IDENTITY, LOG.PROCESSVERSION, LOG.PROCESSNAME, LOG.CORRELATIONKEY, LOG.EXTERNALID, LOG.PROCESSINSTANCEDESCRIPTION, LOG.SLA_DUE_DATE, LOG.SLACOMPLIANCE, COALESCE ( INFO.LASTMODIFICATIONDATE, LOG.END_DATE )")
.target("CUSTOM")
.build();
testQueryDefinitionLoaded(expectedQuery);
}
Expand All @@ -87,6 +88,7 @@ public void testJbpmProcessInstancesWithVariables() {
"vil.variableId, vil.value from VariableInstanceLog vil " +
"left join VariableInstanceLog vil2 on vil.processInstanceId = vil2.processInstanceId " +
"and vil.variableId = vil2.variableId and vil.id < vil2.id where vil2.id is null")
.target("CUSTOM")
.build();
testQueryDefinitionLoaded(expectedQuery);
}
Expand All @@ -98,6 +100,7 @@ public void testProcessesMonitoring() {
.expression("select log.processInstanceId, log.processId, log.start_date, log.end_date, log.status, " +
"log.duration, log.user_identity, log.processVersion, log.processName, " +
"log.externalId from ProcessInstanceLog log")
.target("CUSTOM")
.build();
testQueryDefinitionLoaded(expectedQuery);
}
Expand All @@ -111,6 +114,7 @@ public void testTasksMonitoring() {
"from ProcessInstanceLog p inner join BAMTaskSummary t on " +
"(t.processInstanceId = p.processInstanceId) inner join (select min(pk) as pk " +
"from BAMTaskSummary group by taskId) d on t.pk = d.pk")
.target("CUSTOM")
.build();
testQueryDefinitionLoaded(expectedQuery);
}
Expand All @@ -123,6 +127,7 @@ public void testJbpmRequestList() {
"ri.retries, ri.executions, pil.processName, pil.processInstanceId, " +
"pil.processInstanceDescription, ri.deploymentId from RequestInfo ri left join " +
"ProcessInstanceLog pil on pil.processInstanceId=ri.processInstanceId")
.target("CUSTOM")
.build();
testQueryDefinitionLoaded(expectedQuery);
}
Expand All @@ -135,6 +140,7 @@ public void testJbpmExecutionErrorList() {
"eri.ACTIVITY_NAME, eri.DEPLOYMENT_ID, eri.ERROR_DATE, eri.ERROR_ID, " +
"eri.ERROR_MSG, eri.JOB_ID, eri.PROCESS_ID, eri.PROCESS_INST_ID, eri.ERROR_TYPE " +
"from ExecutionErrorInfo eri")
.target("CUSTOM")
.build();
testQueryDefinitionLoaded(expectedQuery);
}
Expand All @@ -150,6 +156,7 @@ public void testJbpmHumanTasks() {
"nil.sla_due_date, nil.slaCompliance from AuditTaskImpl t left join " +
"ProcessInstanceLog pil on pil.processInstanceId=t.processInstanceId left join " +
"NodeInstanceLog nil on nil.workItemId=t.workItemId")
.target("CUSTOM")
.build();
testQueryDefinitionLoaded(expectedQuery);
}
Expand All @@ -167,6 +174,7 @@ public void testJbpmHumanTasksWithUser() {
"OrganizationalEntity oe on po.entity_id=oe.id left join ProcessInstanceLog pil on " +
"pil.processInstanceId=t.processInstanceId left join PeopleAssignments_ExclOwners eo " +
"on t.taskId=eo.task_id left join NodeInstanceLog nil on nil.workItemId=t.workItemId")
.target("FILTERED_PO_TASK")
.build();
testQueryDefinitionLoaded(expectedQuery);
}
Expand All @@ -185,6 +193,7 @@ public void testJbpmHumanTasksWithAdmin() {
"left join ProcessInstanceLog pil on pil.processInstanceId = t.processInstanceId " +
"left join PeopleAssignments_BAs ba on t.taskId = ba.task_id left join OrganizationalEntity oe " +
"on ba.entity_id = oe.id left join NodeInstanceLog nil on nil.workItemId=t.workItemId")
.target("FILTERED_BA_TASK")
.build();
testQueryDefinitionLoaded(expectedQuery);
}
Expand All @@ -206,6 +215,7 @@ public void testJbpmHumanTasksWithAdminExtended() {
"left join PeopleAssignments_BAs ba on t.taskId = ba.task_id left join OrganizationalEntity oe " +
"on ba.entity_id = oe.id left join NodeInstanceLog nil on nil.workItemId=t.workItemId " +
"left join Task task on task.id = t.taskId left join I18NText i18n ON i18n.Task_Subjects_Id = t.taskId")
.target("FILTERED_BA_TASK")
.build();
testQueryDefinitionLoaded(expectedQuery);
}
Expand All @@ -215,6 +225,7 @@ public void testJbpmHumanTasksWithVariables() {
QueryDefinition expectedQuery = QueryDefinition.builder()
.name("jbpmHumanTasksWithVariables")
.expression("select tvi.taskId, tvi.name, tvi.value from TaskVariableImpl tvi")
.target("CUSTOM")
.build();
testQueryDefinitionLoaded(expectedQuery);
}
Expand All @@ -227,6 +238,7 @@ public void testJbpmProcessInstanceLogs() {
"log.processInstanceId, log.log_date, log.connection, log.type, log.workItemId, " +
"log.referenceId, log.nodeContainerId, log.sla_due_date, log.slaCompliance " +
"from NodeInstanceLog log ")
.target("CUSTOM")
.build();
testQueryDefinitionLoaded(expectedQuery);
}
Expand Down Expand Up @@ -264,5 +276,6 @@ private void testQueryDefinitionLoaded(QueryDefinition expectedQueryDefinition)
assertTrue("No named-queries were found", receivedEvents.size() > 0);
assertNotNull("No query definition found for " + expectedQueryDefinition.getName(), receivedEvents.get(expectedQueryDefinition.getName()));
assertEquals(expectedQueryDefinition.getExpression(), receivedEvents.get(expectedQueryDefinition.getName()).getExpression());
assertEquals(expectedQueryDefinition.getTarget(), receivedEvents.get(expectedQueryDefinition.getName()).getTarget());
}
}

0 comments on commit 65fd1dc

Please sign in to comment.