Java tutorial
/******************************************************************************* * Copyright (c)2014 Prometheus Consulting * * 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 nz.co.senanque.messaging; import java.lang.reflect.InvocationTargetException; import java.util.Iterator; import java.util.List; import java.util.concurrent.locks.Lock; import javax.annotation.PostConstruct; import nz.co.senanque.locking.LockAction; import nz.co.senanque.locking.LockFactory; import nz.co.senanque.locking.LockTemplate; import nz.co.senanque.validationengine.ValidationEngine; import nz.co.senanque.validationengine.ValidationSessionHolder; import nz.co.senanque.validationengine.ValidationSessionHolderImpl; import nz.co.senanque.workflow.BundleSelector; import nz.co.senanque.workflow.ContextUtils; import nz.co.senanque.workflow.WorkflowDAO; import nz.co.senanque.workflow.WorkflowException; import nz.co.senanque.workflow.WorkflowManager; import nz.co.senanque.workflow.instances.ProcessInstance; import nz.co.senanque.workflow.instances.TaskStatus; import org.apache.commons.beanutils.ConvertUtils; import org.apache.commons.beanutils.PropertyUtils; import org.jdom.Document; import org.jdom.Element; import org.jdom.Text; import org.jdom.filter.ContentFilter; import org.jdom.input.DOMBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.integration.IntegrationMessageHeaderAccessor; import org.springframework.messaging.Message; import org.springframework.messaging.MessageHeaders; import org.w3c.dom.ls.DOMImplementationLS; import org.w3c.dom.ls.LSSerializer; /** * An incoming message or a message response arrives here. We don't care which it is. * The message is assumed to have a correlationId we can use as a processInstanceId to find * the process. We lock that process and then proceed to unpack the message. * The message unpacker is contained in this class, though an alternate can be injected if desired. * The default unpacker assumes a simple org.w3c.dom.Document which has one layer of elements under * the root. Each of those element has a name which maps to a context object field and a value * to be set in that field. Names which fail to map are ignored. * The elements can contain an attribute named xpath and, if present, is used as the name when the * call to org.apache.commons.beanutils.PropertyUtils.setProperty(object, name, value). * If the root element has an attribute named 'error' then this message is treated as an error and * not unpacked. The content of the attribute is used as the error message and the process instance is aborted. * * @author Roger Parkinson * */ public class GenericEndpoint implements MessageMapper { private static final Logger log = LoggerFactory.getLogger(GenericEndpoint.class); @Autowired WorkflowDAO m_workflowDAO; @Autowired private LockFactory m_lockFactory; @Autowired private WorkflowManager m_workflowManager; @Autowired private BundleSelector m_bundleSelector; private MessageMapper m_messageMapper; public void issueResponseFor(final Message<?> message) { MessageHeaders messageHeaders = message.getHeaders(); Long correlationId = message.getHeaders().get(IntegrationMessageHeaderAccessor.CORRELATION_ID, Long.class); log.debug("ProcessInstance: correlationId {}", correlationId); if (correlationId == null) { log.error("correlation Id is null"); throw new WorkflowException("correlation Id is null"); } final ProcessInstance processInstance = getWorkflowDAO().findProcessInstance(correlationId); if (processInstance == null) { throw new WorkflowException("Failed to find processInstance for " + correlationId); } getBundleSelector().selectBundle(processInstance); List<Lock> locks = ContextUtils.getLocks(processInstance, getLockFactory(), "nz.co.senanque.messaging.GenericEndpoint.issueResponseFor"); LockTemplate lockTemplate = new LockTemplate(locks, new LockAction() { public void doAction() { if (processInstance.getStatus() != TaskStatus.WAIT) { throw new WorkflowException("Process is not in a wait state"); } getWorkflowManager().processMessage(processInstance, message, getMessageMapper()); log.debug("completed lock message processing"); } }); if (!lockTemplate.doAction()) { throw new WorkflowRetryableException("Failed to get a lock"); // this will be retried later, not a hard error } log.debug("completed incomming message processing"); } public void unpackMessage(Message<?> message, Object context) { Object payload = message.getPayload(); if (payload instanceof org.w3c.dom.Document) { Document document = new DOMBuilder().build((org.w3c.dom.Document) payload); if (log.isDebugEnabled()) { log.debug("document\n{}", getStringFromDoc((org.w3c.dom.Document) payload)); } Element root = document.getRootElement(); String errorValue = root.getAttributeValue("error"); if (errorValue != null) { throw new WorkflowException(errorValue); } unpackRoot(root, context); } else { throw new WorkflowException( "Expected payload to be org.w3c.dom.Document, instead found a " + payload.getClass().getName()); } } private String getStringFromDoc(org.w3c.dom.Document doc) { DOMImplementationLS domImplementation = (DOMImplementationLS) doc.getImplementation(); LSSerializer lsSerializer = domImplementation.createLSSerializer(); return lsSerializer.writeToString(doc); } private void unpackRoot(Element element, Object context) { ValidationSessionHolder validationSessonHolder = new ValidationSessionHolderImpl(getValidationEngine()); validationSessonHolder.bind(context); try { @SuppressWarnings("unchecked") Iterator<Text> itr = (Iterator<Text>) element .getDescendants(new ContentFilter(ContentFilter.TEXT | ContentFilter.CDATA)); while (itr.hasNext()) { Text text = itr.next(); String name = getName(text); if (name.equals("id") || name.equals("version")) { continue; } try { Class<?> targetType = PropertyUtils.getPropertyType(context, name); Object value = ConvertUtils.convert(text.getValue(), targetType); PropertyUtils.setProperty(context, name, value); log.debug("name {} value {}", name, text.getValue()); } catch (IllegalAccessException e) { // Ignore these and move on log.debug("{} {}", name, e.getMessage()); } catch (InvocationTargetException e) { // Ignore these and move on log.debug("{} {}", name, e.getTargetException().toString()); } catch (NoSuchMethodException e) { // Ignore these and move on log.debug("{} {}", name, e.getMessage()); } } } finally { validationSessonHolder.close(); } } private String getName(Text text) { Element parent = text.getParentElement(); String xpath = parent.getAttributeValue("xpath"); if (xpath != null) { return xpath; } return parent.getName(); } @PostConstruct public void init() { if (getMessageMapper() == null) { setMessageMapper(this); } } public WorkflowDAO getWorkflowDAO() { return m_workflowDAO; } public void setWorkflowDAO(WorkflowDAO workflowDAO) { m_workflowDAO = workflowDAO; } public LockFactory getLockFactory() { return m_lockFactory; } public void setLockFactory(LockFactory lockFactory) { m_lockFactory = lockFactory; } public WorkflowManager getWorkflowManager() { return m_workflowManager; } public void setWorkflowManager(WorkflowManager workflowManager) { m_workflowManager = workflowManager; } public MessageMapper getMessageMapper() { return m_messageMapper; } public void setMessageMapper(MessageMapper messageMapper) { m_messageMapper = messageMapper; } public ValidationEngine getValidationEngine() { return getWorkflowManager().getValidationEngine(); } public BundleSelector getBundleSelector() { return m_bundleSelector; } public void setBundleSelector(BundleSelector bundleSelector) { m_bundleSelector = bundleSelector; } }