понедельник, августа 06, 2012

Apache Camel Message Processing Actions (Like OSB)


Работая в проекте с OSB, я удивился как легко и просто там работать с xml. Модификация сообщения на Oracle Service Bus действительно делается легко и непринужденно c помощью инструменов Assign, Delete, Insert. Как же наш Apache Camel? А вот в нем не все так просто. Но все необходимое у нас есть, попробуем сделать что-то подобное!

Интерфейс Exchange предоставляет нам метод
 voidsetProperty(String name, Object value)
          Sets a property on the exchange

Это позволяет нам сохранять переменные в контексте всего процесса обработки сообщения.
Вот что представляет собой элемент Assign в OSB:


Данный элемент сохраняет выражение в переменной.Выражение может быть результатом XQuery, XSLT преобразования или просто строго определенным xml.
Аналогом этого элемента является следющее выражение Java DSL:

from("file:c:/data/inbox").setProperty("original").simple("${body}");

В данном случае мы в переменную "original" сохранили тело запроса, помимо simple, Camel позволяет позволяет выполнить XPath выражение, XQuery и XSLT, и многое другое. В данном случае, чтобы сохранить тело оригинального запроса нам достаточно "simple".

C элементами Delete и Insert не все так просто.
Вот интерфейс элементов OSB
Delete:

 XPath, выражение результат которого будет удален из переменной "original"
Для Apache Camel это будет реализовано с помощью процессора:

beanRef("delete","execute('original', '//record[@recordUUID=1]')");

  • "delete" -имя java-bean заданное в spring-context.xml
  • 'execute'- имя метода 
  • 'original' - имя переменной из которой будет произведено удаление
  • '//record[@recordUUID=1]')' - XPath выражение
Insert:


beanRef("insert","execute('raw','//records', 'original')")

  • "insert" - имя java-bean заданное в spring-context.xml
  • 'raw' - имя переменной значение которой будет вставляться
  • '//records' - Xpath выражение, в переменной original, обозначает место в которое будет произведена вставка.
  • 'original' - имя переменной которое будет изменено.


На Apache Camel нам потребуется разработать два процессора:

DeleteProcessor:
package com.bssys.fileid.message;

import org.apache.camel.Exchange;
import org.apache.camel.builder.xml.XPathBuilder;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

public class DeleteProcessor {
 
 public void execute(String invariable, String xpath, Exchange exchange ) {
  
  Document docVariable = exchange.getProperty(invariable, Document.class);
  
  XPathBuilder xpathBuilder = XPathBuilder.xpath(xpath);
  NodeList list = xpathBuilder.evaluate(exchange.getContext(), docVariable, NodeList.class);
  
  if (list!=null){
   for (int size = list.getLength(), i = 0; i < size; i++) {
    Node child = list.item(i);
    Node parentNode = child.getParentNode();
    parentNode.removeChild(child);
   }
  }
  exchange.setProperty(invariable, docVariable);
 }
}

InsertProcessor
package com.bssys.fileid.message;

import org.apache.camel.Exchange;
import org.apache.camel.builder.xml.XPathBuilder;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

public class InsertProcessor {

 public void execute(String variable, String xpath, String invariable, Exchange exchange) {

  Document docVariable = exchange.getProperty(variable, Document.class);
  Document docInVariable = exchange.getProperty(invariable, Document.class);

  Node rootNode = docVariable.getFirstChild();
  
  if (rootNode != null) {

   XPathBuilder xpathBuilder = XPathBuilder.xpath(xpath);
   NodeList list = xpathBuilder.evaluate(exchange.getContext(), docInVariable, NodeList.class);
   
   for (int i = 0; i < list.getLength(); i++) {
    Node item = list.item(i);
    Node importNode = docInVariable.importNode(rootNode, true);
    item.appendChild(importNode);
   }
  }
  exchange.setProperty(invariable, docInVariable);
 }
}

Теперь воспользуемся разработаными инструментами и построим следующий маршрут.
На входе у нас файловый адаптер, который принимает xml-файлы:

<?xml version="1.0" encoding="UTF-8"?>
<storeRegistryCardsRequest>
 <records>
  <record recordUUID="1">
   <cardHolderIdentifiers>cardHolderIdentifiers</cardHolderIdentifiers>
   <card>card</card>
  </record>
  <record recordUUID="2">
   <cardHolderIdentifiers>cardHolderIdentifiers</cardHolderIdentifiers>
   <card>card</card>
  </record>
  <record recordUUID="3">
   <cardHolderIdentifiers>cardHolderIdentifiers</cardHolderIdentifiers>
   <card>card</card>
  </record>
 </records>
</storeRegistryCardsRequest>

Удаляет record с UUID=1, и добавляет record c UUID=4 Вот такой маршрут у нас получился в итоге.
from("file:c:/data/inbox3").
  setProperty("original").simple("${body}").
  beanRef("delete","execute('original', '//record[@recordUUID=1]')").
  setProperty("raw").simple("<record recordUUID=\"4\"><cardHolderIdentifiers>cardHolderIdentifiers</cardHolderIdentifiers><card>card</card></record>").
  beanRef("insert","execute('raw','//records', 'original')").
  setBody().simple("${property.original}").
  to("file:c:/data/outbox");
Результат обработки:
<storeRegistryCardsRequest>
 <records>

  <record recordUUID="2">
   <cardHolderIdentifiers>cardHolderIdentifiers</cardHolderIdentifiers>
   <card>card</card>
  </record>
  <record recordUUID="3">
   <cardHolderIdentifiers>cardHolderIdentifiers</cardHolderIdentifiers>
   <card>card</card>
  </record>
  <record recordUUID="4">
   <cardHolderIdentifiers>cardHolderIdentifiers</cardHolderIdentifiers>
   <card>card</card>
  </record>
 </records>
</storeRegistryCardsRequest>


Удачи!

Комментариев нет: