Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RHPAM-4889: using a feature flag to enable/disable strict mode. #1606

Merged
merged 2 commits into from
Feb 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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());
}
}
Loading