package hk.hku.cecid.ebms.spa.event;
import hk.hku.cecid.ebms.pkg.EbxmlMessage;
import hk.hku.cecid.ebms.pkg.MessageHeader;
import hk.hku.cecid.ebms.spa.EbmsProcessor;
import hk.hku.cecid.ebms.spa.dao.MessageDAO;
import hk.hku.cecid.ebms.spa.dao.MessageDVO;
import hk.hku.cecid.ebms.spa.dao.MessageServerDAO;
import hk.hku.cecid.ebms.spa.handler.MessageClassifier;
import hk.hku.cecid.ebms.spa.task.EbmsEventListener;
import org.apache.camel.CamelContext;
import org.apache.camel.Exchange;
import org.apache.camel.ExchangePattern;
import org.apache.camel.ProducerTemplate;
import org.apache.camel.impl.DefaultCamelContext;
import org.apache.camel.impl.DefaultExchange;
import org.apache.camel.spi.Synchronization;
import javax.xml.soap.AttachmentPart;
import javax.xml.soap.SOAPMessage;
import java.io.ByteArrayOutputStream;
import java.io.Closeable;
import java.io.IOException;
import java.util.*;
/**
* Created by aaronwalker on 8/07/2016.
*/
public class EbmsCamelListener extends EbmsEventListener implements Closeable {
protected static final String MSG_PROPERTY_MSG_ID = "ebxml_message_id";
protected static final String MSG_PROPERTY_CPA_ID = "ebxml_cpa_id";
protected static final String MSG_PROPERTY_SERVICE = "ebxml_service";
protected static final String MSG_PROPERTY_SERVICE_TYPE = "ebxml_service_type";
protected static final String MSG_PROPERTY_ACTION = "ebxml_action";
protected static final String MSG_PROPERTY_CONV_ID = "ebxml_conv_id";
protected static final String MSG_PROPERTY_FROM_PARTY_ID = "ebxml_from_party_id";
protected static final String MSG_PROPERTY_FROM_PARTY_TYPE = "ebxml_from_party_type";
protected static final String MSG_PROPERTY_TO_PARTY_ID = "ebxml_to_party_id";
protected static final String MSG_PROPERTY_TO_PARTY_TYPE = "ebxml_to_party_type";
protected static final String MSG_PROPERTY_TIMESTAMP = "ebxml_timestamp";
protected static final String MSG_PROPERTY_TIMETOLIVE = "ebxml_timestolive";
protected static final String MSG_PROPERTY_PAYLOAD_ID = "ebxml_payload_id";
protected static final String MSG_PROPERTY_PAYLOAD_TYPE = "ebxml_payload_type";
protected static final String MSG_PROPERTY_REF_MSG_ID = "ebxml_ref_message_id";
private CamelContext camelContext;
private ProducerTemplate eventHandler;
@Override
protected void init() throws Exception {
super.init();
camelContext = new DefaultCamelContext();
camelContext.start();
eventHandler = camelContext.createProducerTemplate();
}
@Override
public void messageSent(EbxmlMessage requestMessage) {
//publish sent event
send(requestMessage, "vm:ebmsSent");
}
@Override
public void messageReceived(EbxmlMessage requestMessage) {
//we only want to support a single inbound delivery endpoint
send(requestMessage, "direct-vm:ebmsReceived", "?failIfNoConsumers=false");
}
@Override
public void responseReceived(EbxmlMessage acknowledgement) {
//publish ack received event
send(acknowledgement, "vm:ebmsAckReceived");
}
@Override
public void errorOccurred(EbxmlMessage errorMessage) {
send(errorMessage, "vm:ebmsErrors");
}
@Override
public void close() throws IOException {
if(camelContext != null) {
try {
camelContext.stop();
} catch (Exception e) {
throw new IOException(e);
}
}
}
private void send(EbxmlMessage ebxml, final String endpoint) {
send(ebxml,endpoint,"?discardIfNoConsumers=true&multipleConsumers=true");
}
private void send(EbxmlMessage ebxml, final String endpoint, final String options) {
try {
List<Exchange> exchanges = convertEbxmlMessage(ebxml);
for(Exchange exchange: exchanges) {
exchange.getIn().setHeader("ebmsEvent", endpoint);
eventHandler.asyncCallback(endpoint + options, exchange, new Synchronization() {
@Override
public void onComplete(Exchange exchange) {
EbmsProcessor.core.log.info(endpoint + ":successfully sent:" + exchange.getIn().getHeader(MSG_PROPERTY_MSG_ID));
if("direct-vm:ebmsReceived".equals(exchange.getIn().getHeader("ebmsEvent"))) {
updateDeliveryStatus(exchange.getIn().getHeader(MSG_PROPERTY_MSG_ID,String.class),
MessageClassifier.INTERNAL_STATUS_DELIVERED,
"Message is delivered");
}
}
@Override
public void onFailure(Exchange exchange) {
EbmsProcessor.core.log.warn(endpoint + ":failed to send:" + exchange.getIn().getHeader(MSG_PROPERTY_MSG_ID));
if("direct-vm:ebmsReceived".equals(exchange.getIn().getHeader("ebmsEvent"))) {
updateDeliveryStatus(exchange.getIn().getHeader(MSG_PROPERTY_MSG_ID,String.class),
MessageClassifier.INTERNAL_STATUS_DELIVERY_FAILURE,
"Failed Delivery:" + exchange.getException());
}
}
});
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private void updateDeliveryStatus(String messageId, String status, String description) {
try {
MessageDAO messageDao = (MessageDAO) EbmsProcessor.core.dao.createDAO(MessageDAO.class);
MessageDVO messageDvo = (MessageDVO) messageDao.createDVO();
messageDvo.setMessageId(messageId);
messageDvo.setMessageBox(MessageClassifier.MESSAGE_BOX_INBOX);
if (messageDao.findMessage(messageDvo)) {
if (messageDvo.getStatus().equals(MessageClassifier.INTERNAL_STATUS_PROCESSED)) {
MessageServerDAO messageServerDao = (MessageServerDAO) EbmsProcessor.core.dao.createDAO(MessageServerDAO.class);
messageDvo.setStatus(status);
messageDvo.setStatusDescription(description);
messageServerDao.clearMessage(messageDvo);
}
}
} catch (Exception e) {
EbmsProcessor.core.log.error("",e);
}
}
private List<Exchange> convertEbxmlMessage(EbxmlMessage ebxml) throws Exception {
List<Exchange> exchanges = new ArrayList<Exchange>();
Map<String,Object> header = new HashMap<String, Object>();
header.put(MSG_PROPERTY_MSG_ID, ebxml.getMessageId());
header.put(MSG_PROPERTY_CPA_ID, ebxml.getCpaId());
header.put(MSG_PROPERTY_SERVICE, ebxml.getService());
header.put(MSG_PROPERTY_SERVICE_TYPE, ebxml.getServiceType());
header.put(MSG_PROPERTY_ACTION, ebxml.getAction());
header.put(MSG_PROPERTY_CONV_ID, ebxml.getConversationId());
header.put(MSG_PROPERTY_REF_MSG_ID,ebxml.getMessageHeader().getRefToMessageId());
if (ebxml.getFromPartyIds().hasNext()) {
MessageHeader.PartyId partyId = (MessageHeader.PartyId) ebxml.getFromPartyIds().next();
header.put(MSG_PROPERTY_FROM_PARTY_ID,partyId.getId());
header.put(MSG_PROPERTY_FROM_PARTY_TYPE, partyId.getType());
}
if (ebxml.getToPartyIds().hasNext()) {
MessageHeader.PartyId partyId = (MessageHeader.PartyId) ebxml.getFromPartyIds().next();
header.put(MSG_PROPERTY_TO_PARTY_ID,partyId.getId());
header.put(MSG_PROPERTY_TO_PARTY_TYPE, partyId.getType());
}
header.put(MSG_PROPERTY_TIMESTAMP,ebxml.getTimestamp());
header.put(MSG_PROPERTY_TIMETOLIVE,ebxml.getTimeToLive());
if(ebxml.getPayloadCount() > 0) {
SOAPMessage msg = ebxml.getSOAPMessage();
Iterator it = msg.getAttachments();
while(it.hasNext()) {
DefaultExchange exchange = new DefaultExchange(camelContext);
exchange.getIn().setHeaders(header);
AttachmentPart part = (AttachmentPart)it.next();
exchange.getIn().setHeader(MSG_PROPERTY_PAYLOAD_ID,part.getContentId());
exchange.getIn().setHeader(MSG_PROPERTY_PAYLOAD_TYPE,part.getContentType());
exchange.getIn().setBody(part.getRawContentBytes());
exchanges.add(exchange);
}
} else {
DefaultExchange exchange = new DefaultExchange(camelContext);
exchange.setPattern(ExchangePattern.InOut);
exchange.getIn().setHeaders(header);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ebxml.writeTo(bos);
exchange.getIn().setBody(bos.toByteArray());
exchanges.add(exchange);
}
return exchanges;
}
}