From a9a70c0f62a3a4b1edce8fbe3d5ba4f06c3a9103 Mon Sep 17 00:00:00 2001 From: Francisco Javier Tirado Sarti Date: Thu, 5 Aug 2021 19:00:56 +0200 Subject: [PATCH] [JBPM-9738] Adding support for headers in WebServiceWorkItemHandler --- .../core/util/WorkItemHeaderInfo.java | 79 ++++++++++++++++++ .../core/util/WorkItemHeaderUtils.java | 63 +++++++++++++++ .../core/util/WorkItemHeaderUtilsTest.java | 68 ++++++++++++++++ .../webservice/WebServiceWorkItemHandler.java | 80 ++++++++++++------- 4 files changed, 263 insertions(+), 27 deletions(-) create mode 100644 jbpm-workitems/jbpm-workitems-core/src/main/java/org/jbpm/process/workitem/core/util/WorkItemHeaderInfo.java create mode 100644 jbpm-workitems/jbpm-workitems-core/src/main/java/org/jbpm/process/workitem/core/util/WorkItemHeaderUtils.java create mode 100644 jbpm-workitems/jbpm-workitems-core/src/test/java/org/jbpm/process/workitem/core/util/WorkItemHeaderUtilsTest.java diff --git a/jbpm-workitems/jbpm-workitems-core/src/main/java/org/jbpm/process/workitem/core/util/WorkItemHeaderInfo.java b/jbpm-workitems/jbpm-workitems-core/src/main/java/org/jbpm/process/workitem/core/util/WorkItemHeaderInfo.java new file mode 100644 index 0000000000..cc09c314e2 --- /dev/null +++ b/jbpm-workitems/jbpm-workitems-core/src/main/java/org/jbpm/process/workitem/core/util/WorkItemHeaderInfo.java @@ -0,0 +1,79 @@ +/* + * 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.process.workitem.core.util; + +import java.util.HashMap; +import java.util.Map; + +public class WorkItemHeaderInfo { + + private final String name; + private final Object content; + private final Map params; + + public static class Builder { + + private final String name; + private Object content; + private Map parameters; + + private Builder(String name) { + this.name = name; + this.parameters = new HashMap<>(); + } + + public static Builder of(String name) { + return new Builder(name); + } + + public Builder withContent(Object content) { + this.content = content; + return this; + } + + public Builder withParam(String key, Object value) { + parameters.put(key, value); + return this; + } + + public WorkItemHeaderInfo build() { + return new WorkItemHeaderInfo(name, content, parameters); + } + } + + private WorkItemHeaderInfo(String name, Object content, Map params) { + this.name = name; + this.content = content; + this.params = params; + } + + public String getName() { + return name; + } + + public Object getContent() { + return content; + } + + public Object getParam(String key) { + return params.get(key); + } + + @Override + public String toString() { + return "WorkItemHeaderInfo [name=" + name + ", content=" + content + ", params=" + params + "]"; + } +} diff --git a/jbpm-workitems/jbpm-workitems-core/src/main/java/org/jbpm/process/workitem/core/util/WorkItemHeaderUtils.java b/jbpm-workitems/jbpm-workitems-core/src/main/java/org/jbpm/process/workitem/core/util/WorkItemHeaderUtils.java new file mode 100644 index 0000000000..81d55c4035 --- /dev/null +++ b/jbpm-workitems/jbpm-workitems-core/src/main/java/org/jbpm/process/workitem/core/util/WorkItemHeaderUtils.java @@ -0,0 +1,63 @@ +/* + * 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.process.workitem.core.util; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; +import java.util.stream.Collectors; + +import org.jbpm.process.workitem.core.util.WorkItemHeaderInfo.Builder; +import org.kie.api.runtime.process.WorkItem; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class WorkItemHeaderUtils { + + private WorkItemHeaderUtils() { + } + + private static final String CONTENT_PREFIX = "HEADER_"; + private static final String PARAM_PREFIX = "HEADER_PARAM_"; + static final String SEPARATOR_PROP = "org.kie.workitem.ws.header.separator"; + + private static final Logger logger = LoggerFactory.getLogger(WorkItemHeaderUtils.class); + + public static Collection getHeaderInfo(WorkItem workItem) { + final String separator = System.getProperty(SEPARATOR_PROP, "_"); + Map map = new HashMap<>(); + for (Entry param : workItem.getParameters().entrySet()) { + String key = param.getKey().toUpperCase(); + if (key.startsWith(PARAM_PREFIX)) { + key = param.getKey().substring(PARAM_PREFIX.length()); + int indexOf = key.indexOf(separator); + if (indexOf != -1) { + map.computeIfAbsent(key.substring(indexOf + separator.length()), Builder::of) + .withParam(key.substring(0, indexOf), param.getValue()); + } else { + logger.warn("Wrong parameter name {}. It expects at least one {} in {}", param.getKey(), separator, + key); + } + } else if (key.startsWith(CONTENT_PREFIX)) { + map.computeIfAbsent(param.getKey().substring(CONTENT_PREFIX.length()), Builder::of) + .withContent(param.getValue()); + } + } + return map.values().stream().map(Builder::build).collect(Collectors.toList()); + } + +} diff --git a/jbpm-workitems/jbpm-workitems-core/src/test/java/org/jbpm/process/workitem/core/util/WorkItemHeaderUtilsTest.java b/jbpm-workitems/jbpm-workitems-core/src/test/java/org/jbpm/process/workitem/core/util/WorkItemHeaderUtilsTest.java new file mode 100644 index 0000000000..232a43818d --- /dev/null +++ b/jbpm-workitems/jbpm-workitems-core/src/test/java/org/jbpm/process/workitem/core/util/WorkItemHeaderUtilsTest.java @@ -0,0 +1,68 @@ +/* + * 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.process.workitem.core.util; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +import org.junit.Test; +import org.kie.api.runtime.process.WorkItem; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class WorkItemHeaderUtilsTest { + + @Test + public void testBuildHeaderList() { + WorkItem workItem = mock(WorkItem.class); + Map map = new HashMap<>(); + map.put("HEADER_Pepito_Grillo", "fulanito"); + map.put("header_param_NS_Pepito_Grillo", "http://pepito.com"); + map.put("mamotreco", "power"); + when(workItem.getParameters()).thenReturn(map); + Collection headers = WorkItemHeaderUtils.getHeaderInfo(workItem); + assertEquals(1, headers.size()); + WorkItemHeaderInfo header = headers.iterator().next(); + assertEquals("Pepito_Grillo", header.getName()); + assertEquals("fulanito", header.getContent()); + assertEquals("http://pepito.com", header.getParam("NS")); + } + + @Test + public void testBuildHeaderListWithCustomSeparator() { + System.setProperty(WorkItemHeaderUtils.SEPARATOR_PROP, "//"); + try { + WorkItem workItem = mock(WorkItem.class); + Map map = new HashMap<>(); + map.put("HEADER_Pepito_Grillo", "fulanito"); + map.put("header_param_NS_232//Pepito_Grillo", "http://pepito.com"); + map.put("mamotreco", "power"); + when(workItem.getParameters()).thenReturn(map); + Collection headers = WorkItemHeaderUtils.getHeaderInfo(workItem); + assertEquals(1, headers.size()); + WorkItemHeaderInfo header = headers.iterator().next(); + assertEquals("Pepito_Grillo", header.getName()); + assertEquals("fulanito", header.getContent()); + assertEquals("http://pepito.com", header.getParam("NS_232")); + } finally { + System.clearProperty(WorkItemHeaderUtils.SEPARATOR_PROP); + } + } + +} diff --git a/jbpm-workitems/jbpm-workitems-webservice/src/main/java/org/jbpm/process/workitem/webservice/WebServiceWorkItemHandler.java b/jbpm-workitems/jbpm-workitems-webservice/src/main/java/org/jbpm/process/workitem/webservice/WebServiceWorkItemHandler.java index 29aefb0f55..3327d215f6 100644 --- a/jbpm-workitems/jbpm-workitems-webservice/src/main/java/org/jbpm/process/workitem/webservice/WebServiceWorkItemHandler.java +++ b/jbpm-workitems/jbpm-workitems-webservice/src/main/java/org/jbpm/process/workitem/webservice/WebServiceWorkItemHandler.java @@ -30,6 +30,7 @@ import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; import javax.xml.namespace.QName; @@ -38,6 +39,8 @@ import org.apache.cxf.endpoint.ClientCallback; import org.apache.cxf.endpoint.Endpoint; import org.apache.cxf.endpoint.dynamic.DynamicClientFactory; +import org.apache.cxf.headers.Header; +import org.apache.cxf.jaxb.JAXBDataBinding; import org.apache.cxf.jaxws.endpoint.dynamic.JaxWsDynamicClientFactory; import org.apache.cxf.jaxws.interceptors.HolderInInterceptor; import org.apache.cxf.jaxws.interceptors.WrapperClassInInterceptor; @@ -52,6 +55,8 @@ import org.jbpm.process.workitem.core.util.WidMavenDepends; import org.jbpm.process.workitem.core.util.WidParameter; import org.jbpm.process.workitem.core.util.WidResult; +import org.jbpm.process.workitem.core.util.WorkItemHeaderInfo; +import org.jbpm.process.workitem.core.util.WorkItemHeaderUtils; import org.jbpm.process.workitem.core.util.service.WidAction; import org.jbpm.process.workitem.core.util.service.WidAuth; import org.jbpm.process.workitem.core.util.service.WidService; @@ -106,7 +111,7 @@ public class WebServiceWorkItemHandler extends AbstractLogOrThrowWorkItemHandler private final Long defaultJbpmCxfClientConnectionTimeout = Long.parseLong(System.getProperty("org.jbpm.cxf.client.connectionTimeout", "30000")); private final Long defaultJbpmCxfClientReceiveTimeout = Long.parseLong(System.getProperty("org.jbpm.cxf.client.receiveTimeout", "60000")); - private ConcurrentHashMap clients = new ConcurrentHashMap(); + private ConcurrentHashMap clients = new ConcurrentHashMap<>(); private DynamicClientFactory dcf = null; private KieSession ksession; private int asyncTimeout = 10; @@ -317,6 +322,7 @@ public WebServiceWorkItemHandler(String handlingProcessId, this.handlingStrategy = handlingStrategy; } + @Override public void executeWorkItem(WorkItem workItem, final WorkItemManager manager) { @@ -397,6 +403,7 @@ public void executeWorkItem(WorkItem workItem, } new Thread(new Runnable() { + @Override public void run() { try { @@ -488,15 +495,7 @@ protected Client getWSClient(WorkItem workItem, String interfaceRef) { String importNamespace = (String) workItem.getParameter("Namespace"); if (importLocation != null && importLocation.trim().length() > 0 && importNamespace != null && importNamespace.trim().length() > 0) { - Client client = getDynamicClientFactory().createClient(importLocation, - new QName(importNamespace, - interfaceRef), - getInternalClassLoader(), - null); - setClientTimeout(workItem, client); - clients.put(interfaceRef, - client); - return client; + return getClient (workItem, importLocation, importNamespace, interfaceRef); } long processInstanceId = ((WorkItemImpl) workItem).getProcessInstanceId(); @@ -508,26 +507,52 @@ protected Client getWSClient(WorkItem workItem, String interfaceRef) { for (Bpmn2Import importObj : typedImports) { if (WSDL_IMPORT_TYPE.equalsIgnoreCase(importObj.getType())) { try { - client = getDynamicClientFactory().createClient(importObj.getLocation(), - new QName(importObj.getNamespace(), - interfaceRef), - getInternalClassLoader(), - null); - setClientTimeout(workItem, client); - clients.put(interfaceRef, - client); - return client; - } catch (Exception e) { - logger.error("Error when creating WS Client", - e); - continue; - } - } - } - } + return getClient (workItem, importObj.getLocation(), importObj.getNamespace(), interfaceRef); + } catch (Exception e) { + logger.error("Error when creating WS Client", e); + } + } + } + } } return null; } + + + private Client getClient(WorkItem workItem, String location, String namespace, String interfaceRef) { + Client client = getDynamicClientFactory().createClient(location, new QName(namespace, interfaceRef), + getInternalClassLoader(), null); + setClientTimeout(workItem, client); + Collection headers = WorkItemHeaderUtils.getHeaderInfo(workItem); + if (!headers.isEmpty()) { + client.getRequestContext().put(Header.HEADER_LIST, + headers.stream().map(this::buildHeader).collect(Collectors.toList())); + } + clients.put(interfaceRef, client); + return client; + } + + + private Header buildHeader(WorkItemHeaderInfo header) { + String namespace = (String) header.getParam("NS"); + QName name = namespace == null ? new QName(header.getName()) : new QName(namespace, header.getName()); + Class contentClass = String.class; + String type = (String) header.getParam("TYPE"); + if (type != null) { + try { + contentClass = classLoader.loadClass(type); + } catch (ClassNotFoundException ex) { + logger.warn("Cannot find type {}", type, ex); + } + } + JAXBDataBinding binding = null; + try { + binding = new JAXBDataBinding(contentClass); + } catch (Exception ex) { + logger.warn("Error creating binding for type {}", type, ex); + } + return new Header(name, header.getContent(), binding); + } private void setClientTimeout(WorkItem workItem, Client client) { HTTPConduit conduit = (HTTPConduit) client.getConduit(); @@ -556,6 +581,7 @@ protected synchronized DynamicClientFactory getDynamicClientFactory() { return this.dcf; } + @Override public void abortWorkItem(WorkItem workItem, WorkItemManager manager) { // Do nothing, cannot be aborted