<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:blogger='http://schemas.google.com/blogger/2008' xmlns:georss='http://www.georss.org/georss' xmlns:gd="http://schemas.google.com/g/2005" xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-37848572</id><updated>2024-09-09T00:59:15.714+09:00</updated><category term="Apache Camel"/><category term="ServiceMix"/><category term="java"/><category term="ActiveMQ"/><category term="JMS"/><category term="XA"/><category term="cluster"/><category term="webservices"/><category term="Asynchronous I/O"/><category term="AsynchronousServerSocketChannel"/><category term="AsynchronousSocketChannel"/><category term="JDBC"/><category term="NIO2"/><category term="WebSocket"/><category term="design patterns"/><category term="transaction"/><category term="AspectJ"/><category term="Atomikos"/><category term="CXF"/><category term="CompletionHandler"/><category term="JSR 356"/><category term="OSB"/><category term="Rest"/><category term="State"/><category term="ZeroMQ"/><category term="minitroopers"/><category term="xml"/><title type='text'>Эксперименты и исследования</title><subtitle type='html'>Apache Camel, ServiceMix, ActiveMQ, java</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://jser.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/37848572/posts/default?redirect=false'/><link rel='alternate' type='text/html' href='http://jser.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><link rel='next' type='application/atom+xml' href='http://www.blogger.com/feeds/37848572/posts/default?start-index=26&amp;max-results=25&amp;redirect=false'/><author><name>JSer</name><uri>http://www.blogger.com/profile/17455716060338444232</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>27</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-37848572.post-3468301183022165249</id><published>2016-10-14T21:49:00.000+09:00</published><updated>2016-11-15T18:23:08.361+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="java"/><title type='text'>The Skip Locked feature in Postgres 9.5 (message queue)</title><content type='html'>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
В углу что-то мяукают по ActiveMQ. А между тем в postgress запилили крутую фичу. for update skip locked позволяет пропустить в select строки заблокированные в другой транзакции. К сожалению такое не прокатит в Oracle. Но тем не менее для мессажинга можно использовать уже сейчас. Из коробки имеем:&lt;br /&gt;
&lt;br /&gt;
&lt;ul style=&quot;text-align: left;&quot;&gt;
&lt;li&gt;транзакции&lt;/li&gt;
&lt;li&gt;персистенцию сообщений&lt;/li&gt;
&lt;li&gt;клиент(обычный jdbc)&lt;/li&gt;
&lt;li&gt;кластер(обычные средства предоставляемые postgres)&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;package com.bssys.blog.messaging;

import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;

import javax.sql.DataSource;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceUtils;
import org.springframework.jdbc.datasource.SingleConnectionDataSource;
import org.springframework.stereotype.Component;

@Component(&quot;consumer&quot;)
public class Consumer implements Runnable {

 private String select = &quot;select id, message from skiptable where status = &#39;TODO&#39; order by priority asc limit 1 for update skip locked&quot;;
 private String delete = &quot;delete from skiptable where id = ? &quot;;

 @Autowired
 private DataSource dataSource;

 public void run() {

  while (!Thread.currentThread().isInterrupted()) {
   Connection con = DataSourceUtils.getConnection(dataSource);
   SingleConnectionDataSource singleDs = new SingleConnectionDataSource(con, true);
   try {
    JdbcTemplate jdbcTemplate = new JdbcTemplate(singleDs);
    List&amp;lt;Map&amp;lt;String, Object&amp;gt;&amp;gt; queryForList = jdbcTemplate.queryForList(select);
    if (!queryForList.isEmpty()) {
     for (Map&amp;lt;String, Object&amp;gt; map : queryForList) {
      String id = (String) map.get(&quot;id&quot;);
      String message = (String) map.get(&quot;message&quot;);
      jdbcTemplate.update(delete, new Object[] { id });
      System.out.println(Thread.currentThread().getName() + &quot;:&quot; + message);
     }
     con.commit();
    } else {
     System.out.println(Thread.currentThread().getName() + &quot;: no mass&quot;);
    }
   } catch (Exception e) {
    try {
     con.rollback();
    } catch (SQLException e1) {
     e1.printStackTrace();
    }
   } finally {
    singleDs.destroy();
   }
  }
 }
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
</content><link rel='replies' type='application/atom+xml' href='http://jser.blogspot.com/feeds/3468301183022165249/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/37848572/3468301183022165249' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/37848572/posts/default/3468301183022165249'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/37848572/posts/default/3468301183022165249'/><link rel='alternate' type='text/html' href='http://jser.blogspot.com/2016/10/the-skip-locked-feature-in-postgres-95.html' title='The Skip Locked feature in Postgres 9.5 (message queue)'/><author><name>JSer</name><uri>http://www.blogger.com/profile/17455716060338444232</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-37848572.post-8980197914844615286</id><published>2016-10-14T00:48:00.001+09:00</published><updated>2016-10-14T00:58:45.229+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Apache Camel"/><category scheme="http://www.blogger.com/atom/ns#" term="ServiceMix"/><category scheme="http://www.blogger.com/atom/ns#" term="webservices"/><title type='text'>Dynamic Webservice registration on ServiceMix</title><content type='html'>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
Рассмотрим как можно динамически зарегистрировать webservice на ServiceMix. В общем смысле подобным образом можно динамически регистрировать любые сервисы. Для примера реализуем простейший сервис c использованием Apache Camel. Сервис будет слушать файловую папку, в которую будет выгружаться файлы со ссылками на wsdl. Эти сервисы и будем динамически регистрировать.&lt;br /&gt;
&lt;br /&gt;
Общий смысл такой. Создадим bean с методом registy который будет принимать ссылку на wsdl, создавать соответствующую конечную точку(endpoint) и динамически добавлять route, принимающий сообщения из нее.
&lt;br /&gt;
&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;package com.bssys.dynamic.cxf_example;

import javax.xml.namespace.QName;

import org.apache.camel.CamelContext;
import org.apache.camel.component.cxf.CxfEndpoint;
import org.apache.camel.component.cxf.DataFormat;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component(&quot;dynamicLogic&quot;)
public class DynamicLogic {
 
 @Autowired
 private CamelContext context;
 
 public void registry(String url) throws Exception{
  
  CxfEndpoint cxfEndpoint = new CxfEndpoint();
  cxfEndpoint.setDataFormat(DataFormat.RAW);
  cxfEndpoint.setWsdlURL(url);
  
  QName serviceName = cxfEndpoint.getServiceName();
  
  cxfEndpoint.setAddress(&quot;http://127.0.0.1:8383/&quot;+serviceName.getLocalPart());
  
  context.addEndpoint(serviceName.getLocalPart(), cxfEndpoint);
  DynamicRouteBuilder builder = new DynamicRouteBuilder(serviceName.getLocalPart());
  context.addRoutes(builder);
  
 }
 
}
&lt;/code&gt;&lt;/pre&gt;
Динамический route принимающий сообщения из зарегистрированной конечной точки
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;package com.bssys.dynamic.cxf_example;

import org.apache.camel.builder.RouteBuilder;

public class DynamicRouteBuilder extends RouteBuilder {

 private String uri;

 public DynamicRouteBuilder(String uri) {
  this.uri = uri;
 }

 @Override
 public void configure() throws Exception {
  from(uri).to(&quot;direct:template&quot;);
 }

}
&lt;/code&gt;&lt;/pre&gt;
Маршрут Apache Camel

&lt;br /&gt;
&lt;pre&gt;&lt;code class=&quot;language-xml&quot;&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&amp;gt;
&amp;lt;beans xmlns=&quot;http://www.springframework.org/schema/beans&quot;
 xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot; xmlns:camel=&quot;http://camel.apache.org/schema/spring&quot;
 xmlns:osgi=&quot;http://www.springframework.org/schema/osgi&quot; xmlns:context=&quot;http://www.springframework.org/schema/context&quot;
 xsi:schemaLocation=&quot;
       http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/osgi http://www.springframework.org/schema/osgi/spring-osgi.xsd
       http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd&quot;&amp;gt;

 &amp;lt;context:annotation-config /&amp;gt;
 &amp;lt;context:component-scan base-package=&quot;com.bssys.dynamic.cxf_example&quot; /&amp;gt;

 &amp;lt;camelContext id=&quot;mainContext&quot; xmlns=&quot;http://camel.apache.org/schema/spring&quot;&amp;gt;
  &amp;lt;route&amp;gt;
   &amp;lt;from uri=&quot;file://C:/data/registryws&quot; /&amp;gt;
   &amp;lt;camel:bean ref=&quot;dynamicLogic&quot; method=&quot;registry&quot; /&amp;gt;
  &amp;lt;/route&amp;gt;
  &amp;lt;route&amp;gt;
   &amp;lt;from uri=&quot;direct:template&quot; /&amp;gt;
   &amp;lt;log message=&quot;The message contains ${body}&quot; /&amp;gt;
  &amp;lt;/route&amp;gt;
 &amp;lt;/camelContext&amp;gt;

&amp;lt;/beans&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
</content><link rel='replies' type='application/atom+xml' href='http://jser.blogspot.com/feeds/8980197914844615286/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/37848572/8980197914844615286' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/37848572/posts/default/8980197914844615286'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/37848572/posts/default/8980197914844615286'/><link rel='alternate' type='text/html' href='http://jser.blogspot.com/2016/10/dynamic-webservice-registration-on.html' title='Dynamic Webservice registration on ServiceMix'/><author><name>JSer</name><uri>http://www.blogger.com/profile/17455716060338444232</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-37848572.post-3004634452119139392</id><published>2016-10-07T17:33:00.001+09:00</published><updated>2016-10-07T17:39:39.615+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="design patterns"/><category scheme="http://www.blogger.com/atom/ns#" term="java"/><category scheme="http://www.blogger.com/atom/ns#" term="State"/><title type='text'>If-less programming (java) Part №2</title><content type='html'>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
В предыдущей части мы рассмотрели как избавиться от if в паттерне &quot;фабричный метод&quot;. Но есть паттерны специально придуманные чтобы избавить вас от этой проблемы. Один из них паттерн &quot;Состояние&quot;.&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhBVBoN7zj198NKU2N9z9oP7FSFCM4T8n7xZ4cpSaAoD2Y_9NR0umGVlVCgbiaFZkCZzHdmbVjMS6UdpL6P_zw33QQDnREclWXgtRPQ5uXG0unFwpzgQL0Iql4AcFhO3y-yL-vY/s1600/uml-state.gif&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;253&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhBVBoN7zj198NKU2N9z9oP7FSFCM4T8n7xZ4cpSaAoD2Y_9NR0umGVlVCgbiaFZkCZzHdmbVjMS6UdpL6P_zw33QQDnREclWXgtRPQ5uXG0unFwpzgQL0Iql4AcFhO3y-yL-vY/s400/uml-state.gif&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;
Рассмотрим небольшой пример. У нас есть класс Tractor, у которого есть состояние: Ориентация по стороне света и координаты. У трактора есть возможность повернуться(метод turn) и двигаться в этом направлении (метод move).&lt;br /&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;package com.bssys.blog;

public class Tractor {

 public Orientation currentOrientation;
 public Coordinate currentCoordinate;

 public Tractor() {
  currentOrientation = Orientation.North;
  currentCoordinate = new Coordinate(0, 0);
 }

 public Tractor(Orientation currentOrientation, Coordinate currentCoordinate) {
  this.currentOrientation = currentOrientation;
  this.currentCoordinate = currentCoordinate;
 }

 public void move() {

  if (currentOrientation == Orientation.North) {
   currentCoordinate.y = currentCoordinate.y + 1;
  } else if (currentOrientation == Orientation.South) {
   currentCoordinate.y = currentCoordinate.y - 1;
  } else if (currentOrientation == Orientation.West) {
   currentCoordinate.x = currentCoordinate.x - 1;
  } else if (currentOrientation == Orientation.East) {
   currentCoordinate.x = currentCoordinate.x + 1;
  }
 }

 public void turn() {
  if (currentOrientation == Orientation.North) {
   currentOrientation = Orientation.East;
  } else if (currentOrientation == Orientation.South) {
   currentOrientation = Orientation.West;
  } else if (currentOrientation == Orientation.West) {
   currentOrientation = Orientation.North;
  } else if (currentOrientation == Orientation.East) {
   currentOrientation = Orientation.South;
  }
 }

 public Orientation getCurrentOrientation() {
  return currentOrientation;
 }

 public Coordinate getCurrentCoordinate() {
  return currentCoordinate;
 }

 @Override
 public String toString() {
  return &quot;&quot; + currentOrientation + &quot; &quot; + currentCoordinate;
 }
}

class Coordinate {

 public int x;
 public int y;

 public Coordinate(int x, int y) {
  this.x = x;
  this.y = y;
 }

 @Override
 public String toString() {
  return &quot;x=&quot; + x + &quot; &quot; + &quot;y=&quot; + y;
 }
}

enum Orientation {
 North, South, West, East
}
&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
Обычно решение в лоб вырождается в портянку if-else в зависимости от некоторого состояния. В нашем случае состояние представляет собой направление движения, т.е. ориентацию по стороне света. Для того чтобы решить эту проблему выделим интерфейс состояния. Он будет представлять собой следующее:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;interface State {
 public void turn(Tractor tractor);
 public void move();
}&lt;/code&gt;&lt;/pre&gt;
Реализуем этот интерфейс для всех наших возможных состояний, и добавим в класс Tractor поле хранящее текущее состояние согласно нашему паттерну:
&lt;br /&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;class EastState implements State {
 
 private Coordinate coordinate;
 
 public EastState(Coordinate coordinate) {
  this.coordinate = coordinate;
 }

 public void turn(Tractor tractor) {
  tractor.setCurrentState(new SouthState(coordinate));
 }
 
 public void move() {
  coordinate.x = coordinate.x + 1;
 }

 @Override
 public String toString() {
  return &quot;East:&quot; + coordinate;
 }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;class NorthState implements State {
 
 private Coordinate coordinate;
 
 public NorthState(Coordinate coordinate) {
  this.coordinate = coordinate;
 }

 public void turn(Tractor tractor) {
  tractor.setCurrentState(new EastState(coordinate));
 }
 
 public void move() {
  coordinate.y = coordinate.y + 1;
 }

 @Override
 public String toString() {
  return &quot;North:&quot; + coordinate;
 }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;class SouthState implements State {
 
 private Coordinate coordinate;
 
 public SouthState(Coordinate coordinate) {
  this.coordinate = coordinate;
 }

 public void turn(Tractor tractor) {
  tractor.setCurrentState(new WestState(coordinate));
 }

 public void move() {
  coordinate.y = coordinate.y - 1;
 }

 @Override
 public String toString() {
  return &quot;South:&quot; + coordinate;
 }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;class WestState implements State {
 
 private Coordinate coordinate;
 
 public WestState(Coordinate coordinate) {
  this.coordinate = coordinate;
 }

 public void turn(Tractor tractor) {
  tractor.setCurrentState(new NorthState(coordinate));
 }

 public void move() {
  coordinate.x = coordinate.x - 1;
 }

 @Override
 public String toString() {
  return &quot;West:&quot; + coordinate;
 }
}&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
Смотрите, логика переходов по состояниям у нас теперь находится в специальных классах. И класс Tractor теперь просто должен делегировать свои действия текущему состоянию. Обратите внимание, что меняет состояние только метод turn.
Таким образом итоговый код класса Tractor вырождается в следующее:
&lt;br /&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;public class Tractor {

 private State currentState;

 public Tractor() {
  currentState = new NorthState(new Coordinate(0, 0));
 }

 public void move() {
  currentState.move();
 }

 public void turn() {
  currentState.turn(this);
 }

 public void setCurrentState(State state) {
  this.currentState = state;
 }

 @Override
 public String toString() {
  return currentState.toString();
 }

 public static void main(String[] args) {
  Tractor tractor = new Tractor();
  System.out.println(tractor);
  tractor.turn();
  tractor.move();
  tractor.move();
  tractor.move();
  System.out.println(tractor);
  tractor.turn();
  tractor.move();
  tractor.move();
  tractor.move();
  System.out.println(tractor);
 }
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
</content><link rel='replies' type='application/atom+xml' href='http://jser.blogspot.com/feeds/3004634452119139392/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/37848572/3004634452119139392' title='Комментарии: 1'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/37848572/posts/default/3004634452119139392'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/37848572/posts/default/3004634452119139392'/><link rel='alternate' type='text/html' href='http://jser.blogspot.com/2016/10/if-less-programming-java-part-2.html' title='If-less programming (java) Part №2'/><author><name>JSer</name><uri>http://www.blogger.com/profile/17455716060338444232</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhBVBoN7zj198NKU2N9z9oP7FSFCM4T8n7xZ4cpSaAoD2Y_9NR0umGVlVCgbiaFZkCZzHdmbVjMS6UdpL6P_zw33QQDnREclWXgtRPQ5uXG0unFwpzgQL0Iql4AcFhO3y-yL-vY/s72-c/uml-state.gif" height="72" width="72"/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-37848572.post-8562486323569499328</id><published>2016-10-06T22:20:00.001+09:00</published><updated>2016-10-07T17:33:10.928+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="design patterns"/><category scheme="http://www.blogger.com/atom/ns#" term="java"/><title type='text'>If-less programming (java) Part №1</title><content type='html'>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
Господа, всем известно что нагромождение конструкций if-else есть признак плохого тона. Попробуем обойтись без их использования в некоторых ситуациях.&lt;br /&gt;
&lt;br /&gt;
If-else в фабричном методе.&lt;br /&gt;
&lt;br /&gt;
Для примера есть стандартная реализация паттерна &quot;Фабричный метод&quot;, создающая объекты в зависимости от некоторого ключа.&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi9qZig8jkmBGJqyj99L4yIohbX6NDPyYHjNSiqYm1YE0bgZwR4_nKGCrkQgoHEJdqS8lNFioNsmDo0Eh6vwHSUg0G7NxQ7dg8Ln8HWm75Qi9KNW9uCyK2nB-8WW0rAWv4xoiYv/s1600/uml-factory-method-1.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;211&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi9qZig8jkmBGJqyj99L4yIohbX6NDPyYHjNSiqYm1YE0bgZwR4_nKGCrkQgoHEJdqS8lNFioNsmDo0Eh6vwHSUg0G7NxQ7dg8Ln8HWm75Qi9KNW9uCyK2nB-8WW0rAWv4xoiYv/s640/uml-factory-method-1.jpg&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;package com.bssys.blog.example;&lt;br /&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;
public class App {
 private Factory factory;

 public App() {
  factory = new Factory();
 }

 public void onMessage(String message) {
  Product product = factory.createProduct(message);
  product.action();
 }

 public static void main(String[] args) {
  App app = new App();
  app.onMessage(&quot;A&quot;);
  app.onMessage(&quot;B&quot;);
 }
}

interface Product {
 public void action();
}

class ConcreteProductA implements Product {

 public void action() {
  System.out.println(&quot;ConcreteProductA&quot;);
 }

}

class ConcreteProductB implements Product {

 public void action() {
  System.out.println(&quot;ConcreteProductB&quot;);
 }

}

class Factory {
 Product createProduct(String key) {
  Product product = null;
  if (&quot;A&quot;.equals(key)) {
   product = new ConcreteProductA();
  } else if (&quot;B&quot;.equals(key)) {
   product = new ConcreteProductB();
  }
  return product;
 }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
Тут присутcnвует громоздкая конструкция if-else. Как избавиться от нее?&lt;br /&gt;
&lt;br /&gt;
Использовать HashMap.&lt;br /&gt;
&lt;br /&gt;
Фактически введем динамический if. Таким образом класс Factory будет выглядеть следующим образом:&lt;br /&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;class Factory {
 
 private Map&amp;lt;String, Product&amp;gt; factoryMap;
 
 public Factory() {
  factoryMap = new HashMap&amp;lt;String, Product&amp;gt;();
  factoryMap.put(&quot;A&quot;, new ConcreteProductA());
  factoryMap.put(&quot;B&quot;, new ConcreteProductB());
 }
 
 Product createProduct(String key) {
  Product product = factoryMap.get(key);
  return product;
 }
}&lt;/code&gt;&lt;/pre&gt;
Тут мы немного сломали ожидаемое поведение, так как фабричный метод будет возвращать одни и теже объекты. Иногда это даже хорошо. Но тем не менее чтобы ожидаемое поведение было одинаковым введем интерфейс фабрики которая будет создавать конкретные продукты. И отрефакторим код:
&lt;br /&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;package com.bssys.blog.example;

import java.util.HashMap;
import java.util.Map;

public class App {
 private CommonFactory factory;

 public App() {
  factory = new CommonFactory();
 }

 public void onMessage(String message) {
  Product product = factory.createProduct(message);
  product.action();
 }

 public static void main(String[] args) {
  App app = new App();
  app.onMessage(&quot;A&quot;);
  app.onMessage(&quot;B&quot;);
 }
}

interface Product {
 public void action();
}

class ConcreteProductA implements Product {

 public void action() {
  System.out.println(&quot;ConcreteProductA&quot;);
 }

}

class ConcreteProductB implements Product {

 public void action() {
  System.out.println(&quot;ConcreteProductB&quot;);
 }

}

interface Factory{
 Product createProduct();
}

class ConcreteProductAFactory implements Factory{

 public Product createProduct() {
  return new ConcreteProductA();
 }
}

class ConcreteProductBFactory implements Factory{

 public Product createProduct() {
  return new ConcreteProductB();
 }
}

class CommonFactory {
 
 private Map&amp;lt;String, Factory&amp;gt; factoryMap;
 
 public CommonFactory() {
  factoryMap = new HashMap&amp;lt;String, Factory&amp;gt;();
  factoryMap.put(&quot;A&quot;, new ConcreteProductAFactory());
  factoryMap.put(&quot;B&quot;, new ConcreteProductBFactory());
 }
 
 Product createProduct(String key) {
  Factory factory = factoryMap.get(key);
  return factory.createProduct();
 }
}
&lt;/code&gt;&lt;/pre&gt;
Уже лучше. Мы выделили интерфейс фабрики. Теперь у нас есть класс CommonFactory, который по сути является реестром всех объектов. Теперь если подключить мощь Spring мы могли бы вынести наш HashMap в конфиг, и управлять поведением не перекомпилируя наш код. Как вы видите мы нагенерили много кода. И хотя это правильный подход в java, есть немного другой способ избавиться от if.&lt;br /&gt;
&lt;br /&gt;
Использовать enum с абстрактным методом.&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;enum Type {
 A {
  @Override
  public Product createProduct() {
   return new ConcreteProductA();
  }
 },
 B {
  @Override
  public Product createProduct() {
   return new ConcreteProductB();
  }
 };

 public abstract Product createProduct();
}&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
В таком случае наш фабричный метод переродится в следующее:
&lt;br /&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;class Factory {
 
 public Product createProduct(String key) {
  Type valueOf = Type.valueOf(key);
  return valueOf.createProduct();
 }
}&lt;/code&gt;&lt;/pre&gt;
Тут конечно тоже есть неявный динамический if при выполнении операции valueOf, но все равно выглядит элегантно.&lt;br /&gt;
&lt;br /&gt;
Есть еще один способ обойтись без if используя reflection, но я не приветствую такой подход. Если хотите динамичности то берите наверное другой язык, а не java.&lt;/div&gt;
</content><link rel='replies' type='application/atom+xml' href='http://jser.blogspot.com/feeds/8562486323569499328/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/37848572/8562486323569499328' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/37848572/posts/default/8562486323569499328'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/37848572/posts/default/8562486323569499328'/><link rel='alternate' type='text/html' href='http://jser.blogspot.com/2016/10/if-less-programming-java.html' title='If-less programming (java) Part №1'/><author><name>JSer</name><uri>http://www.blogger.com/profile/17455716060338444232</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi9qZig8jkmBGJqyj99L4yIohbX6NDPyYHjNSiqYm1YE0bgZwR4_nKGCrkQgoHEJdqS8lNFioNsmDo0Eh6vwHSUg0G7NxQ7dg8Ln8HWm75Qi9KNW9uCyK2nB-8WW0rAWv4xoiYv/s72-c/uml-factory-method-1.jpg" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-37848572.post-4785972176685661193</id><published>2016-04-26T18:13:00.003+09:00</published><updated>2016-04-26T18:29:30.077+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="ActiveMQ"/><category scheme="http://www.blogger.com/atom/ns#" term="Apache Camel"/><category scheme="http://www.blogger.com/atom/ns#" term="Atomikos"/><category scheme="http://www.blogger.com/atom/ns#" term="JDBC"/><category scheme="http://www.blogger.com/atom/ns#" term="JMS"/><category scheme="http://www.blogger.com/atom/ns#" term="transaction"/><category scheme="http://www.blogger.com/atom/ns#" term="XA"/><title type='text'>ServiceMix Atomikos XA transaction JDBC JMS</title><content type='html'>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
Снова вернемся к важному вопросу о XA транзакциях. Рассмотрим реализацию XA транзакций с использованием популярного менеджера распределенных транзакций&amp;nbsp;&lt;a href=&quot;https://www.atomikos.com/&quot; target=&quot;_blank&quot;&gt;Atomikos&lt;/a&gt;. Это широко используемый диспетчер транзакций JTA с открытым исходным кодом, который предназначен для сред отличных от J2EE. Это как раз наш случай.&lt;br /&gt;
В данном примере будет представлена готовая конфигурация транзакций.&lt;br /&gt;
Приложение разделено на модули, которые разделяют настройку ресурсов, от бизнес логики. Фактически вы можете установить модули в ServiceMix и заняться разработкой, не разгребая как во многих других примерах системную и бизнес логику.&lt;br /&gt;
Итак модули:&lt;br /&gt;
&lt;ol style=&quot;text-align: left;&quot;&gt;
&lt;li&gt;ts - поднимает транзакционные артефакты&amp;nbsp;JtaTransactionManager,&amp;nbsp;UserTransactionManager,&amp;nbsp;UserTransactionImp&lt;/li&gt;
&lt;li&gt;ds - поднимает транзакционный источник данных на примере БД Oracle&amp;nbsp;OracleXADataSource,&amp;nbsp;AtomikosDataSourceBean&lt;/li&gt;
&lt;li&gt;amq - поднимает транзакционные артефакты для работы с JMS на примере ActiveMQ, ActiveMQXAConnectionFactory, AtomikosConnectionFactoryBean&lt;/li&gt;
&lt;li&gt;testXA - приложение для ServiceMix демонстрируещее работающие транзакции&amp;nbsp;&lt;/li&gt;
&lt;/ol&gt;
Контекст модуля ts определяет два бина&amp;nbsp;txMgr и&amp;nbsp;userTransaction. Классы реализации предоставляются Atomikos и реализуют интерфейсы TransactionManager и UserTransaction из стандарта J2EE. После этого определяется бин&amp;nbsp;jtaTxMgr с классом реализации JtaTransactionManager и внедряются два бина транзакций предоставляемые Atomikos. Это информирует Spring о необходимости использования Atomikos JTA для управления транзакциями. Далее сконфигурированный менеджер транзакций выставляется как osgi-сервис, чтобы позволить другим модулям его переиспользовать.&lt;br /&gt;
&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;&lt;code class=&quot;language-xml&quot;&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&amp;gt;
&amp;lt;beans xmlns=&quot;http://www.springframework.org/schema/beans&quot;
       xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;
       xmlns:osgi=&quot;http://www.springframework.org/schema/osgi&quot;
       xsi:schemaLocation=&quot;
       http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/osgi http://www.springframework.org/schema/osgi/spring-osgi.xsd
       http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd&quot;&amp;gt;

   &amp;lt;bean id=&quot;jtaTxMgr&quot; class=&quot;org.springframework.transaction.jta.JtaTransactionManager&quot;&amp;gt;
  &amp;lt;property name=&quot;transactionManager&quot; ref=&quot;txMgr&quot; /&amp;gt;
  &amp;lt;property name=&quot;userTransaction&quot; ref=&quot;userTransaction&quot; /&amp;gt;
 &amp;lt;/bean&amp;gt;

 &amp;lt;bean id=&quot;txMgr&quot; class=&quot;com.atomikos.icatch.jta.UserTransactionManager&quot; init-method=&quot;init&quot; destroy-method=&quot;close&quot;&amp;gt;
  &amp;lt;property name=&quot;forceShutdown&quot; value=&quot;false&quot; /&amp;gt;
 &amp;lt;/bean&amp;gt;

 &amp;lt;bean id=&quot;userTransaction&quot; class=&quot;com.atomikos.icatch.jta.UserTransactionImp&quot;&amp;gt;
  &amp;lt;property name=&quot;transactionTimeout&quot; value=&quot;120&quot; /&amp;gt;
 &amp;lt;/bean&amp;gt;
 
 &amp;lt;osgi:service interface=&quot;org.springframework.transaction.PlatformTransactionManager&quot; ref=&quot;jtaTxMgr&quot;&amp;gt;
  &amp;lt;osgi:service-properties&amp;gt;
   &amp;lt;entry key=&quot;transaction.manager.name&quot; value=&quot;atomikos&quot; /&amp;gt;
  &amp;lt;/osgi:service-properties&amp;gt;
 &amp;lt;/osgi:service&amp;gt;
&amp;lt;/beans&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
Контекст модуля ds определяет источник данных OracleXADataSource на примере БД Oracle , с использованием&amp;nbsp;AtomikosDataSourceBean, который поддерживает XA источники данных. Он также предоставляет пул соединений. OracleXADataSource несмотря на название не является пулом, и при работе каждый раз поднимает соединение с базой. Не знаю почему во многих примерах его используют напрямую. При большой нагрузке его использование будет &quot;безбожно жрать коннекты&quot;. Сконфигурированный источник данных выставляется как osgi-сервис. Обратите внимание что интерфейс для сервиса определяется как&amp;nbsp;javax.sql.DataSource, что позволит в дальнейшем использовать удобный JdbcTemplate.
&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-xml&quot;&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&amp;gt;
&amp;lt;beans xmlns=&quot;http://www.springframework.org/schema/beans&quot;
 xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot; xmlns:osgi=&quot;http://www.springframework.org/schema/osgi&quot;
 xsi:schemaLocation=&quot;
       http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/osgi http://www.springframework.org/schema/osgi/spring-osgi.xsd
       http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd&quot;&amp;gt;

 &amp;lt;bean id=&quot;atomikosXaDataSourceBean&quot; class=&quot;com.atomikos.jdbc.AtomikosDataSourceBean&quot;&amp;gt;
  &amp;lt;property name=&quot;uniqueResourceName&quot; value=&quot;oracleXA&quot; /&amp;gt;
  &amp;lt;property name=&quot;testQuery&quot; value=&quot;select * from dual&quot; /&amp;gt;
  &amp;lt;property name=&quot;xaDataSource&quot; ref=&quot;dataSourceXA&quot; /&amp;gt;
  &amp;lt;property name=&quot;minPoolSize&quot; value=&quot;1&quot; /&amp;gt;
  &amp;lt;property name=&quot;maxPoolSize&quot; value=&quot;5&quot; /&amp;gt;
  &amp;lt;property name=&quot;reapTimeout&quot; value=&quot;0&quot; /&amp;gt;
 &amp;lt;/bean&amp;gt;

 &amp;lt;bean id=&quot;dataSourceXA&quot; class=&quot;oracle.jdbc.xa.client.OracleXADataSource&quot;&amp;gt;
  &amp;lt;property name=&quot;URL&quot; value=&quot;jdbc:oracle:thin:@host:1521:sid&quot; /&amp;gt;
  &amp;lt;property name=&quot;user&quot; value=&quot;123&quot; /&amp;gt;
  &amp;lt;property name=&quot;password&quot; value=&quot;123&quot; /&amp;gt;
  &amp;lt;property name=&quot;connectionCachingEnabled&quot; value=&quot;false&quot; /&amp;gt;
 &amp;lt;/bean&amp;gt;

 &amp;lt;osgi:service interface=&quot;javax.sql.DataSource&quot; ref=&quot;atomikosXaDataSourceBean&quot;&amp;gt;
  &amp;lt;osgi:service-properties&amp;gt;
   &amp;lt;entry key=&quot;osgi.jndi.service.name&quot; value=&quot;jdbc/oracleXA&quot; /&amp;gt;
   &amp;lt;entry key=&quot;datasource.name&quot; value=&quot;oracleXA&quot; /&amp;gt;
  &amp;lt;/osgi:service-properties&amp;gt;
 &amp;lt;/osgi:service&amp;gt;
&amp;lt;/beans&amp;gt;&lt;/code&gt;&lt;/pre&gt;
Аналогичным образом определяем нужные бины для работы с ActiveMQ с помощью AtomikosConnectionFactoryBean&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;&lt;code class=&quot;language-xml&quot;&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&amp;gt;
&amp;lt;beans xmlns=&quot;http://www.springframework.org/schema/beans&quot;
       xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;
       xmlns:osgi=&quot;http://www.springframework.org/schema/osgi&quot;
       xsi:schemaLocation=&quot;
       http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/osgi http://www.springframework.org/schema/osgi/spring-osgi.xsd
       http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd&quot;&amp;gt;

  &amp;lt;bean id=&quot;atomikosXaConnectionFactoryBean&quot; class=&quot;com.atomikos.jms.AtomikosConnectionFactoryBean&quot;
  init-method=&quot;init&quot; destroy-method=&quot;close&quot;&amp;gt;
  &amp;lt;property name=&quot;uniqueResourceName&quot; value=&quot;amq1&quot; /&amp;gt;
  &amp;lt;property name=&quot;xaConnectionFactory&quot; ref=&quot;jmsXaConnectionFactory&quot; /&amp;gt;
  &amp;lt;property name=&quot;localTransactionMode&quot; value=&quot;false&quot; /&amp;gt;
  &amp;lt;property name=&quot;minPoolSize&quot; value=&quot;1&quot; /&amp;gt;
  &amp;lt;property name=&quot;maxPoolSize&quot; value=&quot;5&quot; /&amp;gt;
  &amp;lt;property name=&quot;reapTimeout&quot; value=&quot;0&quot; /&amp;gt;
 &amp;lt;/bean&amp;gt;

 &amp;lt;bean id=&quot;jmsXaConnectionFactory&quot; class=&quot;org.apache.activemq.ActiveMQXAConnectionFactory&quot;&amp;gt;
  &amp;lt;property name=&quot;brokerURL&quot; value=&quot;failover:(tcp://localhost:61616)&quot; /&amp;gt;
  &amp;lt;property name=&quot;redeliveryPolicy&quot;&amp;gt;
   &amp;lt;bean class=&quot;org.apache.activemq.RedeliveryPolicy&quot;&amp;gt;
    &amp;lt;property name=&quot;maximumRedeliveries&quot; value=&quot;1&quot; /&amp;gt;
   &amp;lt;/bean&amp;gt;
  &amp;lt;/property&amp;gt;
  &amp;lt;property name=&quot;userName&quot; value=&quot;smx&quot; /&amp;gt;
  &amp;lt;property name=&quot;password&quot; value=&quot;smx&quot; /&amp;gt;
 &amp;lt;/bean&amp;gt;
 
 &amp;lt;osgi:service interface=&quot;javax.jms.ConnectionFactory&quot; ref=&quot;atomikosXaConnectionFactoryBean&quot;&amp;gt;
  &amp;lt;osgi:service-properties&amp;gt;
   &amp;lt;entry key=&quot;osgi.jndi.service.name&quot; value=&quot;jms/connectionFactory&quot; /&amp;gt;
   &amp;lt;entry key=&quot;connection.factory.name&quot; value=&quot;activemqXA&quot; /&amp;gt;
  &amp;lt;/osgi:service-properties&amp;gt;
 &amp;lt;/osgi:service&amp;gt;
&amp;lt;/beans&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
Пример демонстрирующий использование транзакций разделен на два контекста.&lt;br /&gt;
system-context.xml получающий по ссылке нужные системные артефакты для работы с транзакциями и camel-context.xml содержащий бизнес логику&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;&lt;code class=&quot;language-xml&quot;&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&amp;gt;
&amp;lt;beans xmlns=&quot;http://www.springframework.org/schema/beans&quot;
 xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot; xmlns:osgi=&quot;http://www.springframework.org/schema/osgi&quot;
 xsi:schemaLocation=&quot;
       http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/osgi http://www.springframework.org/schema/osgi/spring-osgi.xsd
       http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd&quot;&amp;gt;

 &amp;lt;osgi:reference id=&quot;jtaTxMgr&quot; interface=&quot;org.springframework.transaction.PlatformTransactionManager&quot; filter=&quot;(transaction.manager.name=atomikos)&quot;/&amp;gt;
 &amp;lt;osgi:reference id=&quot;atomikosXaDataSourceBean&quot; interface=&quot;javax.sql.DataSource&quot; filter=&quot;(datasource.name=oracleXA)&quot;/&amp;gt;
 &amp;lt;osgi:reference id=&quot;atomikosXaConnectionFactoryBean&quot; interface=&quot;javax.jms.ConnectionFactory&quot; filter=&quot;(connection.factory.name=activemqXA)&quot;/&amp;gt;
 
 &amp;lt;bean id=&quot;REQUIRED&quot; class=&quot;org.apache.camel.spring.spi.SpringTransactionPolicy&quot;&amp;gt;
  &amp;lt;property name=&quot;transactionManager&quot; ref=&quot;jtaTxMgr&quot; /&amp;gt;
  &amp;lt;property name=&quot;propagationBehaviorName&quot; value=&quot;PROPAGATION_REQUIRED&quot; /&amp;gt;
 &amp;lt;/bean&amp;gt;

 &amp;lt;bean id=&quot;jmsConfig&quot; class=&quot;org.apache.camel.component.jms.JmsConfiguration&quot;&amp;gt;
  &amp;lt;property name=&quot;connectionFactory&quot; ref=&quot;atomikosXaConnectionFactoryBean&quot; /&amp;gt;
  &amp;lt;property name=&quot;transacted&quot; value=&quot;true&quot; /&amp;gt;
  &amp;lt;property name=&quot;transactionManager&quot; ref=&quot;jtaTxMgr&quot; /&amp;gt;
  &amp;lt;property name=&quot;maxConcurrentConsumers&quot; value=&quot;1&quot; /&amp;gt;
  &amp;lt;property name=&quot;cacheLevelName&quot; value=&quot;CACHE_NONE&quot; /&amp;gt;
 &amp;lt;/bean&amp;gt;

 &amp;lt;bean id=&quot;activemqXA&quot; class=&quot;org.apache.activemq.camel.component.ActiveMQComponent&quot;&amp;gt;
  &amp;lt;property name=&quot;configuration&quot; ref=&quot;jmsConfig&quot; /&amp;gt;
 &amp;lt;/bean&amp;gt;
 
 &amp;lt;bean id=&quot;jdbcTemplate&quot; class=&quot;org.springframework.jdbc.core.JdbcTemplate&quot;&amp;gt;
        &amp;lt;property name=&quot;dataSource&quot; ref=&quot;atomikosXaDataSourceBean&quot;/&amp;gt;
    &amp;lt;/bean&amp;gt;
&amp;lt;/beans&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
Бизнес логика следующая:&lt;br /&gt;
&lt;ol style=&quot;text-align: left;&quot;&gt;
&lt;li&gt;Получить jms сообщение&lt;/li&gt;
&lt;li&gt;Выгрузить сообщение в промежуточную очередь&lt;/li&gt;
&lt;li&gt;Выполнить обновление БД (успешное)&lt;/li&gt;
&lt;li&gt;Выполнить обновление БД приводящее к исключению&lt;/li&gt;
&lt;li&gt;Выгрузить сообщение в итоговую очередь&lt;/li&gt;
&lt;/ol&gt;
Как вы можете убедиться, запустив этот пример, что сообщение в результате обработки данным роутом будет выгружено в очередь ошибок, при этом ни в одном промежуточном ресурсе данные не сохранятся. Это то что там надо.&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;&lt;code class=&quot;language-xml&quot;&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&amp;gt;
&amp;lt;beans xmlns=&quot;http://www.springframework.org/schema/beans&quot;
 xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot; xmlns:osgi=&quot;http://www.springframework.org/schema/osgi&quot;
 xsi:schemaLocation=&quot;
       http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/osgi http://www.springframework.org/schema/osgi/spring-osgi.xsd
       http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd&quot;&amp;gt;
 
 &amp;lt;bean id=&quot;hello&quot; class=&quot;com.bssys.nio2.testXA.HelloBean&quot;&amp;gt;
  &amp;lt;constructor-arg ref=&quot;jdbcTemplate&quot;/&amp;gt;
 &amp;lt;/bean&amp;gt;

 &amp;lt;camelContext xmlns=&quot;http://camel.apache.org/schema/spring&quot;&amp;gt;
  &amp;lt;route&amp;gt;
   &amp;lt;from uri=&quot;activemqXA:queue:SQL_IN&quot; /&amp;gt;
   &amp;lt;transacted  ref=&quot;REQUIRED&quot;/&amp;gt;
   &amp;lt;to uri=&quot;activemqXA:queue:SQL_MIDLE&quot; /&amp;gt;
   &amp;lt;bean ref=&quot;hello&quot; method=&quot;goodlogic&quot;/&amp;gt;
   &amp;lt;log message=&quot;REDELIVERED: ${header.JMSRedelivered}&quot; /&amp;gt;
   &amp;lt;bean ref=&quot;hello&quot; method=&quot;badlogic&quot;/&amp;gt;
   &amp;lt;to uri=&quot;activemqXA:queue:SQL_OUT&quot; /&amp;gt;
  &amp;lt;/route&amp;gt;
 &amp;lt;/camelContext&amp;gt;

&amp;lt;/beans&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
Чтобы сообщение выгружалось в очередь DLQ надо настроить немного ActiveMQ прописав в activemq.xml следующее
&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;&lt;code class=&quot;language-xml&quot;&gt;&amp;lt;policyEntry queue=&quot;&amp;gt;&quot;&amp;gt;
          &amp;lt;deadLetterStrategy&amp;gt;
            &amp;lt;!--
              Use the prefix &#39;DLQ.&#39; for the destination name, and make
              the DLQ a queue rather than a topic
            --&amp;gt;
            &amp;lt;individualDeadLetterStrategy
              queuePrefix=&quot;DLQ.&quot; useQueueForQueueMessages=&quot;true&quot; /&amp;gt;
          &amp;lt;/deadLetterStrategy&amp;gt;
 &amp;lt;/policyEntry&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;/pre&gt;
Таким образом для каждом очереди будет создаваться своя очередь DLQ. Очень удобно в дальнейшем разгребать конфликты. Ну и зависимости для Servicemix&#39;а для того чтобы модули взлетели. Предварительно надо соответствующим образом установить драйвер для Oracle, которого нет в центральном репозитории Maven. Будут вопросы пишите в коментариях, я отвечу.&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;&lt;code class=&quot;language-xml&quot;&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&amp;gt;
&amp;lt;features xmlns=&quot;http://karaf.apache.org/xmlns/features/v1.0.0&quot;
 name=&quot;egg&quot;&amp;gt;
 &amp;lt;feature name=&quot;egg-features&quot; version=&quot;1.0&quot;&amp;gt;
  &amp;lt;feature version=&quot;3.2.14.RELEASE_1&quot;&amp;gt;spring-jdbc&amp;lt;/feature&amp;gt;
  &amp;lt;feature&amp;gt;camel-sql&amp;lt;/feature&amp;gt;
  &amp;lt;feature&amp;gt;camel-jdbc&amp;lt;/feature&amp;gt;
  &amp;lt;feature&amp;gt;jndi&amp;lt;/feature&amp;gt;
  &amp;lt;feature&amp;gt;war&amp;lt;/feature&amp;gt;
  &amp;lt;feature&amp;gt;activemq&amp;lt;/feature&amp;gt;
  &amp;lt;feature&amp;gt;activemq-web-console&amp;lt;/feature&amp;gt;
  &amp;lt;feature&amp;gt;activemq-broker&amp;lt;/feature&amp;gt;
  &amp;lt;feature&amp;gt;transaction&amp;lt;/feature&amp;gt;
  &amp;lt;bundle&amp;gt;wrap:mvn:com.oracle/ojdbc6/12.1.0.2.0&amp;lt;/bundle&amp;gt;
  &amp;lt;bundle&amp;gt;wrap:mvn:commons-dbcp/commons-dbcp/1.4&amp;lt;/bundle&amp;gt;
  &amp;lt;bundle&amp;gt;mvn:com.atomikos/transactions-osgi/4.0.2&amp;lt;/bundle&amp;gt;
 &amp;lt;/feature&amp;gt;
&amp;lt;/features&amp;gt;&lt;/code&gt;&lt;/pre&gt;

Исходники на GitHib https://github.com/Hibernate2009/servicemix-atomikos-xa-jms-jdbc.git

&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jser.blogspot.com/feeds/4785972176685661193/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/37848572/4785972176685661193' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/37848572/posts/default/4785972176685661193'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/37848572/posts/default/4785972176685661193'/><link rel='alternate' type='text/html' href='http://jser.blogspot.com/2016/04/servicemix-atomikos-xa-transaction-jdbc.html' title='ServiceMix Atomikos XA transaction JDBC JMS'/><author><name>JSer</name><uri>http://www.blogger.com/profile/17455716060338444232</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-37848572.post-197629666934593843</id><published>2016-03-11T22:25:00.002+09:00</published><updated>2016-03-17T00:15:42.150+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="java"/><category scheme="http://www.blogger.com/atom/ns#" term="JSR 356"/><category scheme="http://www.blogger.com/atom/ns#" term="WebSocket"/><title type='text'>WebSocket server java. Best way</title><content type='html'>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
Итак, когда мы разобрались как реализовать WebSocket server с использованием NIO2.0. Я покажу лучший способ это сделать используя&amp;nbsp;JSR 356, Java API for WebSocket. Обратите внимание насколько все просто. Достаточно обвесить обычный POJO следующими анатациями и упаковать как веб-приложение.&lt;br /&gt;
&lt;ul style=&quot;text-align: left;&quot;&gt;
&lt;li&gt;@ServerEndpoint&lt;/li&gt;
&lt;li&gt;@OnOpen&lt;/li&gt;
&lt;li&gt;@OnMessage&lt;/li&gt;
&lt;li&gt;@OnClose&lt;/li&gt;
&lt;li&gt;@OnError&lt;/li&gt;
&lt;/ul&gt;
Все! Это настолько просто, что даже не требует пояснений.&lt;br /&gt;
Код как обычно на https://github.com/Hibernate2009/websocket-jsr356.git&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;package com.bssys.ws;

import java.util.logging.Logger;

import javax.websocket.CloseReason;
import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;


@ServerEndpoint(&quot;/echo&quot;)
public class WsChatServlet {
 private Logger logger = Logger.getLogger(this.getClass().getName());
  
    @OnOpen
    public void onOpen(Session session) {
        logger.info(&quot;Connected ... &quot; + session.getId());
        SessionManager.getInstance().addSession(session);
    }
 
    @OnMessage
    public void onMessage(Session session, String message ) {
     logger.info(String.format(&quot;Client %s send message %s&quot;, session.getId(), message));
     SessionManager.getInstance().writeMessageToClients(session, message);
    }
 
    @OnClose
    public void onClose(Session session, CloseReason closeReason) {
        logger.info(String.format(&quot;Session %s closed because of %s&quot;, session.getId(), closeReason));
        SessionManager.getInstance().removeSession(session);
    }
    
    @OnError
    public void onError(Session session, Throwable throwable){
     logger.info(String.format(&quot;Session %s error &quot;, session.getId()));
     SessionManager.getInstance().removeSession(session);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;
&lt;/code&gt;&lt;/pre&gt;
Вспомогательный класс для управления подключениями
&lt;br /&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;package com.bssys.ws;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import javax.websocket.Session;

public class SessionManager {

 private final List&amp;lt;Session&amp;gt; sessions = Collections.synchronizedList(new ArrayList&amp;lt;Session&amp;gt;());

 private static SessionManager instance;

 private SessionManager() {
 }

 public static SessionManager getInstance() {
  if (instance == null) {
   instance = new SessionManager();
  }
  return instance;
 }

 public void addSession(Session session) {
  sessions.add(session);
 }

 public void removeSession(Session session) {
  sessions.remove(session);
 }

 public void writeMessageToClients(Session session, String message) {
  synchronized (session) {
   for (Session clientSession : sessions) {
    if (session != clientSession) {
     try {
      clientSession.getBasicRemote().sendText(message);
     } catch (IOException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
     }
    }
   }
  }
 }
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
</content><link rel='replies' type='application/atom+xml' href='http://jser.blogspot.com/feeds/197629666934593843/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/37848572/197629666934593843' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/37848572/posts/default/197629666934593843'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/37848572/posts/default/197629666934593843'/><link rel='alternate' type='text/html' href='http://jser.blogspot.com/2016/03/websocket-server-java-best-way.html' title='WebSocket server java. Best way'/><author><name>JSer</name><uri>http://www.blogger.com/profile/17455716060338444232</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-37848572.post-6683524215418218116</id><published>2016-03-11T00:09:00.002+09:00</published><updated>2016-03-17T00:16:03.051+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Asynchronous I/O"/><category scheme="http://www.blogger.com/atom/ns#" term="AsynchronousServerSocketChannel"/><category scheme="http://www.blogger.com/atom/ns#" term="AsynchronousSocketChannel"/><category scheme="http://www.blogger.com/atom/ns#" term="java"/><category scheme="http://www.blogger.com/atom/ns#" term="NIO2"/><category scheme="http://www.blogger.com/atom/ns#" term="WebSocket"/><title type='text'>NIO2.0 Asynchronous I/O. WebSocket chat server</title><content type='html'>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
Для лучшего понимания NIO2.0 есть очень подходящая задача. А именно попробуем реализовать WebSocket chat server. В качестве клиента будет выступать браузер. Если кратко с помощью WebSocket можно создать двунаправленый канал связи, в частности между браузером и сервером. Клиент в общем может быть любым, главное чтобы он поддерживал нужный протокол.&lt;br /&gt;
&lt;br /&gt;
При установлении соединения браузер и сервер обмениваются так называемым рукопожатием.&lt;br /&gt;
По сути клиент посылает обычный GET запрос примерного вида&lt;br /&gt;
&lt;br /&gt;
&lt;pre style=&quot;background: rgb(244, 244, 244); border: 0px; color: #373737; font-family: &#39;Courier 10 Pitch&#39;, Courier, monospace; font-size: 13px; font-stretch: normal; line-height: 1.5; margin-bottom: 1.625em; outline: 0px; overflow: auto; padding: 0.75em 1.625em; vertical-align: baseline;&quot;&gt;GET / HTTP/1.1
Upgrade: websocket
Connection: Upgrade
Host: 127.0.0.1
Origin: null
Sec-WebSocket-Key: tR+4GeaXR7PuLsQtvlsMTw==
Sec-WebSocket-Version: 13
Sec-WebSocket-Extensions: x-webkit-deflate-frame&lt;/pre&gt;
Если сервер поддерживает websocket ответит следующим образом&lt;br /&gt;
&lt;pre style=&quot;background: rgb(244, 244, 244); border: 0px; color: #373737; font-family: &#39;Courier 10 Pitch&#39;, Courier, monospace; font-size: 13px; font-stretch: normal; line-height: 1.5; margin-bottom: 1.625em; outline: 0px; overflow: auto; padding: 0.75em 1.625em; vertical-align: baseline;&quot;&gt;HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept:&lt;strong style=&quot;border: 0px; font-family: inherit; font-style: inherit; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline;&quot;&gt; ELY6l2B3+S/6vjfrkzFMurz/hhQ=&lt;/strong&gt;&lt;/pre&gt;
Браузер при этом оставляет открытым соединение. Что позволяет отправлять и получать данные в любое время.&lt;br /&gt;
Важную часть при установлении рукопожатия составляет обмен ключами. Особой магии тут нет.&lt;br /&gt;
&lt;br /&gt;
&lt;ul style=&quot;text-align: left;&quot;&gt;
&lt;li&gt;Клиент посылает ключ в заголовке Sec-WebSocket-Key закодированный в base64.&lt;/li&gt;
&lt;li&gt;Серверу нужно взять этот ключ добавить &quot;магическую строку&quot; 258EAFA5-E914-47DA-95CA-C5AB0DC85B11 и построить по итоговой строке SHA-1 хеш, и передать его в заголовке ответа Sec-WebSocket-Accept&lt;/li&gt;
&lt;/ul&gt;
&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
После этого сервер и клиент могут обмениваться сообщениями в специфическом формате, так называемыми фреймами.&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;background-color: #f8f8f8; color: #333333; font-family: &amp;quot;menlo&amp;quot; , &amp;quot;monaco&amp;quot; , &amp;quot;courier new&amp;quot; , monospace; font-size: 12px; line-height: 22.4px; white-space: pre-wrap;&quot;&gt;      0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
     +-+-+-+-+-------+-+-------------+-------------------------------+
     |&lt;/span&gt;&lt;span class=&quot;hljs-string&quot; style=&quot;border: 0px; color: #dd1144; font-family: &amp;quot;menlo&amp;quot; , &amp;quot;monaco&amp;quot; , &amp;quot;courier new&amp;quot; , monospace; font-size: 12px; line-height: 22.4px; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;F&lt;/span&gt;&lt;span style=&quot;background-color: #f8f8f8; color: #333333; font-family: &amp;quot;menlo&amp;quot; , &amp;quot;monaco&amp;quot; , &amp;quot;courier new&amp;quot; , monospace; font-size: 12px; line-height: 22.4px; white-space: pre-wrap;&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;hljs-string&quot; style=&quot;border: 0px; color: #dd1144; font-family: &amp;quot;menlo&amp;quot; , &amp;quot;monaco&amp;quot; , &amp;quot;courier new&amp;quot; , monospace; font-size: 12px; line-height: 22.4px; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;R&lt;/span&gt;&lt;span style=&quot;background-color: #f8f8f8; color: #333333; font-family: &amp;quot;menlo&amp;quot; , &amp;quot;monaco&amp;quot; , &amp;quot;courier new&amp;quot; , monospace; font-size: 12px; line-height: 22.4px; white-space: pre-wrap;&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;hljs-string&quot; style=&quot;border: 0px; color: #dd1144; font-family: &amp;quot;menlo&amp;quot; , &amp;quot;monaco&amp;quot; , &amp;quot;courier new&amp;quot; , monospace; font-size: 12px; line-height: 22.4px; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;R&lt;/span&gt;&lt;span style=&quot;background-color: #f8f8f8; color: #333333; font-family: &amp;quot;menlo&amp;quot; , &amp;quot;monaco&amp;quot; , &amp;quot;courier new&amp;quot; , monospace; font-size: 12px; line-height: 22.4px; white-space: pre-wrap;&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;hljs-string&quot; style=&quot;border: 0px; color: #dd1144; font-family: &amp;quot;menlo&amp;quot; , &amp;quot;monaco&amp;quot; , &amp;quot;courier new&amp;quot; , monospace; font-size: 12px; line-height: 22.4px; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;R&lt;/span&gt;&lt;span style=&quot;background-color: #f8f8f8; color: #333333; font-family: &amp;quot;menlo&amp;quot; , &amp;quot;monaco&amp;quot; , &amp;quot;courier new&amp;quot; , monospace; font-size: 12px; line-height: 22.4px; white-space: pre-wrap;&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;hljs-string&quot; style=&quot;border: 0px; color: #dd1144; font-family: &amp;quot;menlo&amp;quot; , &amp;quot;monaco&amp;quot; , &amp;quot;courier new&amp;quot; , monospace; font-size: 12px; line-height: 22.4px; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline; white-space: pre-wrap;&quot;&gt; opcode&lt;/span&gt;&lt;span style=&quot;background-color: #f8f8f8; color: #333333; font-family: &amp;quot;menlo&amp;quot; , &amp;quot;monaco&amp;quot; , &amp;quot;courier new&amp;quot; , monospace; font-size: 12px; line-height: 22.4px; white-space: pre-wrap;&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;hljs-string&quot; style=&quot;border: 0px; color: #dd1144; font-family: &amp;quot;menlo&amp;quot; , &amp;quot;monaco&amp;quot; , &amp;quot;courier new&amp;quot; , monospace; font-size: 12px; line-height: 22.4px; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;M&lt;/span&gt;&lt;span style=&quot;background-color: #f8f8f8; color: #333333; font-family: &amp;quot;menlo&amp;quot; , &amp;quot;monaco&amp;quot; , &amp;quot;courier new&amp;quot; , monospace; font-size: 12px; line-height: 22.4px; white-space: pre-wrap;&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;hljs-string&quot; style=&quot;border: 0px; color: #dd1144; font-family: &amp;quot;menlo&amp;quot; , &amp;quot;monaco&amp;quot; , &amp;quot;courier new&amp;quot; , monospace; font-size: 12px; line-height: 22.4px; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline; white-space: pre-wrap;&quot;&gt; Payload len &lt;/span&gt;&lt;span style=&quot;background-color: #f8f8f8; color: #333333; font-family: &amp;quot;menlo&amp;quot; , &amp;quot;monaco&amp;quot; , &amp;quot;courier new&amp;quot; , monospace; font-size: 12px; line-height: 22.4px; white-space: pre-wrap;&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;hljs-string&quot; style=&quot;border: 0px; color: #dd1144; font-family: &amp;quot;menlo&amp;quot; , &amp;quot;monaco&amp;quot; , &amp;quot;courier new&amp;quot; , monospace; font-size: 12px; line-height: 22.4px; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;    Extended payload length    &lt;/span&gt;&lt;span style=&quot;background-color: #f8f8f8; color: #333333; font-family: &amp;quot;menlo&amp;quot; , &amp;quot;monaco&amp;quot; , &amp;quot;courier new&amp;quot; , monospace; font-size: 12px; line-height: 22.4px; white-space: pre-wrap;&quot;&gt;|
     |&lt;/span&gt;&lt;span class=&quot;hljs-string&quot; style=&quot;border: 0px; color: #dd1144; font-family: &amp;quot;menlo&amp;quot; , &amp;quot;monaco&amp;quot; , &amp;quot;courier new&amp;quot; , monospace; font-size: 12px; line-height: 22.4px; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;I&lt;/span&gt;&lt;span style=&quot;background-color: #f8f8f8; color: #333333; font-family: &amp;quot;menlo&amp;quot; , &amp;quot;monaco&amp;quot; , &amp;quot;courier new&amp;quot; , monospace; font-size: 12px; line-height: 22.4px; white-space: pre-wrap;&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;hljs-string&quot; style=&quot;border: 0px; color: #dd1144; font-family: &amp;quot;menlo&amp;quot; , &amp;quot;monaco&amp;quot; , &amp;quot;courier new&amp;quot; , monospace; font-size: 12px; line-height: 22.4px; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;S&lt;/span&gt;&lt;span style=&quot;background-color: #f8f8f8; color: #333333; font-family: &amp;quot;menlo&amp;quot; , &amp;quot;monaco&amp;quot; , &amp;quot;courier new&amp;quot; , monospace; font-size: 12px; line-height: 22.4px; white-space: pre-wrap;&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;hljs-string&quot; style=&quot;border: 0px; color: #dd1144; font-family: &amp;quot;menlo&amp;quot; , &amp;quot;monaco&amp;quot; , &amp;quot;courier new&amp;quot; , monospace; font-size: 12px; line-height: 22.4px; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;S&lt;/span&gt;&lt;span style=&quot;background-color: #f8f8f8; color: #333333; font-family: &amp;quot;menlo&amp;quot; , &amp;quot;monaco&amp;quot; , &amp;quot;courier new&amp;quot; , monospace; font-size: 12px; line-height: 22.4px; white-space: pre-wrap;&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;hljs-string&quot; style=&quot;border: 0px; color: #dd1144; font-family: &amp;quot;menlo&amp;quot; , &amp;quot;monaco&amp;quot; , &amp;quot;courier new&amp;quot; , monospace; font-size: 12px; line-height: 22.4px; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;S&lt;/span&gt;&lt;span style=&quot;background-color: #f8f8f8; color: #333333; font-family: &amp;quot;menlo&amp;quot; , &amp;quot;monaco&amp;quot; , &amp;quot;courier new&amp;quot; , monospace; font-size: 12px; line-height: 22.4px; white-space: pre-wrap;&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;hljs-string&quot; style=&quot;border: 0px; color: #dd1144; font-family: &amp;quot;menlo&amp;quot; , &amp;quot;monaco&amp;quot; , &amp;quot;courier new&amp;quot; , monospace; font-size: 12px; line-height: 22.4px; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;  (4)  &lt;/span&gt;&lt;span style=&quot;background-color: #f8f8f8; color: #333333; font-family: &amp;quot;menlo&amp;quot; , &amp;quot;monaco&amp;quot; , &amp;quot;courier new&amp;quot; , monospace; font-size: 12px; line-height: 22.4px; white-space: pre-wrap;&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;hljs-string&quot; style=&quot;border: 0px; color: #dd1144; font-family: &amp;quot;menlo&amp;quot; , &amp;quot;monaco&amp;quot; , &amp;quot;courier new&amp;quot; , monospace; font-size: 12px; line-height: 22.4px; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;A&lt;/span&gt;&lt;span style=&quot;background-color: #f8f8f8; color: #333333; font-family: &amp;quot;menlo&amp;quot; , &amp;quot;monaco&amp;quot; , &amp;quot;courier new&amp;quot; , monospace; font-size: 12px; line-height: 22.4px; white-space: pre-wrap;&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;hljs-string&quot; style=&quot;border: 0px; color: #dd1144; font-family: &amp;quot;menlo&amp;quot; , &amp;quot;monaco&amp;quot; , &amp;quot;courier new&amp;quot; , monospace; font-size: 12px; line-height: 22.4px; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;     (7)     &lt;/span&gt;&lt;span style=&quot;background-color: #f8f8f8; color: #333333; font-family: &amp;quot;menlo&amp;quot; , &amp;quot;monaco&amp;quot; , &amp;quot;courier new&amp;quot; , monospace; font-size: 12px; line-height: 22.4px; white-space: pre-wrap;&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;hljs-string&quot; style=&quot;border: 0px; color: #dd1144; font-family: &amp;quot;menlo&amp;quot; , &amp;quot;monaco&amp;quot; , &amp;quot;courier new&amp;quot; , monospace; font-size: 12px; line-height: 22.4px; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;             (16/64)           &lt;/span&gt;&lt;span style=&quot;background-color: #f8f8f8; color: #333333; font-family: &amp;quot;menlo&amp;quot; , &amp;quot;monaco&amp;quot; , &amp;quot;courier new&amp;quot; , monospace; font-size: 12px; line-height: 22.4px; white-space: pre-wrap;&quot;&gt;|
     |&lt;/span&gt;&lt;span class=&quot;hljs-string&quot; style=&quot;border: 0px; color: #dd1144; font-family: &amp;quot;menlo&amp;quot; , &amp;quot;monaco&amp;quot; , &amp;quot;courier new&amp;quot; , monospace; font-size: 12px; line-height: 22.4px; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;N&lt;/span&gt;&lt;span style=&quot;background-color: #f8f8f8; color: #333333; font-family: &amp;quot;menlo&amp;quot; , &amp;quot;monaco&amp;quot; , &amp;quot;courier new&amp;quot; , monospace; font-size: 12px; line-height: 22.4px; white-space: pre-wrap;&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;hljs-string&quot; style=&quot;border: 0px; color: #dd1144; font-family: &amp;quot;menlo&amp;quot; , &amp;quot;monaco&amp;quot; , &amp;quot;courier new&amp;quot; , monospace; font-size: 12px; line-height: 22.4px; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;V&lt;/span&gt;&lt;span style=&quot;background-color: #f8f8f8; color: #333333; font-family: &amp;quot;menlo&amp;quot; , &amp;quot;monaco&amp;quot; , &amp;quot;courier new&amp;quot; , monospace; font-size: 12px; line-height: 22.4px; white-space: pre-wrap;&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;hljs-string&quot; style=&quot;border: 0px; color: #dd1144; font-family: &amp;quot;menlo&amp;quot; , &amp;quot;monaco&amp;quot; , &amp;quot;courier new&amp;quot; , monospace; font-size: 12px; line-height: 22.4px; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;V&lt;/span&gt;&lt;span style=&quot;background-color: #f8f8f8; color: #333333; font-family: &amp;quot;menlo&amp;quot; , &amp;quot;monaco&amp;quot; , &amp;quot;courier new&amp;quot; , monospace; font-size: 12px; line-height: 22.4px; white-space: pre-wrap;&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;hljs-string&quot; style=&quot;border: 0px; color: #dd1144; font-family: &amp;quot;menlo&amp;quot; , &amp;quot;monaco&amp;quot; , &amp;quot;courier new&amp;quot; , monospace; font-size: 12px; line-height: 22.4px; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;V&lt;/span&gt;&lt;span style=&quot;background-color: #f8f8f8; color: #333333; font-family: &amp;quot;menlo&amp;quot; , &amp;quot;monaco&amp;quot; , &amp;quot;courier new&amp;quot; , monospace; font-size: 12px; line-height: 22.4px; white-space: pre-wrap;&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;hljs-string&quot; style=&quot;border: 0px; color: #dd1144; font-family: &amp;quot;menlo&amp;quot; , &amp;quot;monaco&amp;quot; , &amp;quot;courier new&amp;quot; , monospace; font-size: 12px; line-height: 22.4px; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;       &lt;/span&gt;&lt;span style=&quot;background-color: #f8f8f8; color: #333333; font-family: &amp;quot;menlo&amp;quot; , &amp;quot;monaco&amp;quot; , &amp;quot;courier new&amp;quot; , monospace; font-size: 12px; line-height: 22.4px; white-space: pre-wrap;&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;hljs-string&quot; style=&quot;border: 0px; color: #dd1144; font-family: &amp;quot;menlo&amp;quot; , &amp;quot;monaco&amp;quot; , &amp;quot;courier new&amp;quot; , monospace; font-size: 12px; line-height: 22.4px; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;S&lt;/span&gt;&lt;span style=&quot;background-color: #f8f8f8; color: #333333; font-family: &amp;quot;menlo&amp;quot; , &amp;quot;monaco&amp;quot; , &amp;quot;courier new&amp;quot; , monospace; font-size: 12px; line-height: 22.4px; white-space: pre-wrap;&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;hljs-string&quot; style=&quot;border: 0px; color: #dd1144; font-family: &amp;quot;menlo&amp;quot; , &amp;quot;monaco&amp;quot; , &amp;quot;courier new&amp;quot; , monospace; font-size: 12px; line-height: 22.4px; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;             &lt;/span&gt;&lt;span style=&quot;background-color: #f8f8f8; color: #333333; font-family: &amp;quot;menlo&amp;quot; , &amp;quot;monaco&amp;quot; , &amp;quot;courier new&amp;quot; , monospace; font-size: 12px; line-height: 22.4px; white-space: pre-wrap;&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;hljs-string&quot; style=&quot;border: 0px; color: #dd1144; font-family: &amp;quot;menlo&amp;quot; , &amp;quot;monaco&amp;quot; , &amp;quot;courier new&amp;quot; , monospace; font-size: 12px; line-height: 22.4px; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;   (if payload len==126/127)   &lt;/span&gt;&lt;span style=&quot;background-color: #f8f8f8; color: #333333; font-family: &amp;quot;menlo&amp;quot; , &amp;quot;monaco&amp;quot; , &amp;quot;courier new&amp;quot; , monospace; font-size: 12px; line-height: 22.4px; white-space: pre-wrap;&quot;&gt;|
     |&lt;/span&gt;&lt;span class=&quot;hljs-string&quot; style=&quot;border: 0px; color: #dd1144; font-family: &amp;quot;menlo&amp;quot; , &amp;quot;monaco&amp;quot; , &amp;quot;courier new&amp;quot; , monospace; font-size: 12px; line-height: 22.4px; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline; white-space: pre-wrap;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;background-color: #f8f8f8; color: #333333; font-family: &amp;quot;menlo&amp;quot; , &amp;quot;monaco&amp;quot; , &amp;quot;courier new&amp;quot; , monospace; font-size: 12px; line-height: 22.4px; white-space: pre-wrap;&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;hljs-string&quot; style=&quot;border: 0px; color: #dd1144; font-family: &amp;quot;menlo&amp;quot; , &amp;quot;monaco&amp;quot; , &amp;quot;courier new&amp;quot; , monospace; font-size: 12px; line-height: 22.4px; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;background-color: #f8f8f8; color: #333333; font-family: &amp;quot;menlo&amp;quot; , &amp;quot;monaco&amp;quot; , &amp;quot;courier new&amp;quot; , monospace; font-size: 12px; line-height: 22.4px; white-space: pre-wrap;&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;hljs-string&quot; style=&quot;border: 0px; color: #dd1144; font-family: &amp;quot;menlo&amp;quot; , &amp;quot;monaco&amp;quot; , &amp;quot;courier new&amp;quot; , monospace; font-size: 12px; line-height: 22.4px; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;2&lt;/span&gt;&lt;span style=&quot;background-color: #f8f8f8; color: #333333; font-family: &amp;quot;menlo&amp;quot; , &amp;quot;monaco&amp;quot; , &amp;quot;courier new&amp;quot; , monospace; font-size: 12px; line-height: 22.4px; white-space: pre-wrap;&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;hljs-string&quot; style=&quot;border: 0px; color: #dd1144; font-family: &amp;quot;menlo&amp;quot; , &amp;quot;monaco&amp;quot; , &amp;quot;courier new&amp;quot; , monospace; font-size: 12px; line-height: 22.4px; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;3&lt;/span&gt;&lt;span style=&quot;background-color: #f8f8f8; color: #333333; font-family: &amp;quot;menlo&amp;quot; , &amp;quot;monaco&amp;quot; , &amp;quot;courier new&amp;quot; , monospace; font-size: 12px; line-height: 22.4px; white-space: pre-wrap;&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;hljs-string&quot; style=&quot;border: 0px; color: #dd1144; font-family: &amp;quot;menlo&amp;quot; , &amp;quot;monaco&amp;quot; , &amp;quot;courier new&amp;quot; , monospace; font-size: 12px; line-height: 22.4px; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;       &lt;/span&gt;&lt;span style=&quot;background-color: #f8f8f8; color: #333333; font-family: &amp;quot;menlo&amp;quot; , &amp;quot;monaco&amp;quot; , &amp;quot;courier new&amp;quot; , monospace; font-size: 12px; line-height: 22.4px; white-space: pre-wrap;&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;hljs-string&quot; style=&quot;border: 0px; color: #dd1144; font-family: &amp;quot;menlo&amp;quot; , &amp;quot;monaco&amp;quot; , &amp;quot;courier new&amp;quot; , monospace; font-size: 12px; line-height: 22.4px; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;K&lt;/span&gt;&lt;span style=&quot;background-color: #f8f8f8; color: #333333; font-family: &amp;quot;menlo&amp;quot; , &amp;quot;monaco&amp;quot; , &amp;quot;courier new&amp;quot; , monospace; font-size: 12px; line-height: 22.4px; white-space: pre-wrap;&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;hljs-string&quot; style=&quot;border: 0px; color: #dd1144; font-family: &amp;quot;menlo&amp;quot; , &amp;quot;monaco&amp;quot; , &amp;quot;courier new&amp;quot; , monospace; font-size: 12px; line-height: 22.4px; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;             &lt;/span&gt;&lt;span style=&quot;background-color: #f8f8f8; color: #333333; font-family: &amp;quot;menlo&amp;quot; , &amp;quot;monaco&amp;quot; , &amp;quot;courier new&amp;quot; , monospace; font-size: 12px; line-height: 22.4px; white-space: pre-wrap;&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;hljs-string&quot; style=&quot;border: 0px; color: #dd1144; font-family: &amp;quot;menlo&amp;quot; , &amp;quot;monaco&amp;quot; , &amp;quot;courier new&amp;quot; , monospace; font-size: 12px; line-height: 22.4px; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;                               &lt;/span&gt;&lt;span style=&quot;background-color: #f8f8f8; color: #333333; font-family: &amp;quot;menlo&amp;quot; , &amp;quot;monaco&amp;quot; , &amp;quot;courier new&amp;quot; , monospace; font-size: 12px; line-height: 22.4px; white-space: pre-wrap;&quot;&gt;|
     +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
     |&lt;/span&gt;&lt;span class=&quot;hljs-string&quot; style=&quot;border: 0px; color: #dd1144; font-family: &amp;quot;menlo&amp;quot; , &amp;quot;monaco&amp;quot; , &amp;quot;courier new&amp;quot; , monospace; font-size: 12px; line-height: 22.4px; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;     Extended payload length continued, if payload len == 127  &lt;/span&gt;&lt;span style=&quot;background-color: #f8f8f8; color: #333333; font-family: &amp;quot;menlo&amp;quot; , &amp;quot;monaco&amp;quot; , &amp;quot;courier new&amp;quot; , monospace; font-size: 12px; line-height: 22.4px; white-space: pre-wrap;&quot;&gt;|
     + - - - - - - - - - - - - - - - +-------------------------------+
     |&lt;/span&gt;&lt;span class=&quot;hljs-string&quot; style=&quot;border: 0px; color: #dd1144; font-family: &amp;quot;menlo&amp;quot; , &amp;quot;monaco&amp;quot; , &amp;quot;courier new&amp;quot; , monospace; font-size: 12px; line-height: 22.4px; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;                               &lt;/span&gt;&lt;span style=&quot;background-color: #f8f8f8; color: #333333; font-family: &amp;quot;menlo&amp;quot; , &amp;quot;monaco&amp;quot; , &amp;quot;courier new&amp;quot; , monospace; font-size: 12px; line-height: 22.4px; white-space: pre-wrap;&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;hljs-string&quot; style=&quot;border: 0px; color: #dd1144; font-family: &amp;quot;menlo&amp;quot; , &amp;quot;monaco&amp;quot; , &amp;quot;courier new&amp;quot; , monospace; font-size: 12px; line-height: 22.4px; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;Masking-key, if MASK set to 1  &lt;/span&gt;&lt;span style=&quot;background-color: #f8f8f8; color: #333333; font-family: &amp;quot;menlo&amp;quot; , &amp;quot;monaco&amp;quot; , &amp;quot;courier new&amp;quot; , monospace; font-size: 12px; line-height: 22.4px; white-space: pre-wrap;&quot;&gt;|
     +-------------------------------+-------------------------------+
     |&lt;/span&gt;&lt;span class=&quot;hljs-string&quot; style=&quot;border: 0px; color: #dd1144; font-family: &amp;quot;menlo&amp;quot; , &amp;quot;monaco&amp;quot; , &amp;quot;courier new&amp;quot; , monospace; font-size: 12px; line-height: 22.4px; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline; white-space: pre-wrap;&quot;&gt; Masking-key (continued)       &lt;/span&gt;&lt;span style=&quot;background-color: #f8f8f8; color: #333333; font-family: &amp;quot;menlo&amp;quot; , &amp;quot;monaco&amp;quot; , &amp;quot;courier new&amp;quot; , monospace; font-size: 12px; line-height: 22.4px; white-space: pre-wrap;&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;hljs-string&quot; style=&quot;border: 0px; color: #dd1144; font-family: &amp;quot;menlo&amp;quot; , &amp;quot;monaco&amp;quot; , &amp;quot;courier new&amp;quot; , monospace; font-size: 12px; line-height: 22.4px; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;          Payload Data         &lt;/span&gt;&lt;span style=&quot;background-color: #f8f8f8; color: #333333; font-family: &amp;quot;menlo&amp;quot; , &amp;quot;monaco&amp;quot; , &amp;quot;courier new&amp;quot; , monospace; font-size: 12px; line-height: 22.4px; white-space: pre-wrap;&quot;&gt;|
     +-------------------------------- - - - - - - - - - - - - - - - +
     :                     Payload Data continued ...                :
     + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
     |&lt;/span&gt;&lt;span class=&quot;hljs-string&quot; style=&quot;border: 0px; color: #dd1144; font-family: &amp;quot;menlo&amp;quot; , &amp;quot;monaco&amp;quot; , &amp;quot;courier new&amp;quot; , monospace; font-size: 12px; line-height: 22.4px; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline; white-space: pre-wrap;&quot;&gt;                     Payload Data continued ...                &lt;/span&gt;&lt;span style=&quot;background-color: #f8f8f8; color: #333333; font-family: &amp;quot;menlo&amp;quot; , &amp;quot;monaco&amp;quot; , &amp;quot;courier new&amp;quot; , monospace; font-size: 12px; line-height: 22.4px; white-space: pre-wrap;&quot;&gt;|
     +---------------------------------------------------------------+&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
На самом деле полностью для нашего примера реализовывать парсинг не обязательно. На просторах интернета я нашел готовую реализацию парсера фреймов, которая нам подойдет. В любом случае в продакшен такую поделку тащить ни в коем случае нельзя. Для этого лучше подойдут уже готовые имплементации.(Jetty, Spring и тп).&lt;br /&gt;
&lt;br /&gt;
Итак для реализации чата на websocket нам понадобятся следующие CompletedHandler&lt;br /&gt;
&lt;br /&gt;
&lt;ul style=&quot;text-align: left;&quot;&gt;
&lt;li&gt;&lt;b&gt;AcceptCompletionHandler&lt;/b&gt; - принимающий подключение от браузера&lt;/li&gt;
&lt;li&gt;&lt;b&gt;HandShakeReadCompletionHandler&lt;/b&gt; - читающий handshake от клиента&lt;/li&gt;
&lt;li&gt;&lt;b&gt;HandShakeWriteCompletionHandler&lt;/b&gt; - handshake ответ сервера&amp;nbsp;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;ReadCompletionHandler&lt;/b&gt; - принимающий сообщение от сервера&lt;/li&gt;
&lt;li&gt;&lt;b&gt;WriteCompletionHandler&lt;/b&gt; - отправляющий сообщение клиентам&lt;/li&gt;
&lt;li&gt;&lt;b&gt;WebSocketServer&lt;/b&gt; - main класс&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;b&gt;WebSocketServer&lt;/b&gt; - запускает основной поток на прием подключенией через AcceptCompletionHandler, хранит все подключения клиентов и позволяет отправить сообщение всем подключеным клиентам.&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;package com.bssys.ws_nio2;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousChannelGroup;
import java.nio.channels.AsynchronousServerSocketChannel;
import java.nio.channels.AsynchronousSocketChannel;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class WebSocketServer {

 private final List&amp;lt;AsynchronousSocketChannel&amp;gt; connections = Collections
   .synchronizedList(new ArrayList&amp;lt;AsynchronousSocketChannel&amp;gt;());

 public void run() throws IOException {
  ExecutorService newFixedThreadPool = Executors.newFixedThreadPool(10);
  AsynchronousChannelGroup group = AsynchronousChannelGroup.withThreadPool(newFixedThreadPool);

  final AsynchronousServerSocketChannel listener = AsynchronousServerSocketChannel.open(group);
  InetSocketAddress address = new InetSocketAddress(&quot;localhost&quot;, 3333);
  listener.bind(address);

  AcceptCompletionHandler acceptCompletionHandler = new AcceptCompletionHandler(listener, this);

  listener.accept(null, acceptCompletionHandler);
 }

 public void addClient(AsynchronousSocketChannel client) {
  connections.add(client);
 }

 public void removeClient(AsynchronousSocketChannel channel) {
  connections.remove(channel);
 }

 public void writeMessageToClients(AsynchronousSocketChannel channel, String message) {
  synchronized (connections) {
   for (AsynchronousSocketChannel clientConnection : connections) {
    if (clientConnection != channel) {
     try {
      byte[] doWrite = WriteFrame.doWrite(message);
      ByteBuffer outputBuffer = ByteBuffer.wrap(doWrite);
      SessionState sessionState = new SessionState();
      clientConnection.write(outputBuffer, sessionState, new WriteCompletionHandler());
     } catch (IOException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
     }
    }
   }
  }
 }

 public static void main(String[] args) throws IOException {
  System.out.println(&quot;WebSocket server start...&quot;);
  new WebSocketServer().run();
  try {
   Thread.currentThread().join();
  } catch (InterruptedException e) {
   e.printStackTrace();
  }
 }
}&lt;/code&gt;&lt;/pre&gt;
&lt;b&gt;AcceptCompletionHandler&lt;/b&gt; - при успешном подключении клиента, запускает асинхронный обмен рукопожатием через HandShakeReadCompletionHandler&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;package com.bssys.ws_nio2;

import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousServerSocketChannel;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;

public class AcceptCompletionHandler implements CompletionHandler&amp;lt;AsynchronousSocketChannel, Void&amp;gt; {

 private AsynchronousServerSocketChannel listener;
 private WebSocketServer server;

 public AcceptCompletionHandler(AsynchronousServerSocketChannel listener, WebSocketServer server) {
  this.listener = listener;
  this.server = server;
 }

 public void completed(AsynchronousSocketChannel socketChannel, Void arg1) {
  System.out.println(&quot;client connected: &quot; + socketChannel);

  listener.accept(null, this);

  ByteBuffer inputBuffer = ByteBuffer.allocate(2048);
  HandShakeReadCompletionHandler readCompletionHandler = new HandShakeReadCompletionHandler(socketChannel,
    inputBuffer, server);
  SessionState sessionState = new SessionState();
  socketChannel.read(inputBuffer, sessionState, readCompletionHandler);
 }

 public void failed(Throwable arg0, Void arg1) {
 }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;/pre&gt;
&lt;b&gt;HandShakeReadCompletionHandler&lt;/b&gt; - читает переданное сообщение от клиента на установление рукопожатия. Генерирует ключ, подготавливает ответное сообщение handshake и оправляет его асинхронно с помощью HandShakeWriteCompletionHandler&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;package com.bssys.ws_nio2;

import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;
import java.security.NoSuchAlgorithmException;
import java.util.Map;

public class HandShakeReadCompletionHandler implements CompletionHandler&amp;lt;Integer, SessionState&amp;gt; {

 private AsynchronousSocketChannel socketChannel;
 private ByteBuffer inputBuffer;
 private WebSocketServer server;

 public HandShakeReadCompletionHandler(AsynchronousSocketChannel socketChannel, ByteBuffer inputBuffer,
   WebSocketServer server) {
  this.socketChannel = socketChannel;
  this.inputBuffer = inputBuffer;
  this.server = server;
 }

 public void completed(Integer bytesRead, SessionState sessionState) {

  byte[] buffer = new byte[bytesRead];
  inputBuffer.rewind();

  inputBuffer.get(buffer);
  String message = new String(buffer);

  System.out.println(&quot;Received message from client : &quot; + message);

  HttpRequest httpRequest = new HttpRequest(message);
  Map&amp;lt;String, String&amp;gt; headers = httpRequest.getHeaders();

  inputBuffer.clear();

  String websocketKey = headers.get(&quot;Sec-WebSocket-Key&quot;).trim();
  try {
   String websocketAcceptKey = KeyGeneration.getKey(websocketKey);
   String response = String.format(&quot;HTTP/1.1 101 Switching Protocols\r\n&quot; + &quot;Upgrade: websocket\r\n&quot;
     + &quot;Connection: Upgrade\r\n&quot; + &quot;Sec-WebSocket-Accept: %s\r\n\r\n&quot;, websocketAcceptKey);
   ByteBuffer wrap = ByteBuffer.wrap(response.getBytes());
   HandShakeWriteCompletionHandler handShakeWriteCompletionHandler = new HandShakeWriteCompletionHandler(
     socketChannel, server);
   socketChannel.write(wrap, sessionState, handShakeWriteCompletionHandler);

  } catch (NoSuchAlgorithmException e) {
   e.printStackTrace();
  }
 }

 public void failed(Throwable exc, SessionState sessionState) {
 }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;/pre&gt;
&lt;b&gt;HandShakeWriteCompletionHandler&lt;/b&gt; -при успешной отправке handshake, сохраняет поключение, и запускает асинхронное чтение канала клиента. через ReadCompletionHandler&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;package com.bssys.ws_nio2;

import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;

public class HandShakeWriteCompletionHandler implements CompletionHandler&amp;lt;Integer, SessionState&amp;gt; {

 private AsynchronousSocketChannel socketChannel;
 private WebSocketServer server;

 public HandShakeWriteCompletionHandler(AsynchronousSocketChannel socketChannel, WebSocketServer server) {
  this.socketChannel = socketChannel;
  this.server = server;
 }

 public void completed(Integer result, SessionState sessionState) {
  server.addClient(socketChannel);
  ByteBuffer inputBuffer = ByteBuffer.allocate(1024);
  socketChannel.read(inputBuffer, sessionState, new ReadCompletionHandler(socketChannel, inputBuffer, server));
 }

 public void failed(Throwable exc, SessionState sessionState) {
 }
}&lt;/code&gt;&lt;/pre&gt;
&lt;b&gt;ReadCompletionHandler&lt;/b&gt; - Читает сообщение, декодирует его, отправляет его всем подключеным клиентам, и снова запускает чтение из канала, используя самого себя в качестве CompletionHandler. Там образом организуется цикл чтения сообщения от клиента. Все работает асинхронно.&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;package com.bssys.ws_nio2;

import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;

public class ReadCompletionHandler implements CompletionHandler&amp;lt;Integer, SessionState&amp;gt; {

 private AsynchronousSocketChannel channel;
 private ByteBuffer inputBuffer;
 private WebSocketServer server;

 public ReadCompletionHandler(AsynchronousSocketChannel channel, ByteBuffer inputBuffer, WebSocketServer server) {
  this.channel = channel;
  this.inputBuffer = inputBuffer;
  this.server = server;
 }

 public void completed(Integer bytesRead, SessionState session) {
  if (bytesRead &amp;lt; 1) {
   System.out.println(&quot;Closing connection to &quot; + channel);
   server.removeClient(channel);
  } else {

   byte[] buffer = new byte[bytesRead];
   inputBuffer.rewind();
   inputBuffer.get(buffer);

   byte[] decodeFrame = RawParse.parse(buffer).payload;
   String message = new String(decodeFrame);

   System.out.println(&quot;Received message from &quot; + &quot;:&quot; + message);
   inputBuffer.clear();

   channel.read(inputBuffer, session, this);

   server.writeMessageToClients(channel, message);
  }
 }

 public void failed(Throwable exc, SessionState attachment) {
  server.removeClient(channel);
 }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;/pre&gt;
&lt;br /&gt;
Весь код вместе с клиентом на js на gihub&lt;br /&gt;
https://github.com/Hibernate2009/nio-websocket-server.git&lt;br /&gt;
&lt;br /&gt;
Ссылки которые мне очень помогли самому разобраться с websocket&#39;ами.&lt;br /&gt;
&lt;br /&gt;
&lt;a href=&quot;https://websockets.wordpress.com/the-lightweight-websockets-java-server/&quot; target=&quot;_blank&quot;&gt;https://websockets.wordpress.com/the-lightweight-websockets-java-server/&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;http://stackoverflow.com/questions/18368130/how-to-parse-and-validate-a-websocket-frame-in-java&quot; target=&quot;_blank&quot;&gt;http://stackoverflow.com/questions/18368130/how-to-parse-and-validate-a-websocket-frame-in-java&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;http://learn.javascript.ru/websockets&quot; target=&quot;_blank&quot;&gt;http://learn.javascript.ru/websockets&lt;/a&gt;&lt;/div&gt;
</content><link rel='replies' type='application/atom+xml' href='http://jser.blogspot.com/feeds/6683524215418218116/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/37848572/6683524215418218116' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/37848572/posts/default/6683524215418218116'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/37848572/posts/default/6683524215418218116'/><link rel='alternate' type='text/html' href='http://jser.blogspot.com/2016/03/nio20-asynchronous-io-websocket-chat.html' title='NIO2.0 Asynchronous I/O. WebSocket chat server'/><author><name>JSer</name><uri>http://www.blogger.com/profile/17455716060338444232</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-37848572.post-3153763561879828876</id><published>2016-03-03T18:48:00.000+09:00</published><updated>2016-03-17T00:16:12.425+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Asynchronous I/O"/><category scheme="http://www.blogger.com/atom/ns#" term="AsynchronousServerSocketChannel"/><category scheme="http://www.blogger.com/atom/ns#" term="AsynchronousSocketChannel"/><category scheme="http://www.blogger.com/atom/ns#" term="CompletionHandler"/><category scheme="http://www.blogger.com/atom/ns#" term="java"/><category scheme="http://www.blogger.com/atom/ns#" term="NIO2"/><title type='text'>Understanding NIO2.0 Asynchronous I/O. EchoServer</title><content type='html'>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
Посмотрим внимательно на NIO2.0 асинхронную сокетную модель появившуюся еще в Java 7 и напишем полностью асинхронный сервер и клиент по типу request/reply.&lt;br /&gt;
Асинхронный IO для сокетного взаимодействия представлен следующими классами:&lt;br /&gt;
&lt;ul style=&quot;text-align: left;&quot;&gt;
&lt;li&gt;AsynchronousSocketChannel&lt;/li&gt;
&lt;li&gt;AsynchronousServerSocketChannel&lt;/li&gt;
&lt;/ul&gt;
Есть два подхода работы с ними. Используя:&lt;br /&gt;
&lt;ul style=&quot;text-align: left;&quot;&gt;
&lt;li&gt;Future&lt;/li&gt;
&lt;li&gt;CompletionHandler&lt;/li&gt;
&lt;/ul&gt;
Использование Future предполагает следующие действия&lt;br /&gt;
&lt;ul style=&quot;text-align: left;&quot;&gt;
&lt;li&gt;Использование блокирующего вызова get(), который сразу вернет результат&lt;br /&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;AsynchronousSocketChannel worker = future.get();&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;Проверка результата выполнения&lt;br /&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;while(!future.isDone()) {
 // do something
        Thread.sleep(100);
}&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
Работа с Future моделью в любом случае предполагает блокировку потока выполнения. Я вообще не вижу смысла в таком случае в использовании асинхронного IO. Какой в смысл, если в результате у нас все равно возникают блокировки, и при этом нужно будет выносить процессинг в отдельный поток. Такое может понадобиться только в самых простых случаях.&lt;br /&gt;
&lt;br /&gt;
Очевидно что для полного асинхронного взаимодействия надо использовать CompletionHandler. При этом надо действовать аккуратно чтобы не свалиться calback hell.&lt;br /&gt;
&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;
Интерфейс CompletionHandler следующий:&lt;br /&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;interface CompletionHandler&amp;lt;V,A&amp;gt; {
 void completed(V result, A attachment);
 void failed(Throwable exc, A attachment);
}&lt;/code&gt;&lt;/pre&gt;
V = &amp;nbsp;тип результирующего значения&lt;br /&gt;
A = тип объекта вложения при выполнении операции. Используется для того чтобы передавать контекст. Фактически это сессия текущего запроса.&lt;br /&gt;
&lt;br /&gt;
Для реализации сервера нам в общем случае понадобится 3 реализации CompletionHandler&lt;br /&gt;
&lt;br /&gt;
&lt;ul style=&quot;text-align: left;&quot;&gt;
&lt;li&gt;для обработки подключения клиента&lt;br /&gt;CompletionHandler&amp;lt;AsynchronousSocketChannel, ? super A&amp;gt;&lt;/li&gt;
&lt;li&gt;для чтения запроса клиента&lt;br /&gt;CompletionHandler&amp;lt;Integer, ? super A&amp;gt;&lt;/li&gt;
&lt;li&gt;для отправки ответа клиенту&lt;br /&gt;CompletionHandler&amp;lt;Integer, ? super A&amp;gt;&lt;/li&gt;
&lt;/ul&gt;
Для клиента аналогично
&lt;br /&gt;
&lt;ul style=&quot;text-align: left;&quot;&gt;
&lt;li&gt;для подключения к серверу&lt;br /&gt;CompletionHandler&amp;lt;Void, ? super A&amp;gt;&lt;/li&gt;
&lt;li&gt;для отправки запроса на сервер&lt;br /&gt;CompletionHandler&amp;lt;Integer, ? super A&amp;gt;&lt;/li&gt;
&lt;li&gt;для чтения ответа серверу&lt;br /&gt;CompletionHandler&amp;lt;Integer, ? super A&amp;gt;&lt;/li&gt;
&lt;/ul&gt;
Итак вот шаблон реализации асинхронного сервера calback hell исполнении. Инициализацию и поднятие сервера опустим, ее вы можете увидеть в итоговой реализации.
&lt;br /&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;listener.accept(state, new CompletionHandler&amp;lt;AsynchronousSocketChannel, SessionState&amp;gt;() {
 @Override
 public void completed(final AsynchronousSocketChannel socketChannel, SessionState attachment) {
  ByteBuffer inputBuffer = ByteBuffer.allocate(1024);
  socketChannel.read(inputBuffer, attachment, new CompletionHandler&amp;lt;Integer, SessionState&amp;gt;(){
   @Override
   public void completed(Integer result, SessionState attachment) {
    ByteBuffer outputBuffer = ByteBuffer.allocate(1024);;
    socketChannel.write(outputBuffer, attachment,new CompletionHandler&amp;lt;Integer, SessionState&amp;gt;(){
     @Override
     public void completed(Integer result, SessionState attachment) {
      
     }
     @Override
     public void failed(Throwable exc, SessionState attachment) {
      
     }
    });
   }
   @Override
   public void failed(Throwable exc, SessionState attachment) {
    
   }
  });
 }
 @Override
 public void failed(Throwable exc, SessionState attachment) {
  
 }
});&lt;/code&gt;&lt;/pre&gt;
Конечно код по такому шаблону неудобно сопровождать. Лучше всего разнести реализацию CompletionHandler в отдельные классы.
Итак вот итоговый результат EchoServer. Исходный код https://github.com/Hibernate2009/nio-echoserver.git&lt;br /&gt;
EchoServer
&lt;br /&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;package com.bssys.nio2.server;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.AsynchronousChannelGroup;
import java.nio.channels.AsynchronousServerSocketChannel;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import com.bssys.nio2.SessionState;

public class EchoServer {

 public void run() throws IOException {
  ExecutorService newFixedThreadPool = Executors.newFixedThreadPool(10);
  AsynchronousChannelGroup group = AsynchronousChannelGroup.withThreadPool(newFixedThreadPool);

  final AsynchronousServerSocketChannel listener = AsynchronousServerSocketChannel.open(group);
  InetSocketAddress address = new InetSocketAddress(&quot;localhost&quot;, 3333);
  listener.bind(address);

  AcceptCompletionHandler acceptCompletionHandler = new AcceptCompletionHandler(listener);

  SessionState state = new SessionState();
  listener.accept(state, acceptCompletionHandler);
  
 }

 public static void main(String[] args) throws IOException {
  System.out.println(&quot;Async server started&quot;);
  new EchoServer().run();
  try {
   Thread.currentThread().join();
  } catch (InterruptedException e) {
   e.printStackTrace();
  }
 }
}&lt;/code&gt;&lt;/pre&gt;
AcceptCompletionHandler
&lt;br /&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;package com.bssys.nio2.server;

import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousServerSocketChannel;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;

import com.bssys.nio2.SessionState;

public class AcceptCompletionHandler implements CompletionHandler&amp;lt;AsynchronousSocketChannel, SessionState&amp;gt; {

 private AsynchronousServerSocketChannel listener;

 public AcceptCompletionHandler(AsynchronousServerSocketChannel listener) {
  this.listener = listener;
 }

 @Override
 public void completed(AsynchronousSocketChannel socketChannel, SessionState sessionState) {
  // accept the next connection
  SessionState newSessionState = new SessionState();
  listener.accept(newSessionState, this);

  // handle this connection
  ByteBuffer inputBuffer = ByteBuffer.allocate(2048);
  ServerReadCompletionHandler readCompletionHandler = new ServerReadCompletionHandler(socketChannel, inputBuffer);
  socketChannel.read(inputBuffer, sessionState, readCompletionHandler);
 }

 @Override
 public void failed(Throwable exc, SessionState sessionState) {
  // Handle connection failure...
 }
}&lt;/code&gt;&lt;/pre&gt;
ServerReadCompletionHandler
&lt;br /&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;package com.bssys.nio2.server;

import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;

import com.bssys.nio2.SessionState;

public class ServerReadCompletionHandler implements CompletionHandler&amp;lt;Integer, SessionState&amp;gt; {

 private AsynchronousSocketChannel socketChannel;
 private ByteBuffer inputBuffer;

 public ServerReadCompletionHandler(AsynchronousSocketChannel socketChannel, ByteBuffer inputBuffer) {
  this.socketChannel = socketChannel;
  this.inputBuffer = inputBuffer;
 }

 @Override
 public void completed(Integer bytesRead, SessionState sessionState) {

  byte[] buffer = new byte[bytesRead];
  inputBuffer.rewind();

  inputBuffer.get(buffer);
  String message = new String(buffer);

  System.out.println(&quot;Received message from client : &quot; + message);
  
  ServerWriteCompletionHandler writeCompletionHandler = new ServerWriteCompletionHandler(socketChannel);
  
  ByteBuffer outputBuffer = ByteBuffer.wrap(buffer);
  socketChannel.write(outputBuffer, sessionState, writeCompletionHandler);
 }

 @Override
 public void failed(Throwable exc, SessionState attachment) {
  // Handle read failure.....
 }
}&lt;/code&gt;&lt;/pre&gt;
ServerWriteCompletionHandler
&lt;br /&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;package com.bssys.nio2.server;

import java.io.IOException;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;

import com.bssys.nio2.SessionState;

public class ServerWriteCompletionHandler implements CompletionHandler&amp;lt;Integer, SessionState&amp;gt; {

 private AsynchronousSocketChannel socketChannel;
 

 public ServerWriteCompletionHandler(AsynchronousSocketChannel socketChannel) {
  this.socketChannel = socketChannel;
 }

 @Override
 public void completed(Integer bytesWritten, SessionState sessionState) {
  try {
   socketChannel.close();
  } catch (IOException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }
 }

 @Override
 public void failed(Throwable exc, SessionState attachment) {
  // Handle write failure.....
 }
}&lt;/code&gt;&lt;/pre&gt;
Неплохая презентация по работе с буферами http://www.kgeorgiy.info/courses/java-advanced/slides/nio.xhtml#(1)
&lt;/div&gt;
</content><link rel='replies' type='application/atom+xml' href='http://jser.blogspot.com/feeds/3153763561879828876/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/37848572/3153763561879828876' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/37848572/posts/default/3153763561879828876'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/37848572/posts/default/3153763561879828876'/><link rel='alternate' type='text/html' href='http://jser.blogspot.com/2016/03/understanding-nio20-asynchronous-io.html' title='Understanding NIO2.0 Asynchronous I/O. EchoServer'/><author><name>JSer</name><uri>http://www.blogger.com/profile/17455716060338444232</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-37848572.post-1852158600102917176</id><published>2016-02-25T20:51:00.001+09:00</published><updated>2016-03-17T00:16:35.221+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="java"/><category scheme="http://www.blogger.com/atom/ns#" term="ZeroMQ"/><title type='text'>Client notification using ZeroMQ</title><content type='html'>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgivZe168biytUyMNs-IMqPJBNKBvhJTDGSy5Wg8PYfc7KtAukE2MuFguFQdiqDOLIYtyoc4UtxdTGDeO82rmlN4fHe1ECaNF0Iy__OaGZdUSTyx9ezXl8Gt-JF0DICcudSWeNg/s1600/zeromq1-300x115.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgivZe168biytUyMNs-IMqPJBNKBvhJTDGSy5Wg8PYfc7KtAukE2MuFguFQdiqDOLIYtyoc4UtxdTGDeO82rmlN4fHe1ECaNF0Iy__OaGZdUSTyx9ezXl8Gt-JF0DICcudSWeNg/s1600/zeromq1-300x115.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
Итак нам понадобилось реализовать механизм рассылки сообщений. Дополнительных требований таких как гарантированная доставка не предъявляется. Фактически нам подойдет примитивное сокетное взаимодействие.&lt;br /&gt;
На практике обычно стоит обратная задача, когда надо реализовать сервер выдерживающий большую нагрузку. Есть множество решений как ее решать, используя традиционные блокирующие сокеты, или что будет лучшим выбором, используя асинхронное взаимодействие NIO в экосистеме java. Например можно взять готовый клиент-серверный фремворк&amp;nbsp;&lt;a href=&quot;http://netty.io/&quot; target=&quot;_blank&quot;&gt;netty&lt;/a&gt;.&lt;br /&gt;
У нас же стоит обратная задача, сервер должен уведомлять большое количество клиентов.&lt;br /&gt;
В enterpise системах, для этой цели обычно используются сиcтемы MQ. Но в нашем pet-project можно использовать кое-что поинтереснее.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;Используя для нашей задачи традиционные сокеты, нам бы потребовалось заводить в отдельный поток каждый сокет клиента, и при наступлении события перебирать их в цикле, решая в том числе проблемы с дисконектами. А сколько при этом потребуется ресурсов, если клиентов будет хотя бы больше 1000.&lt;br /&gt;
И вот для этого нам идеально подойдет библиотека &lt;a href=&quot;http://zeromq.org/&quot; target=&quot;_blank&quot;&gt;ZeroMQ&lt;/a&gt;.&lt;br /&gt;
Подробно рассказывать о ней я не буду, информации в интернетах достаточно. Отмечу просто что кроме традиционного взаимодействия&amp;nbsp;&lt;a href=&quot;http://api.zeromq.org/zmq_socket.html#_request_reply_pattern&quot;&gt;REQUEST/REPLY&lt;/a&gt;&amp;nbsp;библиотека предоставляет возможность взаимодействия по типу&amp;nbsp;&lt;a href=&quot;http://api.zeromq.org/zmq_socket.html#_publish_subscribe_pattern&quot;&gt;PUBLISH/SUBSCRIBE&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
Это то что нам нужно.&lt;br /&gt;
Итак архитектура механизма доставки следующая:&lt;br /&gt;
&lt;br /&gt;
&lt;ul style=&quot;text-align: left;&quot;&gt;
&lt;li&gt;PubServer.java - сервер доставки получает сообщение из ActiveMQ в примитивном формате из которого получает имя топик и само сообщение. И собственно отправляет его в топик нужному клиенту.&lt;/li&gt;
&lt;li&gt;SubClient.java- принимает имя топика из которого будет получать сообщения устанавливает соединение с сервером и подписывается на топик.&lt;/li&gt;
&lt;/ul&gt;
&lt;br /&gt;
Реализация ZeroMQ предоставлена официально поддерживамой библиотекой jeromq для java.&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;package com.bssys.zeromq;

import javax.jms.Connection;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageConsumer;
import javax.jms.MessageListener;
import javax.jms.Session;
import javax.jms.TextMessage;

import org.apache.activemq.ActiveMQConnectionFactory;
import org.zeromq.ZMQ;
import org.zeromq.ZMQ.Context;
import org.zeromq.ZMQ.Socket;

public class PubServer {

 private Socket publisher = null;

 public PubServer() throws Exception {
  initJMSConnection();
  initZMQ();
 }

 private void initZMQ() {
  Context context = ZMQ.context(1);
  publisher = context.socket(ZMQ.PUB);
  publisher.bind(&quot;tcp://*:5563&quot;);

  Runtime.getRuntime().addShutdownHook(new Thread() {
   public void run() {
    System.out.println(&quot;ShutDown&quot;);
    if (publisher != null) {
     publisher.close();
    }
    if (context != null) {
     context.term();
    }
   }
  });
 }

 private void initJMSConnection() {
  Connection connection = null;
  Session session = null;
  MessageConsumer consumer = null;
  try {
   ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory(&quot;tcp://localhost:61616&quot;);
   connection = connectionFactory.createConnection();
   connection.start();
   session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
   Destination adminQueue = session.createQueue(&quot;TEST_QUEUE&quot;);
   consumer = session.createConsumer(adminQueue);
   for (int i = 0; i &amp;lt;= 10; i++) {
    consumer.setMessageListener(new ConsumerMessageListener());
   }

  } catch (Exception e) {
   if (consumer != null) {
    try {
     consumer.close();
    } catch (JMSException e1) {
     e1.printStackTrace();
    }
   }
   if (session != null) {
    try {
     session.close();
    } catch (JMSException e1) {
     e1.printStackTrace();
    }
   }
   if (connection != null) {
    try {
     connection.close();
    } catch (JMSException e1) {
     e1.printStackTrace();
    }
   }
   throw new IllegalStateException(&quot;JMS connection fail&quot;);
  }
 }

 public synchronized void send(String topic, String msg) {
  publisher.sendMore(topic);
  publisher.send(msg);
 }

 class ConsumerMessageListener implements MessageListener {

  @Override
  public void onMessage(Message jmsMessage) {
   System.out.println(Thread.currentThread().getName()+&quot; recive message&quot;);
   if (jmsMessage instanceof TextMessage) {
    TextMessage textMessage = (TextMessage) jmsMessage;
    try {
     String text = textMessage.getText();
     String[] split = text.split(&quot;:&quot;);
     if (split != null &amp;amp;&amp;amp; split.length &amp;gt; 1) {
      String topic = split[0];
      String msg = split[1];
      send(topic, msg);
     }
    } catch (JMSException e) {
     e.printStackTrace();
    }
   }
  }
 }

 public static void main(String arg[]) {
  try {
   new PubServer();
  } catch (Exception e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }
 }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;package com.bssys.zeromq;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import org.zeromq.ZMQ;
import org.zeromq.ZMQ.Context;
import org.zeromq.ZMQ.Socket;

public class SubClient implements Runnable {

 private Context context;
 private Socket subscriber;
 
 private String topic;

 public SubClient(String topic) {
  this.topic = topic;
  context = ZMQ.context(1);
  subscriber = context.socket(ZMQ.SUB);
  
  ExecutorService newCachedThreadPool = Executors.newCachedThreadPool();
  newCachedThreadPool.submit(this);
  
  Runtime.getRuntime().addShutdownHook(new Thread() {
   public void run() {
    System.out.println(&quot;ShutDown&quot;);
    subscriber.close();
    context.term();
   }
  });

 }

 @Override
 public void run() {
  subscriber.connect(&quot;tcp://localhost:5563&quot;);
  subscriber.subscribe(topic.getBytes());
  while (!Thread.currentThread().isInterrupted()) {
   // Read envelope with address
   String address = subscriber.recvStr();
   // Read message contents
   String contents = subscriber.recvStr();
   System.out.println(address + &quot; : &quot; + contents);
  }
 }
 
 public static void main(String args[]){
  if (args!=null &amp;amp;&amp;amp; args.length&amp;gt;0){
   new SubClient(args[0]);
  }else {
   System.out.println(&quot;java SubClient A&quot;);
  }
 }

}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
</content><link rel='replies' type='application/atom+xml' href='http://jser.blogspot.com/feeds/1852158600102917176/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/37848572/1852158600102917176' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/37848572/posts/default/1852158600102917176'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/37848572/posts/default/1852158600102917176'/><link rel='alternate' type='text/html' href='http://jser.blogspot.com/2016/02/client-notification-using-zeromq.html' title='Client notification using ZeroMQ'/><author><name>JSer</name><uri>http://www.blogger.com/profile/17455716060338444232</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgivZe168biytUyMNs-IMqPJBNKBvhJTDGSy5Wg8PYfc7KtAukE2MuFguFQdiqDOLIYtyoc4UtxdTGDeO82rmlN4fHe1ECaNF0Iy__OaGZdUSTyx9ezXl8Gt-JF0DICcudSWeNg/s72-c/zeromq1-300x115.png" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-37848572.post-3691928488743900003</id><published>2016-02-09T01:00:00.001+09:00</published><updated>2016-03-17T00:16:46.681+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="ActiveMQ"/><title type='text'>Authentication and authorization ActiveMQ</title><content type='html'>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
Итак, если вам надо управлять доступом к очередям на ActiveMQ, предоставляю пример как это можно сделать. Может кому пригодится. В файле activemq.xml делаем следующее
&lt;br /&gt;
&lt;pre&gt;&lt;code class=&quot;language-xml&quot;&gt;&amp;lt;plugins&amp;gt;
 &amp;lt;simpleAuthenticationPlugin&amp;gt;
  &amp;lt;users&amp;gt;
   &amp;lt;authenticationUser username=&quot;admin&quot; password=&quot;admin&quot; groups=&quot;producers,consumers,admins&quot; /&amp;gt;
   &amp;lt;authenticationUser username=&quot;producer&quot; password=&quot;producer&quot; groups=&quot;producers&quot; /&amp;gt;
   &amp;lt;authenticationUser username=&quot;consumer&quot; password=&quot;consumer&quot; groups=&quot;consumers&quot; /&amp;gt;
  &amp;lt;/users&amp;gt;
 &amp;lt;/simpleAuthenticationPlugin&amp;gt;
 &amp;lt;authorizationPlugin&amp;gt;
  &amp;lt;map&amp;gt;
   &amp;lt;authorizationMap&amp;gt;
    &amp;lt;authorizationEntries&amp;gt;
     &amp;lt;authorizationEntry queue=&quot;&amp;gt;&quot; write=&quot;producers&quot; read=&quot;consumers&quot; admin=&quot;admins&quot; /&amp;gt;
     &amp;lt;authorizationEntry topic=&quot;ActiveMQ.Advisory.&amp;gt;&quot; write=&quot;producers,consumers&quot; read=&quot;producers,consumers&quot; admin=&quot;admins&quot; /&amp;gt;
    &amp;lt;/authorizationEntries&amp;gt;
    &amp;lt;tempDestinationAuthorizationEntry&amp;gt;
     &amp;lt;tempDestinationAuthorizationEntry write=&quot;producers&quot; read=&quot;consumers&quot; admin=&quot;admins&quot; /&amp;gt;
    &amp;lt;/tempDestinationAuthorizationEntry&amp;gt;
   &amp;lt;/authorizationMap&amp;gt;
  &amp;lt;/map&amp;gt;
 &amp;lt;/authorizationPlugin&amp;gt;
&amp;lt;/plugins&amp;gt;&lt;/code&gt;&lt;/pre&gt;
Теперь положить сообщение в очередь может пользователь producer, а прочитать consumer

&lt;/div&gt;
</content><link rel='replies' type='application/atom+xml' href='http://jser.blogspot.com/feeds/3691928488743900003/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/37848572/3691928488743900003' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/37848572/posts/default/3691928488743900003'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/37848572/posts/default/3691928488743900003'/><link rel='alternate' type='text/html' href='http://jser.blogspot.com/2016/02/authentication-and-authorization.html' title='Authentication and authorization ActiveMQ'/><author><name>JSer</name><uri>http://www.blogger.com/profile/17455716060338444232</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-37848572.post-5922093103828323467</id><published>2016-01-12T20:34:00.000+09:00</published><updated>2016-02-09T01:04:37.862+09:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="ActiveMQ"/><category scheme="http://www.blogger.com/atom/ns#" term="Apache Camel"/><category scheme="http://www.blogger.com/atom/ns#" term="ServiceMix"/><title type='text'>Best practices ServiceMix and Apache Camel</title><content type='html'>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;/div&gt;
В этой статье я решил поделится всеми своими рекомендациями при разработке с применением ServiceMix и Apache Camel. По мере развития статья будет дополняться.&lt;br /&gt;
&lt;div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEih2VZnfpUtgdRoQU5esWlosl2iEbXSCAWiVqZfoUDO0ejnQzNwOI9iR3VWha-MrYG-U6236_t6xUnyx8dgxfpWwsRyo7DqiJB3h6AUvOIBZQpXmBZUYv_ypT5EOSMWwGtRZUj9/s1600/best-practices.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;145&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEih2VZnfpUtgdRoQU5esWlosl2iEbXSCAWiVqZfoUDO0ejnQzNwOI9iR3VWha-MrYG-U6236_t6xUnyx8dgxfpWwsRyo7DqiJB3h6AUvOIBZQpXmBZUYv_ypT5EOSMWwGtRZUj9/s400/best-practices.jpg&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;ul style=&quot;text-align: left;&quot;&gt;
&lt;li&gt;Используйте Spring DSL вместо Java DSL. Это позволит в некоторых случаях при необходимости вести разработку в обычном текстовом редакторе, избавит вас от format-hell. Так же в силу древовидности XML вам не придется использовать костыли по типу end(), которые вы вынуждены вставлять чтобы контролировать блоки кода.&lt;/li&gt;
&lt;li&gt;При подключении к ActiveMQ всегда используйте failover (failover:tcp://127.0.0.1:61616)&lt;/li&gt;
&lt;li&gt;При использовании файлового адаптера читающего файлы из директории, всегда подключайте фильтр на проверку пустого файла.
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;import org.apache.camel.component.file.GenericFile;
import org.apache.camel.component.file.GenericFileFilter;
import org.apache.log4j.Logger;

public class EmptyFilter&amp;lt;T&amp;gt; implements GenericFileFilter&amp;lt;T&amp;gt; {
 private static Logger log = Logger.getLogger(EmptyFilter.class);

 public boolean accept(GenericFile&amp;lt;T&amp;gt; file) {
  boolean res = false;
  if (!file.isDirectory()) {
   if (file.getFileLength() &amp;gt; 0) {
    res = true;
   }
  }
  return res;
 }
}

&lt;/code&gt;&lt;/pre&gt;
Иcпользование:
&lt;br /&gt;&lt;pre&gt;&lt;code class=&quot;language-xml&quot;&gt;file:{{inbox}}?filter=#emptyFilter
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;Итерирование по коллекциям удобно делать через &lt;a href=&quot;http://camel.apache.org/splitter.html&quot;&gt;http://camel.apache.org/splitter.html&lt;/a&gt;&amp;nbsp;из коробки предоставляющий возможность параллелизации&lt;br /&gt;
&lt;pre&gt;&lt;code class=&quot;language-xml&quot;&gt;&amp;lt;split parallelProcessing=&quot;true&quot;&amp;gt;
 &amp;lt;xpath&amp;gt;/items/*&amp;lt;/xpath&amp;gt;
        &amp;lt;to uri=&quot;direct:foo&quot; /&amp;gt;
&amp;lt;/split&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;Все используемые библиотеке выносите в отдельный файл feature. И никогда не носите библиотеки внутри разрабатываемых вами bundle. Любой jar можно установить используя wrap.
&lt;pre&gt;&lt;code class=&quot;language-xml&quot;&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&amp;gt;
&amp;lt;features&amp;gt;
  &amp;lt;feature name=&#39;tir_config&#39; version=&#39;1.0&#39; install=&#39;auto&#39;&amp;gt;
    &amp;lt;feature version=&quot;3.2.11.RELEASE_1&quot;&amp;gt;spring-jdbc&amp;lt;/feature&amp;gt;
    &amp;lt;feature&amp;gt;jdbc&amp;lt;/feature&amp;gt;
    &amp;lt;feature&amp;gt;jetty&amp;lt;/feature&amp;gt;
    &amp;lt;feature&amp;gt;camel-quartz&amp;lt;/feature&amp;gt;
    &amp;lt;feature&amp;gt;activemq-broker&amp;lt;/feature&amp;gt;
    &amp;lt;feature&amp;gt;camel-jetty&amp;lt;/feature&amp;gt;
    &amp;lt;feature&amp;gt;camel-jackson&amp;lt;/feature&amp;gt;
    &amp;lt;feature&amp;gt;camel-urlrewrite&amp;lt;/feature&amp;gt;
    &amp;lt;feature&amp;gt;camel-saxon&amp;lt;/feature&amp;gt;
    &amp;lt;feature&amp;gt;activemq-web-console&amp;lt;/feature&amp;gt;
    &amp;lt;bundle&amp;gt;wrap:mvn:org.apache.httpcomponents/httpcore/4.3.3&amp;lt;/bundle&amp;gt;
    &amp;lt;bundle&amp;gt;wrap:mvn:org.apache.httpcomponents/httpclient/4.3.6&amp;lt;/bundle&amp;gt;
    &amp;lt;bundle&amp;gt;wrap:mvn:org.apache.commons/commons-pool2/2.4.2&amp;lt;/bundle&amp;gt;
    &amp;lt;bundle&amp;gt;mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.jedis/2.7.3_1&amp;lt;/bundle&amp;gt;
  &amp;lt;/feature&amp;gt;
&amp;lt;/features&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
JDBC-источники данных(DataSource) удобно деплоить как blueprint и не городить отдельный bundle, который будет доступен как osgi-сервис. Также всегда добавляйте свойство osgi.jndi.service.name, это позволит обращаться к источнику также через JNDI. Файл достаточно положить в папку deploy. Это представляет настройки в чистом виде, без необходимости выводить их в отдельный настроечный файл
&lt;pre&gt;&lt;code class=&quot;language-xml&quot;&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&amp;gt;
&amp;lt;blueprint xmlns=&quot;http://www.osgi.org/xmlns/blueprint/v1.0.0&quot;
 xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot; xmlns:cm=&quot;http://aries.apache.org/blueprint/xmlns/blueprint-cm/v1.0.0&quot;&amp;gt;
 &amp;lt;bean id=&quot;postgresPoolingDS&quot; class=&quot;org.apache.commons.dbcp.BasicDataSource&quot;&amp;gt;
  &amp;lt;property name=&quot;driverClassName&quot; value=&quot;org.postgresql.Driver&quot; /&amp;gt;
  &amp;lt;property name=&quot;url&quot; value=&quot;jdbc:postgresql://10.1.6.66/postgres&quot; /&amp;gt;
  &amp;lt;property name=&quot;username&quot; value=&quot;postgres&quot; /&amp;gt;
  &amp;lt;property name=&quot;password&quot; value=&quot;postgres&quot; /&amp;gt;
  &amp;lt;property name=&quot;defaultAutoCommit&quot; value=&quot;true&quot; /&amp;gt;
  &amp;lt;property name=&quot;testWhileIdle&quot; value=&quot;false&quot; /&amp;gt;
  &amp;lt;property name=&quot;testOnBorrow&quot; value=&quot;false&quot; /&amp;gt;
  &amp;lt;property name=&quot;testOnReturn&quot; value=&quot;false&quot; /&amp;gt;
  &amp;lt;property name=&quot;timeBetweenEvictionRunsMillis&quot; value=&quot;5000&quot; /&amp;gt;
  &amp;lt;property name=&quot;maxActive&quot; value=&quot;10&quot; /&amp;gt;
  &amp;lt;property name=&quot;maxIdle&quot; value=&quot;10&quot; /&amp;gt;
  &amp;lt;property name=&quot;minIdle&quot; value=&quot;0&quot; /&amp;gt;
  &amp;lt;property name=&quot;maxWait&quot; value=&quot;10000&quot; /&amp;gt;
  &amp;lt;property name=&quot;initialSize&quot; value=&quot;1&quot; /&amp;gt;
  &amp;lt;property name=&quot;removeAbandonedTimeout&quot; value=&quot;10&quot; /&amp;gt;
  &amp;lt;property name=&quot;removeAbandoned&quot; value=&quot;true&quot; /&amp;gt;
  &amp;lt;property name=&quot;logAbandoned&quot; value=&quot;false&quot; /&amp;gt;
  &amp;lt;property name=&quot;minEvictableIdleTimeMillis&quot; value=&quot;30000&quot; /&amp;gt;
 &amp;lt;/bean&amp;gt;
 &amp;lt;service interface=&quot;javax.sql.DataSource&quot; ref=&quot;postgresPoolingDS&quot; &amp;gt;
  &amp;lt;service-properties&amp;gt;
   &amp;lt;entry key=&quot;datasource.name&quot; value=&quot;postgres&quot; /&amp;gt;
   &amp;lt;entry key=&quot;osgi.jndi.service.name&quot; value=&quot;jdbc/postgresds&quot; /&amp;gt;
  &amp;lt;/service-properties&amp;gt;
 &amp;lt;/service&amp;gt;
&amp;lt;/blueprint&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;Связи между модулями реализуйте через osgi-сервисы. Зависимостями можно управлять через настроечные файлы. Таким образом можно например настраивать используемый источник данных (DataSource)
&lt;pre&gt;&lt;code class=&quot;language-xml&quot;&gt;&amp;lt;osgi:reference id=&quot;dataSource&quot; interface=&quot;javax.sql.DataSource&quot; filter=&quot;(datasource.name=${dbType})&quot; /&amp;gt;&lt;/code&gt;&lt;/pre&gt;
Использование osgi-сервисов избавит вас от головной боли связанной с очередностью запуска модулей. Они сами будут разруливать свои зависимости, и ждать нужного osgi-сервиса(Модуль будет находиться в состоянии WAIT). В отличии от использования кода напрямую, когда модуль просто свалится (FAIL), и даже появление нужной зависимости не заставит его подняться. Необходимо будет делать restart вручную. Если какая-то часть кода просится в библиотеку, выносите его в отдельный jar и подключайте в файле features, про который мы уже сказали выше.
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
</content><link rel='replies' type='application/atom+xml' href='http://jser.blogspot.com/feeds/5922093103828323467/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/37848572/5922093103828323467' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/37848572/posts/default/5922093103828323467'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/37848572/posts/default/5922093103828323467'/><link rel='alternate' type='text/html' href='http://jser.blogspot.com/2016/01/best-practices-servicemix-and-apache.html' title='Best practices ServiceMix and Apache Camel'/><author><name>JSer</name><uri>http://www.blogger.com/profile/17455716060338444232</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEih2VZnfpUtgdRoQU5esWlosl2iEbXSCAWiVqZfoUDO0ejnQzNwOI9iR3VWha-MrYG-U6236_t6xUnyx8dgxfpWwsRyo7DqiJB3h6AUvOIBZQpXmBZUYv_ypT5EOSMWwGtRZUj9/s72-c/best-practices.jpg" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-37848572.post-5435624180823374889</id><published>2014-04-28T19:25:00.003+10:00</published><updated>2014-04-29T17:01:47.105+10:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="ActiveMQ"/><category scheme="http://www.blogger.com/atom/ns#" term="Apache Camel"/><category scheme="http://www.blogger.com/atom/ns#" term="cluster"/><category scheme="http://www.blogger.com/atom/ns#" term="ServiceMix"/><title type='text'>ActiveMQ Master/Slave and load balancer cluster</title><content type='html'>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
Пришло время объединить все технологии кластеризации ActiveMQ вместе, и продемонстрировать систему устойчивую к сбоям и позволяющую распределять нагрузку. Для примера построим архитектуру кластера на базе 8 инстансов ActiveMQ. Схема выглядит следующим образом.&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjZFbcs3LQmgDA_Vb57UkS9Gx5TF5ONyRq5_Nq7x24Trk5uUS1FQ5-NJUMOhMIQnvpreur7EOQKf9WpW5F_sVIoBKVmmGUKtM3UYssQzFv3CpG43-c866OWefixHMyVuTlypMXk/s1600/Kraken4.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjZFbcs3LQmgDA_Vb57UkS9Gx5TF5ONyRq5_Nq7x24Trk5uUS1FQ5-NJUMOhMIQnvpreur7EOQKf9WpW5F_sVIoBKVmmGUKtM3UYssQzFv3CpG43-c866OWefixHMyVuTlypMXk/s1600/Kraken4.png&quot; height=&quot;195&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
Рассмотрим детально:&lt;br /&gt;
&lt;br /&gt;
&lt;ul style=&quot;text-align: left;&quot;&gt;
&lt;li&gt;Инстансы связаные общим хранилищем Master/Slave 1-2, 3-4, 5-6, 7-8. По идее каждая пара разворачивается на отдельной машине. Для возможности развертывания на отдельной машине, все инстансы разнесены по портам.&lt;/li&gt;
&lt;li&gt;С помощью Network Broker каждая пара с связана с каждой парой.&lt;/li&gt;
&lt;li&gt;Для каждой пары на этом же хосте, с помощью Apache Camel поднимается HTTP точка, принимающая запросы от внешнего балансировщика и прокидывающая запрос в очередь.&lt;/li&gt;
&lt;li&gt;Все инстансы из каждой группы могут делиться сообщения с другими группами&lt;/li&gt;
&lt;li&gt;Отдельные клиенты разворачиваются каждый на своем хосте и читают сообщения и очереди.&lt;/li&gt;
&lt;li&gt;Балансировщик для простоты тоже реализован с помощью Apache Camel&lt;/li&gt;
&lt;/ul&gt;
Ниже можно взглянуть на настройки каждого инстанса и исходный код.&lt;br /&gt;
&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
instance1:
&lt;br /&gt;
&lt;pre&gt;&lt;code class=&quot;language-xml&quot;&gt;&amp;lt;networkConnectors&amp;gt;
 &amp;lt;networkConnector name=&quot;instance1-instance3-instance4&quot; uri=&quot;masterslave:(tcp://localhost:61619,tcp://localhost:61620)&quot; /&amp;gt;
 &amp;lt;networkConnector name=&quot;instance1-instance5-instance6&quot; uri=&quot;masterslave:(tcp://localhost:61621,tcp://localhost:61622)&quot; /&amp;gt;
 &amp;lt;networkConnector name=&quot;instance1-instance7-instance8&quot; uri=&quot;masterslave:(tcp://localhost:61623,tcp://localhost:61624)&quot; /&amp;gt;
&amp;lt;/networkConnectors&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-xml&quot;&gt;&amp;lt;persistenceAdapter&amp;gt;
 &amp;lt;kahaDB directory=&quot;${activemq.data}/../../../kahadb1&quot; /&amp;gt;
&amp;lt;/persistenceAdapter&amp;gt;&lt;/code&gt;&lt;/pre&gt;
instance2:
&lt;br /&gt;
&lt;pre&gt;&lt;code class=&quot;language-xml&quot;&gt;&amp;lt;networkConnectors&amp;gt;
 &amp;lt;networkConnector name=&quot;instance2-instance3-instance4&quot; uri=&quot;masterslave:(tcp://localhost:61619,tcp://localhost:61620)&quot; /&amp;gt;
 &amp;lt;networkConnector name=&quot;instance2-instance5-instance6&quot; uri=&quot;masterslave:(tcp://localhost:61621,tcp://localhost:61622)&quot; /&amp;gt;
 &amp;lt;networkConnector name=&quot;instance2-instance7-instance8&quot; uri=&quot;masterslave:(tcp://localhost:61623,tcp://localhost:61624)&quot; /&amp;gt;
&amp;lt;/networkConnectors&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-xml&quot;&gt;&amp;lt;persistenceAdapter&amp;gt;
 &amp;lt;kahaDB directory=&quot;${activemq.data}/../../../kahadb1&quot; /&amp;gt;
&amp;lt;/persistenceAdapter&amp;gt;&lt;/code&gt;&lt;/pre&gt;
instance3:
&lt;br /&gt;
&lt;pre&gt;&lt;code class=&quot;language-xml&quot;&gt;&amp;lt;networkConnectors&amp;gt;
 &amp;lt;networkConnector name=&quot;instance3-instance1-instance2&quot; uri=&quot;masterslave:(tcp://localhost:61617,tcp://localhost:61618)&quot; /&amp;gt;
 &amp;lt;networkConnector name=&quot;instance3-instance5-instance6&quot; uri=&quot;masterslave:(tcp://localhost:61621,tcp://localhost:61622)&quot; /&amp;gt;
 &amp;lt;networkConnector name=&quot;instance3-instance7-instance8&quot; uri=&quot;masterslave:(tcp://localhost:61623,tcp://localhost:61624)&quot; /&amp;gt;
&amp;lt;/networkConnectors&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-xml&quot;&gt;&amp;lt;persistenceAdapter&amp;gt;
 &amp;lt;kahaDB directory=&quot;${activemq.data}/../../../kahadb2&quot; /&amp;gt;
&amp;lt;/persistenceAdapter&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
instance4:
&lt;br /&gt;
&lt;pre&gt;&lt;code class=&quot;language-xml&quot;&gt;&amp;lt;networkConnectors&amp;gt;
 &amp;lt;networkConnector name=&quot;instance4-instance1-instance2&quot; uri=&quot;masterslave:(tcp://localhost:61617,tcp://localhost:61618)&quot; /&amp;gt;
 &amp;lt;networkConnector name=&quot;instance4-instance5-instance6&quot; uri=&quot;masterslave:(tcp://localhost:61621,tcp://localhost:61622)&quot; /&amp;gt;
 &amp;lt;networkConnector name=&quot;instance4-instance7-instance8&quot; uri=&quot;masterslave:(tcp://localhost:61623,tcp://localhost:61624)&quot; /&amp;gt;
&amp;lt;/networkConnectors&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-xml&quot;&gt;&amp;lt;persistenceAdapter&amp;gt;
 &amp;lt;kahaDB directory=&quot;${activemq.data}/../../../kahadb2&quot; /&amp;gt;
&amp;lt;/persistenceAdapter&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
instance5:
&lt;br /&gt;
&lt;pre&gt;&lt;code class=&quot;language-xml&quot;&gt;&amp;lt;networkConnectors&amp;gt;
 &amp;lt;networkConnector name=&quot;instance5-instance1-instance2&quot; uri=&quot;masterslave:(tcp://localhost:61617,tcp://localhost:61618)&quot; /&amp;gt;
 &amp;lt;networkConnector name=&quot;instance5-instance3-instance4&quot; uri=&quot;masterslave:(tcp://localhost:61619,tcp://localhost:61620)&quot; /&amp;gt;
 &amp;lt;networkConnector name=&quot;instance5-instance7-instance8&quot; uri=&quot;masterslave:(tcp://localhost:61623,tcp://localhost:61624)&quot; /&amp;gt;
&amp;lt;/networkConnectors&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-xml&quot;&gt;&amp;lt;persistenceAdapter&amp;gt;
 &amp;lt;kahaDB directory=&quot;${activemq.data}/../../../kahadb3&quot; /&amp;gt;
&amp;lt;/persistenceAdapter&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
instance6:
&lt;br /&gt;
&lt;pre&gt;&lt;code class=&quot;language-xml&quot;&gt;&amp;lt;networkConnectors&amp;gt;
 &amp;lt;networkConnector name=&quot;instance6-instance1-instance2&quot; uri=&quot;masterslave:(tcp://localhost:61617,tcp://localhost:61618)&quot; /&amp;gt;
 &amp;lt;networkConnector name=&quot;instance6-instance3-instance4&quot; uri=&quot;masterslave:(tcp://localhost:61619,tcp://localhost:61620)&quot; /&amp;gt;
 &amp;lt;networkConnector name=&quot;instance6-instance7-instance8&quot; uri=&quot;masterslave:(tcp://localhost:61623,tcp://localhost:61624)&quot; /&amp;gt;
&amp;lt;/networkConnectors&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-xml&quot;&gt;&amp;lt;persistenceAdapter&amp;gt;
 &amp;lt;kahaDB directory=&quot;${activemq.data}/../../../kahadb3&quot; /&amp;gt;
&amp;lt;/persistenceAdapter&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
instance7:
&lt;br /&gt;
&lt;pre&gt;&lt;code class=&quot;language-xml&quot;&gt;&amp;lt;networkConnectors&amp;gt;
 &amp;lt;networkConnector name=&quot;instance7-instance1-instance2&quot; uri=&quot;masterslave:(tcp://localhost:61617,tcp://localhost:61618)&quot; /&amp;gt;
 &amp;lt;networkConnector name=&quot;instance7-instance3-instance4&quot; uri=&quot;masterslave:(tcp://localhost:61619,tcp://localhost:61620)&quot; /&amp;gt;
 &amp;lt;networkConnector name=&quot;instance7-instance5-instance6&quot; uri=&quot;masterslave:(tcp://localhost:61621,tcp://localhost:61622)&quot; /&amp;gt;
&amp;lt;/networkConnectors&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-xml&quot;&gt;&amp;lt;persistenceAdapter&amp;gt;
 &amp;lt;kahaDB directory=&quot;${activemq.data}/../../../kahadb4&quot; /&amp;gt;
&amp;lt;/persistenceAdapter&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
instance8:
&lt;br /&gt;
&lt;pre&gt;&lt;code class=&quot;language-xml&quot;&gt;&amp;lt;networkConnectors&amp;gt;
 &amp;lt;networkConnector name=&quot;instance8-instance1-instance2&quot; uri=&quot;masterslave:(tcp://localhost:61617,tcp://localhost:61618)&quot; /&amp;gt;
 &amp;lt;networkConnector name=&quot;instance8-instance3-instance4&quot; uri=&quot;masterslave:(tcp://localhost:61619,tcp://localhost:61620)&quot; /&amp;gt;
 &amp;lt;networkConnector name=&quot;instance8-instance5-instance6&quot; uri=&quot;masterslave:(tcp://localhost:61621,tcp://localhost:61622)&quot; /&amp;gt;
&amp;lt;/networkConnectors&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-xml&quot;&gt;&amp;lt;persistenceAdapter&amp;gt;
 &amp;lt;kahaDB directory=&quot;${activemq.data}/../../../kahadb4&quot; /&amp;gt;
&amp;lt;/persistenceAdapter&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
Настройки ресурсов для клиента, я объединил в один модуль. В реальном случае настройки будут разнесены.
&lt;pre&gt;&lt;code class=&quot;language-xml&quot;&gt;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot;?&amp;gt;
&amp;lt;beans xmlns=&amp;quot;http://www.springframework.org/schema/beans&amp;quot;
 xmlns:xsi=&amp;quot;http://www.w3.org/2001/XMLSchema-instance&amp;quot; xmlns:osgi=&amp;quot;http://www.springframework.org/schema/osgi&amp;quot;
 xsi:schemaLocation=&amp;quot;
       http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/osgi http://www.springframework.org/schema/osgi/spring-osgi.xsd&amp;quot;&amp;gt;

 &amp;lt;osgi:reference id=&amp;quot;platformTransactionManager&amp;quot;
  interface=&amp;quot;org.springframework.transaction.PlatformTransactionManager&amp;quot; /&amp;gt;


 &amp;lt;bean id=&amp;quot;instanceConnectionFactory1&amp;quot; class=&amp;quot;org.apache.activemq.ActiveMQConnectionFactory&amp;quot;&amp;gt;
  &amp;lt;property name=&amp;quot;brokerURL&amp;quot;
   value=&amp;quot;failover://(tcp://localhost:61617,tcp://localhost:61618)?initialReconnectDelay=100&amp;quot; /&amp;gt;
  &amp;lt;property name=&amp;quot;userName&amp;quot; value=&amp;quot;karaf&amp;quot; /&amp;gt;
  &amp;lt;property name=&amp;quot;password&amp;quot; value=&amp;quot;karaf&amp;quot; /&amp;gt;
  &amp;lt;property name=&amp;quot;redeliveryPolicy&amp;quot;&amp;gt;
   &amp;lt;bean class=&amp;quot;org.apache.activemq.RedeliveryPolicy&amp;quot;&amp;gt;
    &amp;lt;property name=&amp;quot;maximumRedeliveries&amp;quot; value=&amp;quot;0&amp;quot; /&amp;gt;
   &amp;lt;/bean&amp;gt;
  &amp;lt;/property&amp;gt;
 &amp;lt;/bean&amp;gt;

 &amp;lt;bean id=&amp;quot;instanceConnectionFactory2&amp;quot; class=&amp;quot;org.apache.activemq.ActiveMQConnectionFactory&amp;quot;&amp;gt;
  &amp;lt;property name=&amp;quot;brokerURL&amp;quot;
   value=&amp;quot;failover://(tcp://localhost:61619,tcp://localhost:61620)?initialReconnectDelay=100&amp;quot; /&amp;gt;
  &amp;lt;property name=&amp;quot;userName&amp;quot; value=&amp;quot;karaf&amp;quot; /&amp;gt;
  &amp;lt;property name=&amp;quot;password&amp;quot; value=&amp;quot;karaf&amp;quot; /&amp;gt;
  &amp;lt;property name=&amp;quot;redeliveryPolicy&amp;quot;&amp;gt;
   &amp;lt;bean class=&amp;quot;org.apache.activemq.RedeliveryPolicy&amp;quot;&amp;gt;
    &amp;lt;property name=&amp;quot;maximumRedeliveries&amp;quot; value=&amp;quot;0&amp;quot; /&amp;gt;
   &amp;lt;/bean&amp;gt;
  &amp;lt;/property&amp;gt;
 &amp;lt;/bean&amp;gt;

 &amp;lt;bean id=&amp;quot;instanceConnectionFactory3&amp;quot; class=&amp;quot;org.apache.activemq.ActiveMQConnectionFactory&amp;quot;&amp;gt;
  &amp;lt;property name=&amp;quot;brokerURL&amp;quot;
   value=&amp;quot;failover://(tcp://localhost:61621,tcp://localhost:61622)?initialReconnectDelay=100&amp;quot; /&amp;gt;
  &amp;lt;property name=&amp;quot;userName&amp;quot; value=&amp;quot;karaf&amp;quot; /&amp;gt;
  &amp;lt;property name=&amp;quot;password&amp;quot; value=&amp;quot;karaf&amp;quot; /&amp;gt;
  &amp;lt;property name=&amp;quot;redeliveryPolicy&amp;quot;&amp;gt;
   &amp;lt;bean class=&amp;quot;org.apache.activemq.RedeliveryPolicy&amp;quot;&amp;gt;
    &amp;lt;property name=&amp;quot;maximumRedeliveries&amp;quot; value=&amp;quot;0&amp;quot; /&amp;gt;
   &amp;lt;/bean&amp;gt;
  &amp;lt;/property&amp;gt;
 &amp;lt;/bean&amp;gt;

 &amp;lt;bean id=&amp;quot;instanceConnectionFactory4&amp;quot; class=&amp;quot;org.apache.activemq.ActiveMQConnectionFactory&amp;quot;&amp;gt;
  &amp;lt;property name=&amp;quot;brokerURL&amp;quot;
   value=&amp;quot;failover://(tcp://localhost:61623,tcp://localhost:61624)?initialReconnectDelay=100&amp;quot; /&amp;gt;
  &amp;lt;property name=&amp;quot;userName&amp;quot; value=&amp;quot;karaf&amp;quot; /&amp;gt;
  &amp;lt;property name=&amp;quot;password&amp;quot; value=&amp;quot;karaf&amp;quot; /&amp;gt;
  &amp;lt;property name=&amp;quot;redeliveryPolicy&amp;quot;&amp;gt;
   &amp;lt;bean class=&amp;quot;org.apache.activemq.RedeliveryPolicy&amp;quot;&amp;gt;
    &amp;lt;property name=&amp;quot;maximumRedeliveries&amp;quot; value=&amp;quot;0&amp;quot; /&amp;gt;
   &amp;lt;/bean&amp;gt;
  &amp;lt;/property&amp;gt;
 &amp;lt;/bean&amp;gt;

 &amp;lt;bean id=&amp;quot;jmsConfig1&amp;quot;
  class=&amp;quot;org.apache.activemq.camel.component.ActiveMQConfiguration&amp;quot;&amp;gt;
  &amp;lt;property name=&amp;quot;connectionFactory&amp;quot; ref=&amp;quot;instanceConnectionFactory1&amp;quot; /&amp;gt;
  &amp;lt;property name=&amp;quot;transactionManager&amp;quot; ref=&amp;quot;platformTransactionManager&amp;quot; /&amp;gt;
  &amp;lt;property name=&amp;quot;transacted&amp;quot; value=&amp;quot;true&amp;quot; /&amp;gt;
 &amp;lt;/bean&amp;gt;

 &amp;lt;bean id=&amp;quot;jmsConfig2&amp;quot;
  class=&amp;quot;org.apache.activemq.camel.component.ActiveMQConfiguration&amp;quot;&amp;gt;
  &amp;lt;property name=&amp;quot;connectionFactory&amp;quot; ref=&amp;quot;instanceConnectionFactory2&amp;quot; /&amp;gt;
  &amp;lt;property name=&amp;quot;transactionManager&amp;quot; ref=&amp;quot;platformTransactionManager&amp;quot; /&amp;gt;
  &amp;lt;property name=&amp;quot;transacted&amp;quot; value=&amp;quot;true&amp;quot; /&amp;gt;
 &amp;lt;/bean&amp;gt;

 &amp;lt;bean id=&amp;quot;jmsConfig3&amp;quot;
  class=&amp;quot;org.apache.activemq.camel.component.ActiveMQConfiguration&amp;quot;&amp;gt;
  &amp;lt;property name=&amp;quot;connectionFactory&amp;quot; ref=&amp;quot;instanceConnectionFactory3&amp;quot; /&amp;gt;
  &amp;lt;property name=&amp;quot;transactionManager&amp;quot; ref=&amp;quot;platformTransactionManager&amp;quot; /&amp;gt;
  &amp;lt;property name=&amp;quot;transacted&amp;quot; value=&amp;quot;true&amp;quot; /&amp;gt;
 &amp;lt;/bean&amp;gt;

 &amp;lt;bean id=&amp;quot;jmsConfig4&amp;quot;
  class=&amp;quot;org.apache.activemq.camel.component.ActiveMQConfiguration&amp;quot;&amp;gt;
  &amp;lt;property name=&amp;quot;connectionFactory&amp;quot; ref=&amp;quot;instanceConnectionFactory4&amp;quot; /&amp;gt;
  &amp;lt;property name=&amp;quot;transactionManager&amp;quot; ref=&amp;quot;platformTransactionManager&amp;quot; /&amp;gt;
  &amp;lt;property name=&amp;quot;transacted&amp;quot; value=&amp;quot;true&amp;quot; /&amp;gt;
 &amp;lt;/bean&amp;gt;

 &amp;lt;bean id=&amp;quot;instance1&amp;quot; class=&amp;quot;org.apache.activemq.camel.component.ActiveMQComponent&amp;quot;&amp;gt;
  &amp;lt;constructor-arg ref=&amp;quot;jmsConfig1&amp;quot; /&amp;gt;
 &amp;lt;/bean&amp;gt;
 &amp;lt;bean id=&amp;quot;instance2&amp;quot; class=&amp;quot;org.apache.activemq.camel.component.ActiveMQComponent&amp;quot;&amp;gt;
  &amp;lt;constructor-arg ref=&amp;quot;jmsConfig2&amp;quot; /&amp;gt;
 &amp;lt;/bean&amp;gt;
 &amp;lt;bean id=&amp;quot;instance3&amp;quot; class=&amp;quot;org.apache.activemq.camel.component.ActiveMQComponent&amp;quot;&amp;gt;
  &amp;lt;constructor-arg ref=&amp;quot;jmsConfig3&amp;quot; /&amp;gt;
 &amp;lt;/bean&amp;gt;
 &amp;lt;bean id=&amp;quot;instance4&amp;quot; class=&amp;quot;org.apache.activemq.camel.component.ActiveMQComponent&amp;quot;&amp;gt;
  &amp;lt;constructor-arg ref=&amp;quot;jmsConfig4&amp;quot; /&amp;gt;
 &amp;lt;/bean&amp;gt;

 &amp;lt;bean id=&amp;quot;PROPAGATION_REQUIRED&amp;quot; class=&amp;quot;org.apache.camel.spring.spi.SpringTransactionPolicy&amp;quot;&amp;gt;
  &amp;lt;property name=&amp;quot;transactionManager&amp;quot; ref=&amp;quot;platformTransactionManager&amp;quot; /&amp;gt;
  &amp;lt;property name=&amp;quot;propagationBehaviorName&amp;quot; value=&amp;quot;PROPAGATION_REQUIRED&amp;quot; /&amp;gt;
 &amp;lt;/bean&amp;gt;
 &amp;lt;bean id=&amp;quot;PROPAGATION_REQUIRES_NEW&amp;quot; class=&amp;quot;org.apache.camel.spring.spi.SpringTransactionPolicy&amp;quot;&amp;gt;
  &amp;lt;property name=&amp;quot;transactionManager&amp;quot; ref=&amp;quot;platformTransactionManager&amp;quot; /&amp;gt;
  &amp;lt;property name=&amp;quot;propagationBehaviorName&amp;quot; value=&amp;quot;PROPAGATION_REQUIRES_NEW&amp;quot; /&amp;gt;
 &amp;lt;/bean&amp;gt;

&amp;lt;/beans&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
Непосредственно код клиента на Apache Camel
&lt;pre&gt;&lt;code class=&quot;language-xml&quot;&gt;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot;?&amp;gt;
&amp;lt;beans xmlns=&amp;quot;http://www.springframework.org/schema/beans&amp;quot;
 xmlns:xsi=&amp;quot;http://www.w3.org/2001/XMLSchema-instance&amp;quot; xmlns:camel=&amp;quot;http://camel.apache.org/schema/spring&amp;quot;
 xmlns:osgi=&amp;quot;http://www.springframework.org/schema/osgi&amp;quot;
 xsi:schemaLocation=&amp;quot;
       http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/osgi http://www.springframework.org/schema/osgi/spring-osgi.xsd
       http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd&amp;quot;&amp;gt;

 &amp;lt;camelContext xmlns=&amp;quot;http://camel.apache.org/schema/spring&amp;quot;&amp;gt;
  &amp;lt;route&amp;gt;
   &amp;lt;from uri=&amp;quot;jetty:http://localhost:8191/myapp/myservice&amp;quot; /&amp;gt;
   &amp;lt;to uri=&amp;quot;instance1:queue:notify&amp;quot; /&amp;gt;
  &amp;lt;/route&amp;gt;
  &amp;lt;route&amp;gt;
   &amp;lt;from uri=&amp;quot;jetty:http://localhost:8192/myapp/myservice&amp;quot; /&amp;gt;
   &amp;lt;to uri=&amp;quot;instance2:queue:notify&amp;quot; /&amp;gt;
  &amp;lt;/route&amp;gt;
  &amp;lt;route&amp;gt;
   &amp;lt;from uri=&amp;quot;jetty:http://localhost:8193/myapp/myservice&amp;quot; /&amp;gt;
   &amp;lt;to uri=&amp;quot;instance3:queue:notify&amp;quot; /&amp;gt;
  &amp;lt;/route&amp;gt;
  &amp;lt;route&amp;gt;
   &amp;lt;from uri=&amp;quot;jetty:http://localhost:8194/myapp/myservice&amp;quot; /&amp;gt;
   &amp;lt;to uri=&amp;quot;instance4:queue:notify&amp;quot; /&amp;gt;
  &amp;lt;/route&amp;gt;
  
  &amp;lt;route&amp;gt;
   &amp;lt;from uri=&amp;quot;instance1:queue:notify&amp;quot; /&amp;gt;
   &amp;lt;log message=&amp;quot;instance1:${body}&amp;quot; /&amp;gt;
   &amp;lt;to uri=&amp;quot;file:C:/data/kraken/instance1&amp;quot; /&amp;gt;
  &amp;lt;/route&amp;gt;
  &amp;lt;route&amp;gt;
   &amp;lt;from uri=&amp;quot;instance2:queue:notify&amp;quot; /&amp;gt;
   &amp;lt;log message=&amp;quot;instance2:${body}&amp;quot; /&amp;gt;
   &amp;lt;to uri=&amp;quot;file:C:/data/kraken/instance2&amp;quot; /&amp;gt;
  &amp;lt;/route&amp;gt;
  &amp;lt;route&amp;gt;
   &amp;lt;from uri=&amp;quot;instance3:queue:notify&amp;quot; /&amp;gt;
   &amp;lt;log message=&amp;quot;instance3:${body}&amp;quot; /&amp;gt;
   &amp;lt;to uri=&amp;quot;file:C:/data/kraken/instance3&amp;quot; /&amp;gt;
  &amp;lt;/route&amp;gt;
  &amp;lt;route&amp;gt;
   &amp;lt;from uri=&amp;quot;instance4:queue:notify&amp;quot; /&amp;gt;
   &amp;lt;log message=&amp;quot;instance4:${body}&amp;quot; /&amp;gt;
   &amp;lt;to uri=&amp;quot;file:C:/data/kraken/instance4&amp;quot; /&amp;gt;
  &amp;lt;/route&amp;gt;
 &amp;lt;/camelContext&amp;gt;
&amp;lt;/beans&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
И код балансировщика, который отправляет 1000 сообщений, также на Apache Camel.
&lt;pre&gt;&lt;code class=&quot;language-xml&quot;&gt;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot;?&amp;gt;
&amp;lt;beans xmlns=&amp;quot;http://www.springframework.org/schema/beans&amp;quot;
 xmlns:xsi=&amp;quot;http://www.w3.org/2001/XMLSchema-instance&amp;quot; xmlns:camel=&amp;quot;http://camel.apache.org/schema/spring&amp;quot;
 xmlns:osgi=&amp;quot;http://www.springframework.org/schema/osgi&amp;quot;
 xsi:schemaLocation=&amp;quot;
       http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/osgi http://www.springframework.org/schema/osgi/spring-osgi.xsd
       http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd&amp;quot;&amp;gt;

 &amp;lt;bean id=&amp;quot;myDataSet&amp;quot; class=&amp;quot;org.apache.camel.component.dataset.SimpleDataSet&amp;quot;&amp;gt;
  &amp;lt;property name=&amp;quot;size&amp;quot; value=&amp;quot;1000&amp;quot; /&amp;gt;
 &amp;lt;/bean&amp;gt;

 &amp;lt;camelContext xmlns=&amp;quot;http://camel.apache.org/schema/spring&amp;quot;&amp;gt;
  &amp;lt;route&amp;gt;
   &amp;lt;from uri=&amp;quot;dataset:myDataSet?produceDelay=-1&amp;quot; /&amp;gt;
   &amp;lt;loadBalance&amp;gt;
    &amp;lt;roundRobin/&amp;gt;
    &amp;lt;to uri=&amp;quot;http://localhost:8191/myapp/myservice&amp;quot; /&amp;gt;
    &amp;lt;to uri=&amp;quot;http://localhost:8192/myapp/myservice&amp;quot; /&amp;gt;
    &amp;lt;to uri=&amp;quot;http://localhost:8193/myapp/myservice&amp;quot; /&amp;gt;
    &amp;lt;to uri=&amp;quot;http://localhost:8194/myapp/myservice&amp;quot; /&amp;gt;
   &amp;lt;/loadBalance&amp;gt;
  &amp;lt;/route&amp;gt;
 &amp;lt;/camelContext&amp;gt;
 
&amp;lt;/beans&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jser.blogspot.com/feeds/5435624180823374889/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/37848572/5435624180823374889' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/37848572/posts/default/5435624180823374889'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/37848572/posts/default/5435624180823374889'/><link rel='alternate' type='text/html' href='http://jser.blogspot.com/2014/04/activemq-masterslave-and-load-balancer.html' title='ActiveMQ Master/Slave and load balancer cluster'/><author><name>JSer</name><uri>http://www.blogger.com/profile/17455716060338444232</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjZFbcs3LQmgDA_Vb57UkS9Gx5TF5ONyRq5_Nq7x24Trk5uUS1FQ5-NJUMOhMIQnvpreur7EOQKf9WpW5F_sVIoBKVmmGUKtM3UYssQzFv3CpG43-c866OWefixHMyVuTlypMXk/s72-c/Kraken4.png" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-37848572.post-4019447359324397147</id><published>2014-04-24T17:57:00.002+10:00</published><updated>2014-04-29T17:02:02.376+10:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="ActiveMQ"/><category scheme="http://www.blogger.com/atom/ns#" term="Apache Camel"/><category scheme="http://www.blogger.com/atom/ns#" term="cluster"/><category scheme="http://www.blogger.com/atom/ns#" term="ServiceMix"/><title type='text'>ActiveMQ Master/Slave and Broker Networks cluster</title><content type='html'>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
В предыдущей статье мы рассмотрели высоко-нагруженый кластер ActiveMQ для распределения нагрузки. Однако эта конфигурация все таки обладает следующим недостатком. При выходе из строя одного из узлов ActiveMQ, сообщения размещенные в нем становятся более недоступны, и будут вычитаны только после того как конкретный инстанс ActiveMQ будет снова поднят. Проблема может быть решена с помощью конфигурации Master/Slave, в которой два, или больше инстанса ActiveMQ имеют общее хранилище. Пара Master/Slave может быть связана с аналогичной парой, размещенной на другом хосте, и связаны с помощью &amp;nbsp;Broker Networks. Схематично это выглядит следующим образом.&lt;br /&gt;
Для простоты развертывания все производится на одной машине. Инстансы ActiveMQ инстансы разделены по портам.&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiykItYg8xKJzaAZlPE5QhYMsOLSkq2YvES5pXX1PgdH4qA7zd-vTPiH0iy6CN995oGQKTiILIA47VEVyyFnqNu8kMXJ6WD0aoyZlF5pjtCZR_WamW6Yum-NfxYZ_EE3hLWF13h/s1600/Kraren2.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiykItYg8xKJzaAZlPE5QhYMsOLSkq2YvES5pXX1PgdH4qA7zd-vTPiH0iy6CN995oGQKTiILIA47VEVyyFnqNu8kMXJ6WD0aoyZlF5pjtCZR_WamW6Yum-NfxYZ_EE3hLWF13h/s1600/Kraren2.png&quot; height=&quot;201&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
Ниже будут представлены настройки каждого из узлов кластера&lt;br /&gt;
&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;
instance1:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;&lt;code class=&quot;language-xml&quot;&gt;&amp;lt;networkConnectors&amp;gt;
 &amp;lt;networkConnector name=&quot;instance1-instance3-instance4&quot; uri=&quot;masterslave:(tcp://localhost:61619,tcp://localhost:61620)&quot; /&amp;gt;
&amp;lt;/networkConnectors&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-xml&quot;&gt;&amp;lt;persistenceAdapter&amp;gt;
 &amp;lt;kahaDB directory=&quot;${activemq.data}/../../../kahadb1&quot; /&amp;gt;
&amp;lt;/persistenceAdapter&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
instance2:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;&lt;code class=&quot;language-xml&quot;&gt;&amp;lt;networkConnectors&amp;gt;
 &amp;lt;networkConnector name=&quot;instance2-instance3-instance4&quot; uri=&quot;masterslave:(tcp://localhost:61619,tcp://localhost:61620)&quot; /&amp;gt;
&amp;lt;/networkConnectors&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-xml&quot;&gt;&amp;lt;persistenceAdapter&amp;gt;
 &amp;lt;kahaDB directory=&quot;${activemq.data}/../../../kahadb1&quot; /&amp;gt;
&amp;lt;/persistenceAdapter&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
instance3:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;&lt;code class=&quot;language-xml&quot;&gt;&amp;lt;networkConnectors&amp;gt;
 &amp;lt;networkConnector name=&quot;instance3-instance1-instance2&quot; uri=&quot;masterslave:(tcp://localhost:61617,tcp://localhost:61618)&quot; /&amp;gt;
&amp;lt;/networkConnectors&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-xml&quot;&gt;&amp;lt;persistenceAdapter&amp;gt;
 &amp;lt;kahaDB directory=&quot;${activemq.data}/../../../kahadb2&quot; /&amp;gt;
&amp;lt;/persistenceAdapter&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
instance4:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;&lt;code class=&quot;language-xml&quot;&gt;&amp;lt;networkConnectors&amp;gt;
 &amp;lt;networkConnector name=&quot;instance4-instance1-instance2&quot; uri=&quot;masterslave:(tcp://localhost:61617,tcp://localhost:61618)&quot; /&amp;gt;
&amp;lt;/networkConnectors&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-xml&quot;&gt;&amp;lt;persistenceAdapter&amp;gt;
 &amp;lt;kahaDB directory=&quot;${activemq.data}/../../../kahadb2&quot; /&amp;gt;
&amp;lt;/persistenceAdapter&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
Обратите внимание:&lt;br /&gt;
&lt;br /&gt;
&lt;ul style=&quot;text-align: left;&quot;&gt;
&lt;li&gt;instance1 и instance2 настроены на одну директорию kahadb1, и связаны с помощью networkConnectors с instance3 и instance4.&lt;/li&gt;
&lt;li&gt;instance3 и instance4 настроены на одну директорию kahadb2, и связаны с помощью networkConnectors с instance1 и instance2.&lt;/li&gt;
&lt;/ul&gt;
клиентский url в таком случае будет выглядеть так:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;failover:(tcp://machine1:masterAport,tcp://machine2:masterBport,tcp://machine1:slaveBport,tcp://machine2:slaveAport)?randomize=false&lt;/pre&gt;
&lt;br /&gt;
т.е. в нашем случае:
&lt;br /&gt;
&lt;pre&gt;failover:(tcp://localhost:61617,tcp://localhost:61619,tcp://localhost:61618,tcp://localhost:61620)?randomize=false&lt;/pre&gt;
&lt;br /&gt;&lt;/div&gt;
</content><link rel='replies' type='application/atom+xml' href='http://jser.blogspot.com/feeds/4019447359324397147/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/37848572/4019447359324397147' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/37848572/posts/default/4019447359324397147'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/37848572/posts/default/4019447359324397147'/><link rel='alternate' type='text/html' href='http://jser.blogspot.com/2014/04/activemq-masterslave-and-broker.html' title='ActiveMQ Master/Slave and Broker Networks cluster'/><author><name>JSer</name><uri>http://www.blogger.com/profile/17455716060338444232</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiykItYg8xKJzaAZlPE5QhYMsOLSkq2YvES5pXX1PgdH4qA7zd-vTPiH0iy6CN995oGQKTiILIA47VEVyyFnqNu8kMXJ6WD0aoyZlF5pjtCZR_WamW6Yum-NfxYZ_EE3hLWF13h/s72-c/Kraren2.png" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-37848572.post-4305948530727433044</id><published>2014-04-14T22:54:00.000+10:00</published><updated>2014-04-24T19:56:52.956+10:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="ActiveMQ"/><category scheme="http://www.blogger.com/atom/ns#" term="Apache Camel"/><category scheme="http://www.blogger.com/atom/ns#" term="cluster"/><category scheme="http://www.blogger.com/atom/ns#" term="ServiceMix"/><title type='text'>ActiveMQ high availability load balancer cluster</title><content type='html'>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
Рассмотрим актуальную задачу Load Balancer между инстансами ActiveMQ. Балансировка в таком случае строиться с помощью&amp;nbsp;networkConnectors. Для простоты развертывания инстансы ActiveMQ развернем на одной машине, разделение будет строиться на базе портов. Вот схематичное представление кластера:&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhT2s1P5LCjqTohHiRwY04JDNLFYY49q32OjRnwmLzRy0uRnE6LavrLVG-GDxzT_rmqdCOsvQo64zfwrUSZvFYeGbpuyDESPx4xXO4U8VtcRLBA8pO61puI5ONDZhhQdmsUSitQ/s1600/Kraken3.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhT2s1P5LCjqTohHiRwY04JDNLFYY49q32OjRnwmLzRy0uRnE6LavrLVG-GDxzT_rmqdCOsvQo64zfwrUSZvFYeGbpuyDESPx4xXO4U8VtcRLBA8pO61puI5ONDZhhQdmsUSitQ/s1600/Kraken3.png&quot; height=&quot;320&quot; width=&quot;225&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
Конфигурация каждого инстанса ActiveMQ представлена ниже:&lt;br /&gt;
&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;
instance1:
&lt;br /&gt;
&lt;pre&gt;&lt;code class=&quot;language-xml&quot;&gt;&amp;lt;networkConnectors&amp;gt;
 &amp;lt;!-- broker1 -&amp;gt; broker2 --&amp;gt;
 &amp;lt;networkConnector name=&quot;instance1-instance2&quot; uri=&quot;static:(tcp://localhost:61618)&quot; /&amp;gt;
 &amp;lt;!-- broker1 -&amp;gt; broker3 --&amp;gt;
 &amp;lt;networkConnector name=&quot;instance1-instance3&quot; uri=&quot;static:(tcp://localhost:61619)&quot; /&amp;gt;
 &amp;lt;!-- broker1 -&amp;gt; broker4 --&amp;gt;
 &amp;lt;networkConnector name=&quot;instance1-instance4&quot; uri=&quot;static:(tcp://localhost:61620)&quot; /&amp;gt;
&amp;lt;/networkConnectors&amp;gt;&lt;/code&gt;&lt;/pre&gt;
instance2:
&lt;br /&gt;
&lt;pre&gt;&lt;code class=&quot;language-xml&quot;&gt;&amp;lt;networkConnectors&amp;gt;
 &amp;lt;!-- broker2 -&amp;gt; broker1 --&amp;gt;
 &amp;lt;networkConnector name=&quot;instance2-instance1&quot; uri=&quot;static:(tcp://localhost:61617)&quot; /&amp;gt;
 &amp;lt;!-- broker2 -&amp;gt; broker3 --&amp;gt;
 &amp;lt;networkConnector name=&quot;instance2-instance3&quot; uri=&quot;static:(tcp://localhost:61619)&quot; /&amp;gt;
 &amp;lt;!-- broker2 -&amp;gt; broker4 --&amp;gt;
 &amp;lt;networkConnector name=&quot;instance2-instance4&quot; uri=&quot;static:(tcp://localhost:61620)&quot; /&amp;gt;
&amp;lt;/networkConnectors&amp;gt;&lt;/code&gt;&lt;/pre&gt;
instance3:
&lt;br /&gt;
&lt;pre&gt;&lt;code class=&quot;language-xml&quot;&gt;&amp;lt;networkConnectors&amp;gt;
 &amp;lt;!-- broker3 -&amp;gt; broker1 --&amp;gt;
 &amp;lt;networkConnector name=&quot;instance3-instance1&quot; uri=&quot;static:(tcp://localhost:61617)&quot; /&amp;gt;
 &amp;lt;!-- broker3 -&amp;gt; broker2 --&amp;gt;
 &amp;lt;networkConnector name=&quot;instance3-instance2&quot; uri=&quot;static:(tcp://localhost:61618)&quot; /&amp;gt;
 &amp;lt;!-- broker3 -&amp;gt; broker4 --&amp;gt;
 &amp;lt;networkConnector name=&quot;instance3-instance4&quot; uri=&quot;static:(tcp://localhost:61620)&quot; /&amp;gt;
&amp;lt;/networkConnectors&amp;gt;&lt;/code&gt;&lt;/pre&gt;
instance4:
&lt;br /&gt;
&lt;pre&gt;&lt;code class=&quot;language-xml&quot;&gt;&amp;lt;networkConnectors&amp;gt;
 &amp;lt;!-- broker4 -&amp;gt; broker2 --&amp;gt;
 &amp;lt;networkConnector name=&quot;instance4-instance1&quot; uri=&quot;static:(tcp://localhost:61617)&quot; /&amp;gt;
 &amp;lt;!-- broker4 -&amp;gt; broker3 --&amp;gt;
 &amp;lt;networkConnector name=&quot;instance4-instance2&quot; uri=&quot;static:(tcp://localhost:61618)&quot; /&amp;gt;
 &amp;lt;!-- broker4 -&amp;gt; broker4 --&amp;gt;
 &amp;lt;networkConnector name=&quot;instance4-instance3&quot; uri=&quot;static:(tcp://localhost:61619)&quot; /&amp;gt;
&amp;lt;/networkConnectors&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
URL для consumer и producer будет выглядеть следующим образом:
&lt;pre&gt;failover://(tcp://localhost:61617,tcp://localhost:61619,tcp://localhost:61618,tcp://localhost:61620)?randomize=true&lt;/pre&gt;
&lt;br /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jser.blogspot.com/feeds/4305948530727433044/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/37848572/4305948530727433044' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/37848572/posts/default/4305948530727433044'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/37848572/posts/default/4305948530727433044'/><link rel='alternate' type='text/html' href='http://jser.blogspot.com/2014/04/activemq-high-availability-cluster.html' title='ActiveMQ high availability load balancer cluster'/><author><name>JSer</name><uri>http://www.blogger.com/profile/17455716060338444232</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhT2s1P5LCjqTohHiRwY04JDNLFYY49q32OjRnwmLzRy0uRnE6LavrLVG-GDxzT_rmqdCOsvQo64zfwrUSZvFYeGbpuyDESPx4xXO4U8VtcRLBA8pO61puI5ONDZhhQdmsUSitQ/s72-c/Kraken3.png" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-37848572.post-1390275284767442135</id><published>2014-04-03T20:29:00.002+10:00</published><updated>2014-04-06T22:27:16.080+10:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Apache Camel"/><category scheme="http://www.blogger.com/atom/ns#" term="JMS"/><category scheme="http://www.blogger.com/atom/ns#" term="transaction"/><title type='text'>Transactional JMS Client</title><content type='html'>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
Разберемся детально с локальными транзакциями на примере взаимодействия JMS очередей (ActiveMQ). В &lt;a href=&quot;https://access.redhat.com/site/documentation/en-US/JBoss_Fuse/6.0/html/EIP_Transaction_Guide/files/TxnDemarcation-ByMarking.html&quot;&gt;документации&lt;/a&gt;&amp;nbsp;по этому поводу говорится, что для того чтобы с route был транзакционным достаточно в начале объявить его с помощью инструкции&amp;nbsp;&lt;span style=&quot;color: #990033; font-family: &#39;liberation mono&#39;, &#39;bitstream vera mono&#39;, &#39;dejavu mono&#39;, monospace; font-size: 12px;&quot;&gt;&amp;lt;transacted/&amp;gt;&lt;/span&gt;&amp;nbsp;. Но как будет выглядеть транзакционное поведение умалчивается. Обычно в качестве примера демонстрируется подобный route.&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;&lt;code class=&quot;language-xml&quot;&gt;&amp;lt;beans xmlns=&quot;http://www.springframework.org/schema/beans&quot;
       xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;&amp;gt;

  &amp;lt;camelContext xmlns=&quot;http://camel.apache.org/schema/spring&quot;&amp;gt;
    &amp;lt;route&amp;gt;
      &amp;lt;from uri=&quot;jmstx:queue:okay&quot;/&amp;gt;
      &amp;lt;transacted/&amp;gt;
      &amp;lt;to uri=&quot;jmstx:queue:final&quot;/&amp;gt;
    &amp;lt;/route&amp;gt;
  &amp;lt;/camelContext&amp;gt;
&amp;lt;/beans&amp;gt;&lt;/code&gt;&lt;/pre&gt;
Который не демонстрирует ровным счетом ничего.&lt;br /&gt;
&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
Модифицируем код следующим образом&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;&lt;code class=&quot;language-xml&quot;&gt;&amp;lt;beans xmlns=&quot;http://www.springframework.org/schema/beans&quot;
       xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;&amp;gt;

  &amp;lt;camelContext xmlns=&quot;http://camel.apache.org/schema/spring&quot;&amp;gt;
    &amp;lt;route&amp;gt;
      &amp;lt;from uri=&quot;jmstx:queue:okay&quot;/&amp;gt;
      &amp;lt;transacted/&amp;gt;
      &amp;lt;to uri=&quot;jmstx:queue:final&quot;/&amp;gt;
      &amp;lt;bean ref=&quot;businessLogic&quot; method=&quot;business&quot;&amp;gt;
    &amp;lt;/route&amp;gt;
  &amp;lt;/camelContext&amp;gt;
&amp;lt;/beans&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
Поставим посередине вызов bean содержащей бизнес-логику. Сама бизнес-логика простейшая&lt;br /&gt;
package com.bssys.transaction_client;&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;package com.bssys.transaction_client;

import org.apache.log4j.Logger;

public class BusinessLogic {
 private Logger log = Logger.getLogger(BusinessLogic.class);
 
 public void business(String message){
  log.info(message);
  if (&quot;exception&quot;.equals(message)){
   log.info(&quot;Rollback &quot; + message);
   throw new IllegalArgumentException(&quot;This is forced&quot;);
  }
 }
}&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
Задумка такова, что при отправке сообщения содержащего строку &quot;exception&quot; поднимается исключение, которое должно вызвать откат транзакции. Для того чтобы вся эта магия заработала необходимо настроить транзакции. Все транзакционные артефакты я описал в отдельном spring-контексте.&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;&lt;code class=&quot;language-xml&quot;&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&amp;gt;
&amp;lt;beans xmlns=&quot;http://www.springframework.org/schema/beans&quot;
 xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot; xmlns:osgi=&quot;http://www.springframework.org/schema/osgi&quot;
 xsi:schemaLocation=&quot;
       http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/osgi http://www.springframework.org/schema/osgi/spring-osgi.xsd&quot;&amp;gt;

 &amp;lt;osgi:reference id=&quot;connectionFactory&quot; interface=&quot;javax.jms.ConnectionFactory&quot; /&amp;gt;
 &amp;lt;osgi:reference id=&quot;platformTransactionManager&quot; interface=&quot;org.springframework.transaction.PlatformTransactionManager&quot; /&amp;gt;

 &amp;lt;bean id=&quot;jmsConfig&quot; class=&quot;org.apache.camel.component.jms.JmsConfiguration&quot;&amp;gt;
  &amp;lt;property name=&quot;connectionFactory&quot; ref=&quot;connectionFactory&quot; /&amp;gt;
  &amp;lt;property name=&quot;transactionManager&quot; ref=&quot;platformTransactionManager&quot; /&amp;gt;
  &amp;lt;property name=&quot;transacted&quot; value=&quot;true&quot; /&amp;gt;
 &amp;lt;/bean&amp;gt;

 &amp;lt;bean id=&quot;jmstx&quot; class=&quot;org.apache.camel.component.jms.JmsComponent&quot;&amp;gt;
  &amp;lt;constructor-arg ref=&quot;jmsConfig&quot; /&amp;gt;
 &amp;lt;/bean&amp;gt;
 
 &amp;lt;bean id=&quot;PROPAGATION_REQUIRED&quot; class=&quot;org.apache.camel.spring.spi.SpringTransactionPolicy&quot;&amp;gt;
  &amp;lt;property name=&quot;transactionManager&quot; ref=&quot;platformTransactionManager&quot; /&amp;gt;
  &amp;lt;property name=&quot;propagationBehaviorName&quot; value=&quot;PROPAGATION_REQUIRED&quot; /&amp;gt;
 &amp;lt;/bean&amp;gt;
 
 &amp;lt;bean id=&quot;PROPAGATION_REQUIRES_NEW&quot; class=&quot;org.apache.camel.spring.spi.SpringTransactionPolicy&quot;&amp;gt;
  &amp;lt;property name=&quot;transactionManager&quot; ref=&quot;platformTransactionManager&quot; /&amp;gt;
  &amp;lt;property name=&quot;propagationBehaviorName&quot; value=&quot;PROPAGATION_REQUIRES_NEW&quot; /&amp;gt;
 &amp;lt;/bean&amp;gt;

&amp;lt;/beans&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
Попробуем запустить и проверить. Модуль я запускаю на ServiceMix 5.0.0 последней на данный момент версии. Отправив сообщение &quot;exception&quot; в очередь okay. В логах видим следующее сообщение, повторяющееся некоторое количество раз.&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;&lt;code class=&quot;language-xml&quot;&gt;12:51:52,683 | INFO  | msConsumer[okay] | BusinessLogic                    | 205 - transaction-client - 0.0.1 | exception
12:51:52,684 | INFO  | msConsumer[okay] | BusinessLogic                    | 205 - transaction-client - 0.0.1 | Rollback exception
12:51:52,685 | ERROR | msConsumer[okay] | DefaultErrorHandler              | 110 - org.apache.camel.camel-core - 2.12.3 | Failed delivery for (MessageId: ID:NovA-63543-1396509662689-1:3:1:1:2 on ExchangeId: ID-NovA-63522-1396509447471-9-1). Exhausted after delivery attempt: 1 caught: java.lang.IllegalArgumentException: This is forced

Message History
---------------------------------------------------------------------------------------------------------------------------------------
RouteId              ProcessorId          Processor                                                                        Elapsed (ms)
[route15           ] [route15           ] [jmstx://queue:okay                                                            ] [         3]
[route15           ] [transacted15      ] [transacted[]                                                                  ] [         3]
[route15           ] [to21              ] [jmstx:queue:final                                                             ] [         1]
[route15           ] [bean8             ] [bean[ref:businessLogic method: business]                                      ] [         2]

Exchange
---------------------------------------------------------------------------------------------------------------------------------------
Exchange[
 Id                  ID-NovA-63522-1396509447471-9-1
 ExchangePattern     InOnly
 Headers             {breadcrumbId=ID:NovA-63543-1396509662689-1:3:1:1:2, CamelRedelivered=false, CamelRedeliveryCounter=0, JMSCorrelationID=, JMSDeliveryMode=1, JMSDestination=queue://okay, JMSExpiration=0, JMSMessageID=ID:NovA-63543-1396509662689-1:3:1:1:2, JMSPriority=0, JMSRedelivered=false, JMSReplyTo=null, JMSTimestamp=1396515112680, JMSType=, JMSXGroupID=null, JMSXUserID=null}
 BodyType            String
 Body                exception
]&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
Сообщение действительно не попало в очередь final. Но это ли мы действительно хотели? В результате сообщение не находится ни в одной из очередей. Т.е. сообщение пропало! Ищите в логах. Это просто не допустимо. Итого корректное поведение, которого я хочу добиться следующее.&lt;br /&gt;
При возникновении исключения сообщение должно откатываться во всех системах, и для того чтобы оно не потерялось перекладываться в специальную очередь для ошибок. Иными словами, нам необходимо реализовать специльный паттерн для таких случаев &quot;Dead letter chanel&quot;. Судя по документации есть несколько путей чтобы реализовать подобное.&lt;br /&gt;
Например в некоторых местах &lt;a href=&quot;http://fusesource.com/docs/router/2.2/transactions/TxnDemarcation-ErrorHandling.html&quot;&gt;документации&lt;/a&gt;&amp;nbsp;предлагают такой вариант:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;// Java
import org.apache.camel.spring.SpringRouteBuilder;

public class MyRouteBuilder extends SpringRouteBuilder {
    ...
    public void configure() {
        onException(IllegalArgumentException.class).
            maximumRedeliveries(1)
            .handled(true)
            .to(&quot;file:target/messages?fileName=deadLetters.xml&amp;amp;fileExist=Append&quot;)
            .markRollbackOnly();  // NB: Must come *after* the dead letter endpoint.

        from(&quot;file:src/data?noop=true&quot;)
            .transacted()
            .beanRef(&quot;accountService&quot;,&quot;credit&quot;)
            .beanRef(&quot;accountService&quot;,&quot;debit&quot;)
            .beanRef(&quot;accountService&quot;,&quot;dumpTable&quot;)
            .to(&quot;file:target/messages&quot;);
    }
}&lt;/code&gt;&lt;/pre&gt;
Попробуем адаптировать этот пример ближе к нам. Таким образом это будет выглядеть следующим образом:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;&lt;code class=&quot;language-xml&quot;&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&amp;gt;
&amp;lt;beans xmlns=&quot;http://www.springframework.org/schema/beans&quot;
 xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot; xmlns:camel=&quot;http://camel.apache.org/schema/spring&quot;
 xmlns:osgi=&quot;http://www.springframework.org/schema/osgi&quot;
 xsi:schemaLocation=&quot;
       http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/osgi http://www.springframework.org/schema/osgi/spring-osgi.xsd
       http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd&quot;&amp;gt;

 &amp;lt;bean id=&quot;businessLogic&quot; class=&quot;com.bssys.transaction_client.BusinessLogic&quot; /&amp;gt;

 &amp;lt;camelContext xmlns=&quot;http://camel.apache.org/schema/spring&quot;&amp;gt;

  &amp;lt;route&amp;gt;
   &amp;lt;!-- 1: from the jms queue --&amp;gt;
   &amp;lt;from uri=&quot;jmstx:queue:okay&quot; /&amp;gt;
   &amp;lt;onException&amp;gt;
    &amp;lt;exception&amp;gt;java.lang.IllegalArgumentException&amp;lt;/exception&amp;gt;
    &amp;lt;redeliveryPolicy maximumRedeliveries=&quot;1&quot;/&amp;gt;
    &amp;lt;handled&amp;gt;
     &amp;lt;constant&amp;gt;true&amp;lt;/constant&amp;gt;
    &amp;lt;/handled&amp;gt;
    &amp;lt;log message=&quot;${exception.message}&quot; /&amp;gt;
    &amp;lt;to uri=&quot;file:target/messages?fileName=deadLetters.xml&amp;amp;amp;fileExist=Append&quot; /&amp;gt;
    &amp;lt;rollback markRollbackOnly=&quot;true&quot; /&amp;gt;
   &amp;lt;/onException&amp;gt;
   &amp;lt;transacted/&amp;gt;
   &amp;lt;to uri=&quot;jmstx:queue:final&quot; /&amp;gt;
   &amp;lt;bean ref=&quot;businessLogic&quot; method=&quot;business&quot; /&amp;gt;
  &amp;lt;/route&amp;gt;
  
 &amp;lt;/camelContext&amp;gt;

&amp;lt;/beans&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
Да, действительно это похоже на то что нам надо, но только с одной оговоркой. в качестве dead letter chanel, сообщение скидывается в текстовый файл. Но нам то надо перекладывать его в отдельную очередь. Адаптируем пример, где в качестве dead letter queue, будет использоваться отдельная очередь.&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;&lt;code class=&quot;language-xml&quot;&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&amp;gt;
&amp;lt;beans xmlns=&quot;http://www.springframework.org/schema/beans&quot;
 xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot; xmlns:camel=&quot;http://camel.apache.org/schema/spring&quot;
 xmlns:osgi=&quot;http://www.springframework.org/schema/osgi&quot;
 xsi:schemaLocation=&quot;
       http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/osgi http://www.springframework.org/schema/osgi/spring-osgi.xsd
       http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd&quot;&amp;gt;

 &amp;lt;bean id=&quot;businessLogic&quot; class=&quot;com.bssys.transaction_client.BusinessLogic&quot; /&amp;gt;

 &amp;lt;camelContext xmlns=&quot;http://camel.apache.org/schema/spring&quot;&amp;gt;

  &amp;lt;route&amp;gt;
   &amp;lt;!-- 1: from the jms queue --&amp;gt;
   &amp;lt;from uri=&quot;jmstx:queue:okay&quot; /&amp;gt;
   &amp;lt;onException&amp;gt;
    &amp;lt;exception&amp;gt;java.lang.IllegalArgumentException&amp;lt;/exception&amp;gt;
    &amp;lt;camel:redeliveryPolicy maximumRedeliveries=&quot;1&quot;/&amp;gt;
    &amp;lt;handled&amp;gt;
     &amp;lt;constant&amp;gt;true&amp;lt;/constant&amp;gt;
    &amp;lt;/handled&amp;gt;
    &amp;lt;log message=&quot;${exception.message}&quot; /&amp;gt;
    &amp;lt;to uri=&quot;jmstx:queue:bad&quot; /&amp;gt;
    &amp;lt;rollback markRollbackOnly=&quot;true&quot; /&amp;gt;
   &amp;lt;/onException&amp;gt;
   &amp;lt;!-- &amp;lt;transacted ref=&quot;PROPAGATION_REQUIRED&quot; /&amp;gt; --&amp;gt;
   &amp;lt;transacted/&amp;gt;
   &amp;lt;to uri=&quot;jmstx:queue:final&quot; /&amp;gt;
   &amp;lt;bean ref=&quot;businessLogic&quot; method=&quot;business&quot; /&amp;gt;
  &amp;lt;/route&amp;gt;
 &amp;lt;/camelContext&amp;gt;

&amp;lt;/beans&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
Запустив мы увидим в логах следующее&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;&lt;code class=&quot;language-xml&quot;&gt;13:46:09,301 | INFO  | msConsumer[okay] | BusinessLogic                    | 207 - transaction-client - 0.0.1 | exception
13:46:09,302 | INFO  | msConsumer[okay] | BusinessLogic                    | 207 - transaction-client - 0.0.1 | Rollback exception
13:46:09,302 | INFO  | msConsumer[okay] | BusinessLogic                    | 207 - transaction-client - 0.0.1 | exception
13:46:09,303 | INFO  | msConsumer[okay] | BusinessLogic                    | 207 - transaction-client - 0.0.1 | Rollback exception
13:46:09,303 | INFO  | msConsumer[okay] | route5                           | 110 - org.apache.camel.camel-core - 2.12.3 | This is forced
13:46:09,305 | WARN  | msConsumer[okay] | DefaultErrorHandler              | 110 - org.apache.camel.camel-core - 2.12.3 | Rollback (MessageId: ID:NovA-64390-1396518019146-1:1:1:1:3 on ExchangeId: ID-NovA-64373-1396518004181-2-1) due: This is forced

Message History
---------------------------------------------------------------------------------------------------------------------------------------
RouteId              ProcessorId          Processor                                                                        Elapsed (ms)
[route5            ] [route5            ] [jmstx://queue:okay                                                            ] [         5]
[route5            ] [transacted5       ] [transacted[]                                                                  ] [         5]
[route5            ] [to8               ] [jmstx:queue:final                                                             ] [         1]
[route5            ] [bean3             ] [bean[ref:businessLogic method: business]                                      ] [         4]
[route5            ] [log3              ] [log                                                                           ] [         0]
[route5            ] [to7               ] [jmstx:queue:bad                                                               ] [         1]
[route5            ] [rollback3         ] [rollback                                                                      ] [         0]

Exchange
---------------------------------------------------------------------------------------------------------------------------------------
Exchange[
 Id                  ID-NovA-64373-1396518004181-2-1
 ExchangePattern     InOnly
 Headers             {breadcrumbId=ID:NovA-64390-1396518019146-1:1:1:1:3, JMSCorrelationID=, JMSDeliveryMode=1, JMSDestination=queue://okay, JMSExpiration=0, JMSMessageID=ID:NovA-64390-1396518019146-1:1:1:1:3, JMSPriority=0, JMSRedelivered=false, JMSReplyTo=null, JMSTimestamp=1396518369297, JMSType=, JMSXGroupID=null, JMSXUserID=null}
 BodyType            String
 Body                exception
]

13:46:09,305 | WARN  | msConsumer[okay] | TransactionErrorHandler          | 110 - org.apache.camel.camel-core - 2.12.3 | Transaction rollback (0x2b0ef227) redelivered(false) for (MessageId: ID:NovA-64390-1396518019146-1:1:1:1:3 on ExchangeId: ID-NovA-64373-1396518004181-2-1) due exchange was marked for rollbackOnly
13:46:09,305 | WARN  | msConsumer[okay] | EndpointMessageListener          | 110 - org.apache.camel.camel-core - 2.12.3 | Execution of JMS message listener failed. Caused by: [org.apache.camel.RuntimeCamelException - org.apache.camel.RollbackExchangeException: Intended rollback. Exchange[JmsMessage[JmsMessageID: ID:NovA-64390-1396518019146-1:1:1:1:3]]]&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
По факту сообщение попало в очередь jmstx:queue:bad, но было отменено по rollback! Это совсем никуда не годится! Для того чтобы сообщение не откатывалось из очереди ошибок, отправку необходимо выполнять в отдельной транзакции. При этом надо настроить транзакции так, чтобы не было двойной переотправки. Дело в том что помимо повторений на стороне Camel, переотправкой сообщений будет также заниматься transactionManager.&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;&lt;code class=&quot;language-xml&quot;&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&amp;gt;
&amp;lt;beans xmlns=&quot;http://www.springframework.org/schema/beans&quot;
 xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot; xmlns:osgi=&quot;http://www.springframework.org/schema/osgi&quot;
 xsi:schemaLocation=&quot;
       http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/osgi http://www.springframework.org/schema/osgi/spring-osgi.xsd&quot;&amp;gt;

 &amp;lt;osgi:reference id=&quot;platformTransactionManager&quot;
  interface=&quot;org.springframework.transaction.PlatformTransactionManager&quot; /&amp;gt;

  &amp;lt;bean id=&quot;activemqConnectionFactory&quot; class=&quot;org.apache.activemq.ActiveMQConnectionFactory&quot;&amp;gt;
  &amp;lt;property name=&quot;brokerURL&quot; value=&quot;tcp://localhost:61616&quot; /&amp;gt;
  &amp;lt;property name=&quot;userName&quot; value=&quot;karaf&quot; /&amp;gt;
  &amp;lt;property name=&quot;password&quot; value=&quot;karaf&quot; /&amp;gt;
  &amp;lt;property name=&quot;redeliveryPolicy&quot;&amp;gt;
   &amp;lt;bean class=&quot;org.apache.activemq.RedeliveryPolicy&quot;&amp;gt;
    &amp;lt;property name=&quot;maximumRedeliveries&quot; value=&quot;0&quot; /&amp;gt;
   &amp;lt;/bean&amp;gt;
  &amp;lt;/property&amp;gt;
 &amp;lt;/bean&amp;gt;
 
 &amp;lt;bean id=&quot;pooledConnectionFactory&quot; class=&quot;org.apache.activemq.pool.PooledConnectionFactory&quot;&amp;gt;
        &amp;lt;property name=&quot;maxConnections&quot; value=&quot;8&quot;/&amp;gt;
        &amp;lt;property name=&quot;connectionFactory&quot; ref=&quot;activemqConnectionFactory&quot;/&amp;gt;
    &amp;lt;/bean&amp;gt;

 &amp;lt;bean id=&quot;jmsConfig&quot; class=&quot;org.apache.camel.component.jms.JmsConfiguration&quot;&amp;gt;
  &amp;lt;property name=&quot;connectionFactory&quot; ref=&quot;pooledConnectionFactory&quot; /&amp;gt;
  &amp;lt;property name=&quot;transactionManager&quot; ref=&quot;platformTransactionManager&quot; /&amp;gt;
  &amp;lt;property name=&quot;transacted&quot; value=&quot;true&quot; /&amp;gt;
 &amp;lt;/bean&amp;gt;

 &amp;lt;bean id=&quot;jmstx&quot; class=&quot;org.apache.camel.component.jms.JmsComponent&quot;&amp;gt;
  &amp;lt;constructor-arg ref=&quot;jmsConfig&quot; /&amp;gt;
 &amp;lt;/bean&amp;gt;

 &amp;lt;bean id=&quot;PROPAGATION_REQUIRED&quot; class=&quot;org.apache.camel.spring.spi.SpringTransactionPolicy&quot;&amp;gt;
  &amp;lt;property name=&quot;transactionManager&quot; ref=&quot;platformTransactionManager&quot; /&amp;gt;
  &amp;lt;property name=&quot;propagationBehaviorName&quot; value=&quot;PROPAGATION_REQUIRED&quot; /&amp;gt;
 &amp;lt;/bean&amp;gt;
 &amp;lt;bean id=&quot;PROPAGATION_REQUIRES_NEW&quot; class=&quot;org.apache.camel.spring.spi.SpringTransactionPolicy&quot;&amp;gt;
  &amp;lt;property name=&quot;transactionManager&quot; ref=&quot;platformTransactionManager&quot; /&amp;gt;
  &amp;lt;property name=&quot;propagationBehaviorName&quot; value=&quot;PROPAGATION_REQUIRES_NEW&quot; /&amp;gt;
 &amp;lt;/bean&amp;gt;

&amp;lt;/beans&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
&amp;nbsp;Итак вот итоговый результат!&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;&lt;code class=&quot;language-xml&quot;&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&amp;gt;
&amp;lt;beans xmlns=&quot;http://www.springframework.org/schema/beans&quot;
 xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot; xmlns:camel=&quot;http://camel.apache.org/schema/spring&quot;
 xmlns:osgi=&quot;http://www.springframework.org/schema/osgi&quot;
 xsi:schemaLocation=&quot;
       http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/osgi http://www.springframework.org/schema/osgi/spring-osgi.xsd
       http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd&quot;&amp;gt;

 &amp;lt;bean id=&quot;businessLogic&quot; class=&quot;com.bssys.transaction_client.BusinessLogic&quot; /&amp;gt;

 &amp;lt;camelContext xmlns=&quot;http://camel.apache.org/schema/spring&quot;&amp;gt;

  &amp;lt;route&amp;gt;
   &amp;lt;!-- 1: from the jms queue --&amp;gt;
   &amp;lt;from uri=&quot;jmstx:queue:okay&quot; /&amp;gt;
   &amp;lt;onException&amp;gt;
    &amp;lt;exception&amp;gt;java.lang.IllegalArgumentException&amp;lt;/exception&amp;gt;
    &amp;lt;camel:redeliveryPolicy maximumRedeliveries=&quot;3&quot; redeliveryDelay=&quot;1000&quot;/&amp;gt;
    &amp;lt;handled&amp;gt;
     &amp;lt;constant&amp;gt;true&amp;lt;/constant&amp;gt;
    &amp;lt;/handled&amp;gt;
    &amp;lt;log message=&quot;${exception.message}&quot; /&amp;gt;
    &amp;lt;to uri=&quot;direct:dead&quot; /&amp;gt;
    &amp;lt;rollback markRollbackOnly=&quot;true&quot; /&amp;gt;
   &amp;lt;/onException&amp;gt;
   &amp;lt;transacted/&amp;gt;
   &amp;lt;to uri=&quot;jmstx:queue:final&quot; /&amp;gt;
   &amp;lt;bean ref=&quot;businessLogic&quot; method=&quot;business&quot; /&amp;gt;
  &amp;lt;/route&amp;gt;

  &amp;lt;route&amp;gt;
   &amp;lt;camel:from uri=&quot;direct:dead&quot; /&amp;gt;
   &amp;lt;transacted ref=&quot;PROPAGATION_REQUIRES_NEW&quot; /&amp;gt;
   &amp;lt;to uri=&quot;jmstx:queue:bad&quot; /&amp;gt;
  &amp;lt;/route&amp;gt;
 &amp;lt;/camelContext&amp;gt;

&amp;lt;/beans&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
В примере делается 3 попытки с интервалом 1сек, и в конечном итоге сообщение попадает в очередь ошибок.&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;&lt;code class=&quot;language-xml&quot;&gt;14:07:36,765 | INFO  | msConsumer[okay] | BusinessLogic                    | 207 - transaction-client - 0.0.1 | exception
14:07:36,766 | INFO  | msConsumer[okay] | BusinessLogic                    | 207 - transaction-client - 0.0.1 | Rollback exception
14:07:37,766 | INFO  | msConsumer[okay] | BusinessLogic                    | 207 - transaction-client - 0.0.1 | exception
14:07:37,766 | INFO  | msConsumer[okay] | BusinessLogic                    | 207 - transaction-client - 0.0.1 | Rollback exception
14:07:38,767 | INFO  | msConsumer[okay] | BusinessLogic                    | 207 - transaction-client - 0.0.1 | exception
14:07:38,767 | INFO  | msConsumer[okay] | BusinessLogic                    | 207 - transaction-client - 0.0.1 | Rollback exception
14:07:39,767 | INFO  | msConsumer[okay] | BusinessLogic                    | 207 - transaction-client - 0.0.1 | exception
14:07:39,767 | INFO  | msConsumer[okay] | BusinessLogic                    | 207 - transaction-client - 0.0.1 | Rollback exception
14:07:39,768 | INFO  | msConsumer[okay] | route9                           | 110 - org.apache.camel.camel-core - 2.12.3 | This is forced
14:07:39,826 | WARN  | msConsumer[okay] | DefaultErrorHandler              | 110 - org.apache.camel.camel-core - 2.12.3 | Rollback (MessageId: ID:NovA-64390-1396518019146-1:2:1:1:2 on ExchangeId: ID-NovA-64373-1396518004181-4-1) due: This is forced

Message History
---------------------------------------------------------------------------------------------------------------------------------------
RouteId              ProcessorId          Processor                                                                        Elapsed (ms)
[route9            ] [route9            ] [jmstx://queue:okay                                                            ] [      3062]
[route9            ] [transacted9       ] [transacted[]                                                                  ] [      3062]
[route9            ] [to14              ] [jmstx:queue:final                                                             ] [         1]
[route9            ] [bean5             ] [bean[ref:businessLogic method: business]                                      ] [      3061]
[route9            ] [log5              ] [log                                                                           ] [         0]
[route9            ] [to13              ] [direct:dead                                                                   ] [        57]
[route10           ] [transacted10      ] [transacted[ref:PROPAGATION_REQUIRES_NEW]                                      ] [        57]
[route10           ] [to15              ] [jmstx:queue:bad                                                               ] [         3]
[route9            ] [rollback5         ] [rollback                                                                      ] [         0]

Exchange
---------------------------------------------------------------------------------------------------------------------------------------
Exchange[
 Id                  ID-NovA-64373-1396518004181-4-1
 ExchangePattern     InOnly
 Headers             {breadcrumbId=ID:NovA-64390-1396518019146-1:2:1:1:2, JMSCorrelationID=, JMSDeliveryMode=1, JMSDestination=queue://okay, JMSExpiration=0, JMSMessageID=ID:NovA-64390-1396518019146-1:2:1:1:2, JMSPriority=0, JMSRedelivered=false, JMSReplyTo=null, JMSTimestamp=1396519656761, JMSType=, JMSXGroupID=null, JMSXUserID=null}
 BodyType            String
 Body                exception
]

14:07:39,826 | WARN  | msConsumer[okay] | TransactionErrorHandler          | 110 - org.apache.camel.camel-core - 2.12.3 | Transaction rollback (0x2b75718c) redelivered(false) for (MessageId: ID:NovA-64390-1396518019146-1:2:1:1:2 on ExchangeId: ID-NovA-64373-1396518004181-4-1) due exchange was marked for rollbackOnly
14:07:39,827 | WARN  | msConsumer[okay] | EndpointMessageListener          | 110 - org.apache.camel.camel-core - 2.12.3 | Execution of JMS message listener failed. Caused by: [org.apache.camel.RuntimeCamelException - org.apache.camel.RollbackExchangeException: Intended rollback. Exchange[JmsMessage[JmsMessageID: ID:NovA-64390-1396518019146-1:2:1:1:2]]]
&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
Весь код тут: git clone&amp;nbsp;https://Hibernate2009@bitbucket.org/Hibernate2009/transaction-jms-client.git&lt;/div&gt;
</content><link rel='replies' type='application/atom+xml' href='http://jser.blogspot.com/feeds/1390275284767442135/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/37848572/1390275284767442135' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/37848572/posts/default/1390275284767442135'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/37848572/posts/default/1390275284767442135'/><link rel='alternate' type='text/html' href='http://jser.blogspot.com/2014/04/transactional-jms-client.html' title='Transactional JMS Client'/><author><name>JSer</name><uri>http://www.blogger.com/profile/17455716060338444232</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-37848572.post-8180979445876113618</id><published>2014-03-17T22:48:00.002+10:00</published><updated>2014-04-14T23:04:03.432+10:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Apache Camel"/><category scheme="http://www.blogger.com/atom/ns#" term="CXF"/><category scheme="http://www.blogger.com/atom/ns#" term="Rest"/><category scheme="http://www.blogger.com/atom/ns#" term="ServiceMix"/><title type='text'>RESTFul with Apache Camel</title><content type='html'>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
После длительного перерыва, рассмотрим как реализовать с помощью Apache Camel простейший REST-сервис. Реализовать подобное с помощью Apache Camel можно навскидку 3-мя способами. Мне как раз был необходим сервис по хранению и управлению настройками в формате key=value. REST необходим чтобы в дальнейшем сделать на AngularJS страницу для управления настройками.&lt;br /&gt;
Итак Apache Camel предоставляет следующие возможности для реализации REST сервиса&lt;br /&gt;
&lt;ol style=&quot;text-align: left;&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://camel.apache.org/cxf-bean-component.html&quot;&gt;CXF Bean Component&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://camel.apache.org/restlet.html&quot;&gt;Restlet Component&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://camel.apache.org/cxfrs.html&quot;&gt;CXFRS Component&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
CXF Bean Component и CXFRS Component очень похожи друг на друга, и строятся на основе отдельного java-bean помеченного специальными аннотациями, и отличаются лишь обвязкой и инициализацией.Лично меня привлек CXF Bean Component который поддерживает из коробки одновременно два стандарта&amp;nbsp;JAXRS, JAXWS. Что позволяет управлять выводом XML-JSON, плюс есть возможность использовать разработанный код для реализации SOAP WebService.&lt;br /&gt;
Сам код на Apache Camel очень лаконичен:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;&lt;code class=&quot;language-xml&quot;&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&amp;gt;
&amp;lt;beans xmlns=&quot;http://www.springframework.org/schema/beans&quot;
 xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot; xmlns:camel=&quot;http://camel.apache.org/schema/spring&quot;
 xmlns:osgi=&quot;http://www.springframework.org/schema/osgi&quot; xmlns:context=&quot;http://www.springframework.org/schema/context&quot;
 xsi:schemaLocation=&quot;
       http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/osgi http://www.springframework.org/schema/osgi/spring-osgi.xsd
       http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd
       http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd&quot;&amp;gt;

 &amp;lt;context:component-scan base-package=&quot;com.bssys.*&quot; /&amp;gt;
 
 &amp;lt;bean id=&quot;itemRestService&quot; class=&quot;com.bssys.rest_service.ItemRestService&quot; /&amp;gt;
 
 &amp;lt;camelContext xmlns=&quot;http://camel.apache.org/schema/spring&quot;&amp;gt;
  &amp;lt;route&amp;gt;
   &amp;lt;from uri=&quot;jetty:http://localhost:8787?matchOnUriPrefix=true&quot; /&amp;gt;
   &amp;lt;to uri=&quot;cxfbean:itemRestService&quot; /&amp;gt;
  &amp;lt;/route&amp;gt;
 &amp;lt;/camelContext&amp;gt;
&amp;lt;/beans&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
Детали скрываются в реализации ItemRestService. Ниже я опишу полную последовательность разработки &lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;Сам сервис я попытался сделать захватывающим разные аспекты связывания данных.&lt;br /&gt;
Общий путь к REST-сервису:&lt;br /&gt;
&lt;b&gt;http://localhost:8787/example&lt;/b&gt;&lt;br /&gt;
&lt;b&gt;GET http://localhost:8787/example/getAllItem&lt;/b&gt;&lt;br /&gt;
&lt;b&gt;GET http://localhost:8787/example/getItem/{key}&lt;/b&gt;&lt;br /&gt;
&lt;b&gt;PUT http://localhost:8787/example/updateItem&lt;/b&gt;&lt;br /&gt;
&lt;b&gt;PUT http://localhost:8787/example/addItem&lt;/b&gt;&lt;br /&gt;
&lt;b&gt;DELETE http://localhost:8787/example/removeItem/{key}&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
getAllItem- GET запрос без параметров&lt;br /&gt;
getItem/{key}- GET запрос с запросе указывающий имя получаемого свойства&lt;br /&gt;
updateItem - PUT запрос обновления свойства. В теле запроса передается JSON&lt;br /&gt;
addItem - PUT запрос добавления свойства. В теле запроса передается JSON&lt;br /&gt;
removeItem/{key} - DELETE запрос на удаление свойства.&lt;br /&gt;
&lt;br /&gt;
Изначально, для прозрачной работы с данными нам понадобится модель данных. У меня она предоставлена в виде &amp;nbsp;XSD-схемы.&lt;br /&gt;
&lt;pre&gt;&lt;code class=&quot;language-xml&quot;&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&amp;gt;
&amp;lt;xsd:schema xmlns:xsd=&quot;http://www.w3.org/2001/XMLSchema&quot;  targetNamespace=&quot;http://bssys.com/item&quot;
 xmlns:tns=&quot;http://bssys.com/item&quot; elementFormDefault=&quot;qualified&quot;&amp;gt;
 
 &amp;lt;xsd:element name=&quot;itemRequest&quot; type=&quot;tns:itemRequest&quot;/&amp;gt;
 &amp;lt;xsd:element name=&quot;itemResponse&quot; type=&quot;tns:itemResponse&quot;/&amp;gt;
 
 &amp;lt;xsd:complexType name=&quot;item&quot;&amp;gt;
  &amp;lt;xsd:sequence&amp;gt;
   &amp;lt;xsd:element name=&quot;key&quot; type=&quot;xsd:string&quot;/&amp;gt;
   &amp;lt;xsd:element name=&quot;value&quot; type=&quot;xsd:string&quot;/&amp;gt;
  &amp;lt;/xsd:sequence&amp;gt;
 &amp;lt;/xsd:complexType&amp;gt;
 
 &amp;lt;xsd:complexType name=&quot;itemRequest&quot;&amp;gt;
  &amp;lt;xsd:sequence&amp;gt;
   &amp;lt;xsd:element name=&quot;item&quot; type=&quot;tns:item&quot;/&amp;gt;
  &amp;lt;/xsd:sequence&amp;gt;
 &amp;lt;/xsd:complexType&amp;gt;
 
 &amp;lt;xsd:complexType name=&quot;itemResponse&quot;&amp;gt;
  &amp;lt;xsd:sequence&amp;gt;
   &amp;lt;xsd:element name=&quot;item&quot; minOccurs=&quot;0&quot; maxOccurs=&quot;unbounded&quot; type=&quot;tns:item&quot;/&amp;gt;
   &amp;lt;xsd:element name=&quot;errorMessage&quot; type=&quot;xsd:string&quot;/&amp;gt;
  &amp;lt;/xsd:sequence&amp;gt;
 &amp;lt;/xsd:complexType&amp;gt;
&amp;lt;/xsd:schema&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
На основе который мы сгенерируем JAXB классы. Именно знакомый нам JAXB отвечает за &quot;marshaling&quot; из разных форматов. На самом деле, без этого этапа можно запросто обойтись. XSD-схема далее применяться не будет. Конечно можно разработать нужные классы вручную, но в таком подходе сервис получается стройнее, и появляется возможность в дальнейшем переработать его в SOAP Webservice.&lt;br /&gt;
&lt;br /&gt;
После генерации имеем следующие классы:&lt;br /&gt;
&lt;ul style=&quot;text-align: left;&quot;&gt;
&lt;li&gt;Item&lt;/li&gt;
&lt;li&gt;ItemRequest&lt;/li&gt;
&lt;li&gt;ItemResponse&lt;/li&gt;
&lt;li&gt;ObjectFactory&lt;/li&gt;
&lt;/ul&gt;
Классы уже анотированы атонациями JAXB. Для возможности использования в составе REST сервиса, необходимо только добавить анотацию&amp;nbsp;@XmlRootElement в классы ItemRequest ItemResponse.&lt;br /&gt;
На их основе разработаем интерфейс нашего сервиса:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;package com.bssys.service;

import org.springframework.stereotype.Service;
import com.bssys.jaxb.model.ItemRequest;
import com.bssys.jaxb.model.ItemResponse;

@Service
public interface ItemService {
 public ItemResponse addItem(ItemRequest request);
 public ItemResponse updateItem(ItemRequest request);
 public ItemResponse getItem(String key);
 public ItemResponse getAllItem();
 public ItemResponse removeItem(String key);
 public ItemRequest stubRequest();
}&lt;/code&gt;&lt;/pre&gt;
Слой DAO:
&lt;br /&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;package com.bssys.dao;

import java.util.List;

import org.springframework.stereotype.Service;

import com.bssys.jaxb.model.Item;

@Service
public interface ItemManagerDao {
 public Item getItem(String name);
 public void deleteItem(String name);
 public List&lt;item&gt; getAllItems();
 public void insertItem(Item item);
 public void updateItem(Item item);
}&lt;/item&gt;&lt;/code&gt;&lt;/pre&gt;
Далее простейшая реализация &quot;in memory&quot;:
&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;package com.bssys.dao;

import java.util.ArrayList;
import java.util.List;

import org.springframework.stereotype.Component;

import com.bssys.jaxb.model.Item;


@Component
public class ItemManagerMemoryDao implements ItemManagerDao {
 
 private List&lt;item&gt; items = new ArrayList&lt;item&gt;();

 @Override
 public Item getItem(String name) {
  // TODO Auto-generated method stub
  for (Item item: items){
   String key = item.getKey();
   if (key.equals(name)){
    return item;
   }
  }
  throw new RuntimeException(&quot;Item Not Found: &quot; + name);
 }

 @Override
 public List&lt;item&gt; getAllItems() {
  // TODO Auto-generated method stub
  return items;
 }

 @Override
 public void insertItem(Item item) {
  // TODO Auto-generated method stub
  items.add(item);
 }

 @Override
 public void updateItem(Item item) {
  // TODO Auto-generated method stub
  Item storeItem = getItem(item.getKey());
  storeItem.setValue(item.getValue());
 }

 @Override
 public void deleteItem(String name) {
  // TODO Auto-generated method stub
  Item storeItem = getItem(name);
  items.remove(storeItem);
 }
}
&lt;/item&gt;&lt;/item&gt;&lt;/item&gt;&lt;/code&gt;&lt;/pre&gt;
И сам REST-сервис:
&lt;br /&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;package com.bssys.rest_service;

import java.util.List;

import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;

import org.springframework.beans.factory.annotation.Autowired;

import com.bssys.dao.ItemManagerMemoryDao;
import com.bssys.jaxb.model.Item;
import com.bssys.jaxb.model.ItemRequest;
import com.bssys.jaxb.model.ItemResponse;
import com.bssys.service.ItemService;

@Path(&quot;/example&quot;)
public class ItemRestService implements ItemService{
 @Autowired
 private ItemManagerMemoryDao dao;

 @PUT
 @Path(&quot;addItem&quot;)
 @Consumes(&quot;application/json&quot;)
 @Produces(&quot;application/json&quot;)
 public ItemResponse addItem(ItemRequest request) {
  // TODO Auto-generated method stub
  ItemResponse userResponse = new ItemResponse();
  try{
   dao.insertItem(request.getItem());
   userResponse.setErrorMessage(&quot;Ok&quot;);
  }catch(Exception e){
   userResponse.setErrorMessage(&quot;Error: &quot;+e.getMessage());
  }
  return userResponse;
 }

 @PUT
 @Path(&quot;updateItem&quot;)
 @Consumes(&quot;application/json&quot;)
 @Produces(&quot;application/json&quot;)
 public ItemResponse updateItem(ItemRequest request) {
  // TODO Auto-generated method stub
  ItemResponse userResponse = new ItemResponse();
  try{
   dao.updateItem(request.getItem());
   userResponse.setErrorMessage(&quot;Ok&quot;);
  }catch(Exception e){
   userResponse.setErrorMessage(&quot;Error: &quot;+e.getMessage());
  }
  return userResponse;
 }

 @GET
 @Path(&quot;getItem/{key}&quot;)
 @Produces(&quot;application/json&quot;)
 public ItemResponse getItem(@PathParam(&quot;key&quot;) String key) {
  // TODO Auto-generated method stub
  ItemResponse userResponse = new ItemResponse();
  try{
   Item item = dao.getItem(key);
   userResponse.getItem().add(item);
   userResponse.setErrorMessage(&quot;Ok&quot;);
  }catch(Exception e){
   userResponse.setErrorMessage(&quot;Error: &quot;+e.getMessage());
  }
  return userResponse;
 }

 @GET
 @Path(&quot;getAllItem&quot;)
 @Produces(&quot;application/json&quot;)
 public ItemResponse getAllItem() {
  // TODO Auto-generated method stub
  ItemResponse userResponse = new ItemResponse();
  try{
   List&lt;item&gt; allItems = dao.getAllItems();
   List&lt;item&gt; item = userResponse.getItem();
   item.addAll(allItems);
   userResponse.setErrorMessage(&quot;Ok&quot;);
  }catch(Exception e){
   userResponse.setErrorMessage(&quot;Error: &quot;+e.getMessage());
  }
  return userResponse;
 }

 @DELETE
 @Path(&quot;removeItem/{key}&quot;)
 @Consumes(&quot;application/json&quot;)
 @Produces(&quot;application/json&quot;)
 public ItemResponse removeItem(@PathParam(&quot;key&quot;) String key) {
  // TODO Auto-generated method stub
  ItemResponse userResponse = new ItemResponse();
  try{
   dao.deleteItem(key);
   userResponse.setErrorMessage(&quot;Ok&quot;);
  }catch(Exception e){
   userResponse.setErrorMessage(&quot;Error: &quot;+e.getMessage());
  }
  return userResponse;
 }

 @Override
 @GET
 @Path(&quot;stubRequest&quot;)
 @Produces(&quot;application/json&quot;)
 public ItemRequest stubRequest() {
  // TODO Auto-generated method stub
  ItemRequest itemRequest = new ItemRequest();
  Item item = new Item();
  item.setKey(&quot;key&quot;);
  item.setValue(&quot;value&quot;);
  itemRequest.setItem(item);
  return itemRequest;
 }
}&lt;/item&gt;&lt;/item&gt;&lt;/code&gt;&lt;/pre&gt;
Все это собирается как OSGI-модуль готовый к развертыванию в ServiceMi. Исходники на BitBucket&amp;nbsp;https://Hibernate2009@bitbucket.org/Hibernate2009/restful-with-apache-camel.git&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jser.blogspot.com/feeds/8180979445876113618/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/37848572/8180979445876113618' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/37848572/posts/default/8180979445876113618'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/37848572/posts/default/8180979445876113618'/><link rel='alternate' type='text/html' href='http://jser.blogspot.com/2014/03/restful-with-apache-camel.html' title='RESTFul with Apache Camel'/><author><name>JSer</name><uri>http://www.blogger.com/profile/17455716060338444232</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-37848572.post-3667337625687172771</id><published>2013-01-17T22:09:00.000+10:00</published><updated>2013-01-17T22:10:09.569+10:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Apache Camel"/><category scheme="http://www.blogger.com/atom/ns#" term="JDBC"/><category scheme="http://www.blogger.com/atom/ns#" term="JMS"/><category scheme="http://www.blogger.com/atom/ns#" term="ServiceMix"/><category scheme="http://www.blogger.com/atom/ns#" term="XA"/><title type='text'>Apache Camel on ServiceMix XA Transaction with database and JMS</title><content type='html'>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
Перейдем к одной из самых серьезных задач, без которой не может обойтись ни одна серьезная интеграционная платформа, к XA транзакциям. Для примера возьмем ситуацию близкую к реальности&amp;nbsp;описанную&amp;nbsp;как маршрут Apache Camel.
&lt;br /&gt;
&lt;pre&gt;&lt;code class=&quot;language-xml&quot;&gt;&amp;lt;camelContext xmlns=&quot;http://camel.apache.org/schema/spring&quot;&amp;gt;
 &amp;lt;!-- Transactional route --&amp;gt;
 &amp;lt;route&amp;gt;
  &amp;lt;from uri=&quot;jmstx:queue:giro&quot; /&amp;gt;
  &amp;lt;to uri=&quot;jmstx:queue:statusLog&quot; /&amp;gt;
  &amp;lt;bean ref=&quot;accountService&quot; method=&quot;credit&quot; /&amp;gt;
  &amp;lt;bean ref=&quot;accountService&quot; method=&quot;debit&quot; /&amp;gt;
 &amp;lt;/route&amp;gt;
 &amp;lt;!-- Feeder route --&amp;gt;
 &amp;lt;route&amp;gt;
  &amp;lt;from uri=&quot;jmstx:queue:statusLog&quot; /&amp;gt;
  &amp;lt;to uri=&quot;file:Success&quot; /&amp;gt;
 &amp;lt;/route&amp;gt;
&amp;lt;/camelContext&amp;gt;&lt;/code&gt;&lt;/pre&gt;
В этом маршруте описывается следующее:
&lt;br /&gt;
&lt;ol style=&quot;text-align: left;&quot;&gt;
&lt;li&gt;Принимает сообщение из JMS-очереди giro&lt;/li&gt;
&lt;li&gt;Перенаправляет сообщение в JMS-очередь statusLog, в которой должны быть только успешные сообщения&lt;/li&gt;
&lt;li&gt;Меняем состояние базы данных &amp;nbsp;&lt;/li&gt;
&lt;/ol&gt;
Второй маршрут описывает следующее:
&lt;br /&gt;
&lt;ol style=&quot;text-align: left;&quot;&gt;
&lt;li&gt;Принимает сообщение из JMS-очереди statusLog&lt;/li&gt;
&lt;li&gt;Сохраняет сообщение в папке Success&lt;/li&gt;
&lt;/ol&gt;
В ходе работы в итоговой папке Success должны находиться только успешные сообщения. При возникновении исключения, состояние в базе не меняется, и файла с транзакцией не оказывается в папке успешных сообщений. В этом примере участвуют 3 транзакционные системы JMS, database и filesystem. Как это заставить работать на ServiceMix и Apache Camel?...&lt;br /&gt;
&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;
Для начала необходимо ознакомиться с основной документацией по транзакциям на ServiceMix и его коммерческой версии&lt;a href=&quot;http://fusesource.com/docs/esbent/7.1/camel_tx/front.html&quot;&gt;http://fusesource.com/docs/esbent/7.1/camel_tx/front.html&lt;/a&gt;.&amp;nbsp;Ниже&amp;nbsp;изложенный&amp;nbsp;материал полностью основан на стандартной документации.&lt;br /&gt;
Итак для начала как обычно создаем проект в Eclipse на основе архетипа camel-archetype-spring-dm. Мы не будем изобретать логику и воспользуемся примером из документации. Создаем класс AccountService, который будет работать с базой данных. Вот прямая ссылка на исходник&amp;nbsp;&lt;a href=&quot;http://fusesource.com/docs/esbent/7.1/camel_tx/AppxAccountService-Example.html#AppxAccountService-ExASC&quot;&gt;AccountService&lt;/a&gt;. В классе разработаны методы debit и credit которые меняют состояние базы.&lt;br /&gt;
&lt;br /&gt;
Для начала определим DataSource. В предыдущей статье мы как раз опубликовали XADataSource к базе данных Oracle. В конфиге получаем доступ к нашему DataSource
&lt;br /&gt;
&lt;pre&gt;&lt;code class=&quot;language-xml&quot;&gt;&amp;lt;!-- Reference JDBC DataSource --&amp;gt;
&amp;lt;osgi:reference id=&quot;oracleXADataSource&quot; interface=&quot;javax.sql.DataSource&quot;
 filter=&quot;(oracle.xa.datasource.name=oracleXA)&quot; /&amp;gt;&lt;/code&gt;&lt;/pre&gt;
В качестве фильтра используется выражение, которые мы определили при его создании. Теперь можно определить наш бин и передать ему полученный DataSource&lt;br /&gt;
&lt;pre&gt;&lt;code class=&quot;language-xml&quot;&gt;&amp;lt;!-- bean for account business logic --&amp;gt;
&amp;lt;bean id=&quot;accountService&quot; class=&quot;com.bssys.ts2.AccountService&quot;&amp;gt;
 &amp;lt;property name=&quot;dataSource&quot; ref=&quot;oracleXADataSource&quot; /&amp;gt;
&amp;lt;/bean&amp;gt;&lt;/code&gt;&lt;/pre&gt;
Так настраивается соединение с базой.&lt;br /&gt;
Настройки JMS ресурсов чуть более сложны. Первым делом необходимо получить доступ к TransactionManager предоставляемых ServiceMix&lt;br /&gt;
&lt;pre&gt;&lt;code class=&quot;language-xml&quot;&gt;&amp;lt;!-- OSGi TM Service --&amp;gt;
&amp;lt;!-- access through Spring&#39;s PlatformTransactionManager --&amp;gt;
&amp;lt;osgi:reference id=&quot;osgiPlatformTransactionManager&quot;
 interface=&quot;org.springframework.transaction.PlatformTransactionManager&quot; /&amp;gt;
&amp;lt;!-- access through PlatformTransactionManager --&amp;gt;
&amp;lt;osgi:reference id=&quot;osgiJtaTransactionManager&quot;
 interface=&quot;javax.transaction.TransactionManager&quot; /&amp;gt;&lt;/code&gt;&lt;/pre&gt;
Имея их настраиваем ConnectionFactory для JMS
&lt;br /&gt;
&lt;pre&gt;&lt;code class=&quot;language-xml&quot;&gt;&amp;lt;!-- JMS TX endpoint configuration --&amp;gt;
&amp;lt;bean id=&quot;jmstx&quot; class=&quot;org.apache.activemq.camel.component.ActiveMQComponent&quot;&amp;gt;
 &amp;lt;property name=&quot;configuration&quot; ref=&quot;jmsTxConfig&quot; /&amp;gt;
&amp;lt;/bean&amp;gt;

&amp;lt;bean id=&quot;jmsTxConfig&quot; class=&quot;org.apache.camel.component.jms.JmsConfiguration&quot;&amp;gt;
 &amp;lt;property name=&quot;connectionFactory&quot; ref=&quot;jmsXaPoolConnectionFactory&quot; /&amp;gt;
 &amp;lt;property name=&quot;transactionManager&quot; ref=&quot;osgiPlatformTransactionManager&quot; /&amp;gt;
 &amp;lt;property name=&quot;transacted&quot; value=&quot;false&quot; /&amp;gt;
 &amp;lt;property name=&quot;cacheLevelName&quot; value=&quot;CACHE_CONNECTION&quot; /&amp;gt;
&amp;lt;/bean&amp;gt;

&amp;lt;!-- connection factory wrapper to support auto-enlisting of XA resource --&amp;gt;
&amp;lt;bean id=&quot;jmsXaPoolConnectionFactory&quot; class=&quot;org.apache.activemq.pool.XaPooledConnectionFactory&quot;&amp;gt;
 &amp;lt;property name=&quot;maxConnections&quot; value=&quot;1&quot; /&amp;gt;
 &amp;lt;property name=&quot;connectionFactory&quot; ref=&quot;jmsXaConnectionFactory&quot; /&amp;gt;
 &amp;lt;property name=&quot;transactionManager&quot; ref=&quot;osgiJtaTransactionManager&quot; /&amp;gt;
&amp;lt;/bean&amp;gt;

&amp;lt;bean id=&quot;jmsXaConnectionFactory&quot; class=&quot;org.apache.activemq.ActiveMQXAConnectionFactory&quot;&amp;gt;
 &amp;lt;property name=&quot;brokerURL&quot; value=&quot;vm:local&quot; /&amp;gt;
 &amp;lt;property name=&quot;redeliveryPolicy&quot;&amp;gt;
  &amp;lt;bean class=&quot;org.apache.activemq.RedeliveryPolicy&quot;&amp;gt;
   &amp;lt;property name=&quot;maximumRedeliveries&quot; value=&quot;0&quot; /&amp;gt;
  &amp;lt;/bean&amp;gt;
 &amp;lt;/property&amp;gt;
&amp;lt;/bean&amp;gt;&lt;/code&gt;&lt;/pre&gt;
Осталось сформировать базу по примеру из документации. Собрать и закинуть наш модуль в папку deploy.&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;programlisting&quot; style=&quot;background-color: #f0f0f0; border-bottom-style: none; color: #990033; font-size: 9pt; padding-left: 10pt; padding-right: 10pt;&quot;&gt;ij&amp;gt; CREATE TABLE accounts (name VARCHAR(50), amount INT);

ij&amp;gt; INSERT INTO accounts (name,amount) VALUES (&#39;Major Clanger&#39;,2000);

ij&amp;gt; INSERT INTO accounts (name,amount) VALUES (&#39;Tiny Clanger&#39;,100);&lt;/pre&gt;
&lt;br /&gt;
Теперь кидая сообщения вида мы будем видеть их в папке Success, и смотреть как поменялись данные в таблице Account&lt;br /&gt;
&lt;pre&gt;&lt;code class=&quot;language-xml&quot;&gt;&amp;lt;transaction&amp;gt;
  &amp;lt;transfer&amp;gt;
    &amp;lt;sender&amp;gt;Major Clanger&amp;lt;/sender&amp;gt;
    &amp;lt;receiver&amp;gt;Tiny Clanger&amp;lt;/receiver&amp;gt;
    &amp;lt;amount&amp;gt;90&amp;lt;/amount&amp;gt;
  &amp;lt;/transfer&amp;gt;
&amp;lt;/transaction&amp;gt;&lt;/code&gt;&lt;/pre&gt;
Для того чтобы спровоцировать откат транзакции достаточно кинуть сообщение с полем amount превышающим 100.&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;&lt;code class=&quot;language-xml&quot;&gt;&amp;lt;transaction&amp;gt;
  &amp;lt;transfer&amp;gt;
    &amp;lt;sender&amp;gt;Major Clanger&amp;lt;/sender&amp;gt;
    &amp;lt;receiver&amp;gt;Tiny Clanger&amp;lt;/receiver&amp;gt;
    &amp;lt;amount&amp;gt;150&amp;lt;/amount&amp;gt;
  &amp;lt;/transfer&amp;gt;
&amp;lt;/transaction&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
В этом случае метод debit выкинет исключение которое вызовет rollbak для транзакции.&lt;br /&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;public void debit(
            @XPath(&quot;/transaction/transfer/sender/text()&quot;) String name,
            @XPath(&quot;/transaction/transfer/amount/text()&quot;) String amount
            )
    {
        log.info(&quot;debit() called with args name = &quot; + name + &quot; and amount = &quot; + amount);
        int iamount = Integer.parseInt(amount);
        if (iamount &amp;gt; 100) {
            throw new IllegalArgumentException(&quot;Debit limit is 100&quot;);
        }
        int origAmount = jdbc.queryForInt(
                &quot;select amount from accounts where name = ?&quot;,
                new Object[]{name}
        );
        int newAmount = origAmount - Integer.parseInt(amount);
        if (newAmount &amp;lt; 0) {
            throw new IllegalArgumentException(&quot;Not enough in account&quot;);
        }
        
        jdbc.update(
                &quot;update accounts set amount = ? where name = ?&quot;,
                new Object[] {newAmount, name}
        );
    }&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;&lt;/div&gt;
</content><link rel='replies' type='application/atom+xml' href='http://jser.blogspot.com/feeds/3667337625687172771/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/37848572/3667337625687172771' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/37848572/posts/default/3667337625687172771'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/37848572/posts/default/3667337625687172771'/><link rel='alternate' type='text/html' href='http://jser.blogspot.com/2013/01/apache-camel-on-servicemix-xa.html' title='Apache Camel on ServiceMix XA Transaction with database and JMS'/><author><name>JSer</name><uri>http://www.blogger.com/profile/17455716060338444232</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-37848572.post-730952034935927137</id><published>2013-01-16T20:29:00.001+10:00</published><updated>2013-01-16T20:56:32.844+10:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Apache Camel"/><category scheme="http://www.blogger.com/atom/ns#" term="ServiceMix"/><category scheme="http://www.blogger.com/atom/ns#" term="XA"/><title type='text'>JDBC XA DataSource as OSGI Service</title><content type='html'>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
В данной заметке я расскажу как использовать базу данных в связке Apache Camel и Apache ServiceMix. Да, конечно можно описать все необходимы и бины и источники данных в конфиге Camel, но тут мы заглянем дальше.&amp;nbsp;Описанные&amp;nbsp;ресурсы будут участвовать в XA-транзации, и все это добро будет крутиться на ServiceMix. Это первая часть большого примера.&lt;br /&gt;
&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;
Итак, берем последнюю версию ServiceMix, на момент написания статьи это версия 4.4.2. В этой версии ServiceMix использует Apache Camel 2.8.5, версия старая, но для нас абсолютно не критично.&lt;br /&gt;
&lt;br /&gt;
Для того чтобы устанавливать проложения под ServiceMix, необходим правильный способ сборки.&amp;nbsp;Создаем в Eclipse проект Maven на основе архетипа camel-archetype-spring-dm, в результате будет создан проект, который собирается в osgi bundle и после сборки полностью готовый к установке на ServiceMix. Тут важно при создании проекта установить версию Apаche Camel 2.8.5, которая&amp;nbsp;соответствует&amp;nbsp;версии в ServiceMix, иначе у нас возникнут проблемы при запуске нашего модуля.&amp;nbsp;Отличие от архетипа, которым мы пользовались до этого сосредоточены в pom.xml:&lt;br /&gt;
&lt;br /&gt;
Установлен тип сборки bundle&lt;br /&gt;
&lt;pre&gt;&lt;code class=&quot;language-xml&quot;&gt;&amp;lt;packaging&amp;gt;bundle&amp;lt;/packaging&amp;gt;&lt;/code&gt;&lt;/pre&gt;
Добавлен новый плагин&lt;br /&gt;
&lt;pre&gt;&lt;code class=&quot;language-xml&quot;&gt;&amp;lt;plugin&amp;gt;
 &amp;lt;groupId&amp;gt;org.apache.felix&amp;lt;/groupId&amp;gt;
 &amp;lt;artifactId&amp;gt;maven-bundle-plugin&amp;lt;/artifactId&amp;gt;
 &amp;lt;version&amp;gt;2.3.4&amp;lt;/version&amp;gt;
 &amp;lt;extensions&amp;gt;true&amp;lt;/extensions&amp;gt;
 &amp;lt;configuration&amp;gt;
  &amp;lt;instructions&amp;gt;
   &amp;lt;Bundle-SymbolicName&amp;gt;datasources&amp;lt;/Bundle-SymbolicName&amp;gt;
   &amp;lt;Private-Package&amp;gt;com.bssys.datasources.*&amp;lt;/Private-Package&amp;gt;
   &amp;lt;Import-Package&amp;gt;*,org.apache.camel.osgi&amp;lt;/Import-Package&amp;gt;
  &amp;lt;/instructions&amp;gt;
 &amp;lt;/configuration&amp;gt;
&amp;lt;/plugin&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
После команды mvn build в папке target будет сгенерирован модуль готовый к развертыванию.&lt;br /&gt;
На самом деле в этом моделе Camel нам и не нужен. В файле camel-context.xml удаляем дефолтный маршрут, и вместо него описываем нужный нам DataSource. У себя в проекте в качестве базы данных используется Oracle, поэтому для примера возьмем именно его.&lt;br /&gt;
&lt;pre&gt;&lt;code class=&quot;language-xml&quot;&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&amp;gt;
&amp;lt;!-- Licensed to the Apache Software Foundation (ASF) under one or more contributor 
 license agreements. See the NOTICE file distributed with this work for additional 
 information regarding copyright ownership. The ASF licenses this file to 
 You under the Apache License, Version 2.0 (the &quot;License&quot;); 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 &quot;AS IS&quot; 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. --&amp;gt;

&amp;lt;beans xmlns=&quot;http://www.springframework.org/schema/beans&quot;
 xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot; xmlns:camel=&quot;http://camel.apache.org/schema/spring&quot;
 xmlns:osgi=&quot;http://www.springframework.org/schema/osgi&quot;
 xsi:schemaLocation=&quot;
       http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/osgi http://www.springframework.org/schema/osgi/spring-osgi.xsd
       http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd&quot;&amp;gt;

 &amp;lt;bean id=&quot;oracleXADataSource&quot; class=&quot;oracle.jdbc.xa.client.OracleXADataSource&quot;&amp;gt;
  &amp;lt;property name=&quot;URL&quot; value=&quot;jdbc:oracle:thin:@127.0.0.1:1521:orcl&quot; /&amp;gt;
  &amp;lt;property name=&quot;user&quot; value=&quot;user&quot; /&amp;gt;
  &amp;lt;property name=&quot;password&quot; value=&quot;password&quot; /&amp;gt;
 &amp;lt;/bean&amp;gt;

 &amp;lt;osgi:service id=&quot;xaDataSource&quot; ref=&quot;oracleXADataSource&quot;
  interface=&quot;javax.sql.XADataSource&quot;&amp;gt;
  &amp;lt;osgi:service-properties&amp;gt;
   &amp;lt;entry key=&quot;oracle.xa.datasource.name&quot; value=&quot;oracleXA&quot; /&amp;gt;
  &amp;lt;/osgi:service-properties&amp;gt;
 &amp;lt;/osgi:service&amp;gt;

 &amp;lt;bean id=&quot;helloBean&quot; class=&quot;com.bssys.datasources.HelloBean&quot;&amp;gt;
  &amp;lt;property name=&quot;say&quot; value=&quot;Hi from Camel&quot; /&amp;gt;
 &amp;lt;/bean&amp;gt;
&amp;lt;/beans&amp;gt;&lt;/code&gt;&lt;/pre&gt;
Самая интересная часть это описание OSGI-сервиса, который ссылается на описанный&amp;nbsp;OracleXADataSource и выставляет свойство&amp;nbsp;oracle.xa.datasource.name. Свойства могут быть любыми, и нужны там нам чтобы в дальнейшем выбирать нужный OSGI-сервис. Как это сделать будет описано далее.&lt;br /&gt;
Выставленный как OSGI-сервис XA-DataSource в ServiceMIx автоматически оборачивает в обертку Apache Aries Auto-Enlisting XA Wrapper. Более подробно можно почитать в документации &lt;a href=&quot;http://fusesource.com/docs/esbent/7.0/camel_tx/XaJdbc-AutoEnlist.html&quot;&gt;http://fusesource.com/docs/esbent/7.0/camel_tx/XaJdbc-AutoEnlist.html&lt;/a&gt;. После этого выполняем команду:&lt;br /&gt;
&lt;pre&gt;&lt;code class=&quot;language-xml&quot;&gt;mvn build&lt;/code&gt;&lt;/pre&gt;
Забираем собранный модуль из папки проекта target, и закидываем его в папку deploy в ServiceMix.&lt;br /&gt;
Если все прошло успешно, то в ServiceMix должен быть наш установленный модуль.&lt;br /&gt;
Можно проверить набрав в консоли:&lt;br /&gt;
&lt;pre&gt;&lt;code class=&quot;language-xml&quot;&gt;osgi:list&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-xml&quot;&gt;[ 265] [Active     ] [            ] [Started] [   60] DataSource bundle (0.0.1.SNAPSHOT)&lt;/code&gt;&lt;/pre&gt;
Видим наш установленный модуль с id=265&lt;br /&gt;
Проверим что наш OSGI-сервис успешно обернут:&lt;br /&gt;
&lt;pre&gt;&lt;code class=&quot;language-xml&quot;&gt;osgi:ls 265&lt;/code&gt;&lt;/pre&gt;
Среди прочего можно увидеть:
&lt;br /&gt;
&lt;pre&gt;&lt;code class=&quot;language-xml&quot;&gt;aries.xa.aware = true
Bundle-SymbolicName = datasources
Bundle-Version = 0.0.1.SNAPSHOT
objectClass = javax.sql.DataSource
oracle.xa.datasource.name = oracleXA
org.springframework.osgi.bean.name = oracleXADataSource
service.id = 261&lt;/code&gt;&lt;/pre&gt;
Теперь можно получать наш DataSource по OSGI-ссылке. Как это сделать будет рассказано в моей следующей заметке.&lt;br /&gt;
&lt;br /&gt;&lt;/div&gt;
</content><link rel='replies' type='application/atom+xml' href='http://jser.blogspot.com/feeds/730952034935927137/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/37848572/730952034935927137' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/37848572/posts/default/730952034935927137'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/37848572/posts/default/730952034935927137'/><link rel='alternate' type='text/html' href='http://jser.blogspot.com/2013/01/jdbc-xa-datasource-as-osgi-service.html' title='JDBC XA DataSource as OSGI Service'/><author><name>JSer</name><uri>http://www.blogger.com/profile/17455716060338444232</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-37848572.post-7253636005414342618</id><published>2012-08-30T00:00:00.000+10:00</published><updated>2013-05-08T16:06:43.286+10:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Apache Camel"/><title type='text'>Parse HTML-page and follow the link with Apache Camel</title><content type='html'>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
Как распарсить HTML-страницу и пройти по ссылкам с помощью Ahace Camel? Если кратко-легко! Выполним эту задачу не написав ни одной строчки кода.
&lt;br /&gt;
На этот раз воспользуемся Spring-DSL, так как в данном случае он более лаконичен.
&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEij6R8-Hsep1iWuhCleuAC8m861uPjKr6sJ7Kt-HK0Z7wD69b3Kwmj2zgCX8lSNqcecHsETY_gYEZSVwgvWjdtil-iBitNPjNIJ-0hsMRuLCrmL_ajF7vxRZ1UYNReY1XRxkJud/s1600/browse.PNG&quot; imageanchor=&quot;1&quot; style=&quot;clear: left; float: left; margin-bottom: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEij6R8-Hsep1iWuhCleuAC8m861uPjKr6sJ7Kt-HK0Z7wD69b3Kwmj2zgCX8lSNqcecHsETY_gYEZSVwgvWjdtil-iBitNPjNIJ-0hsMRuLCrmL_ajF7vxRZ1UYNReY1XRxkJud/s1600/browse.PNG&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;
Для начала необходимо подключить необходимые зависимости

&lt;br /&gt;
&lt;pre&gt;&lt;code class=&quot;language-xml&quot;&gt;&amp;lt;dependency&amp;gt;
 &amp;lt;groupId&amp;gt;org.apache.camel&amp;lt;/groupId&amp;gt;
 &amp;lt;artifactId&amp;gt;camel-http&amp;lt;/artifactId&amp;gt;
 &amp;lt;version&amp;gt;2.9.1&amp;lt;/version&amp;gt;
&amp;lt;/dependency&amp;gt;
&amp;lt;dependency&amp;gt;
 &amp;lt;groupId&amp;gt;org.apache.camel&amp;lt;/groupId&amp;gt;
 &amp;lt;artifactId&amp;gt;camel-tagsoup&amp;lt;/artifactId&amp;gt;
 &amp;lt;version&amp;gt;2.9.1&amp;lt;/version&amp;gt;
&amp;lt;/dependency&amp;gt;
&lt;/code&gt;
&lt;/pre&gt;
camel-tagsoup необходим для того чтобы распарсить HTML-страницу. Ниже сам маршрут на Spring-DSL выполняющий данную задачу.
&lt;br /&gt;
&lt;pre&gt;&lt;code class=&quot;language-xml&quot;&gt;&amp;lt;camel:route&amp;gt;
 &amp;lt;camel:from uri=&quot;direct:bot&quot; /&amp;gt;&amp;lt;!-- В качестве сообщения будет поступать url --&amp;gt;
 &amp;lt;camel:setHeader headerName=&quot;original&quot;&amp;gt;&amp;lt;!--Сохраним url для дальнейшего использования --&amp;gt;
  &amp;lt;camel:simple&amp;gt;${body}&amp;lt;/camel:simple&amp;gt;
 &amp;lt;/camel:setHeader&amp;gt;
 &amp;lt;camel:recipientList&amp;gt;&amp;lt;!-- Динамически вызовем url --&amp;gt;
  &amp;lt;camel:simple&amp;gt;${body}/b/opp&amp;lt;/camel:simple&amp;gt;
 &amp;lt;/camel:recipientList&amp;gt;
 &amp;lt;camel:unmarshal&amp;gt;
  &amp;lt;camel:tidyMarkup /&amp;gt;&amp;lt;!-- Привести HTML к удобочитаемому виду --&amp;gt;
 &amp;lt;/camel:unmarshal&amp;gt;
 &amp;lt;camel:setBody&amp;gt;
  &amp;lt;camel:xpath&amp;gt;//a&amp;lt;/camel:xpath&amp;gt;&amp;lt;!-- С помощью XPath отбираем только ссылки --&amp;gt;
 &amp;lt;/camel:setBody&amp;gt;
 &amp;lt;camel:split&amp;gt;
  &amp;lt;camel:tokenize token=&quot;a&quot; xml=&quot;true&quot; /&amp;gt;&amp;lt;!-- С помощью сплиттера разделяем полученые ссылки --&amp;gt;
  &amp;lt;camel:setHeader headerName=&quot;href&quot;&amp;gt;
   &amp;lt;camel:xpath&amp;gt;//@href&amp;lt;/camel:xpath&amp;gt;&amp;lt;!-- Получаем url-ссылки --&amp;gt;
  &amp;lt;/camel:setHeader&amp;gt;
  &amp;lt;camel:log message=&quot;${in.header.original}${in.header.href}&quot;&amp;gt;&amp;lt;/camel:log&amp;gt;
  &amp;lt;camel:recipientList&amp;gt;
   &amp;lt;camel:simple&amp;gt;${in.header.original}${in.header.href}&amp;lt;/camel:simple&amp;gt;&amp;lt;!--Формируем итоговый url и проходим по ссылке --&amp;gt;
  &amp;lt;/camel:recipientList&amp;gt;
 &amp;lt;/camel:split&amp;gt;
&amp;lt;/camel:route&amp;gt;
&lt;/code&gt;
&lt;/pre&gt;
Само собой ссылки в HTML относительные.
Удачи! 
&lt;/div&gt;
</content><link rel='replies' type='application/atom+xml' href='http://jser.blogspot.com/feeds/7253636005414342618/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/37848572/7253636005414342618' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/37848572/posts/default/7253636005414342618'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/37848572/posts/default/7253636005414342618'/><link rel='alternate' type='text/html' href='http://jser.blogspot.com/2012/08/parse-html-page-and-follow-link-with.html' title='Parse HTML-page and follow the link with Apache Camel'/><author><name>JSer</name><uri>http://www.blogger.com/profile/17455716060338444232</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEij6R8-Hsep1iWuhCleuAC8m861uPjKr6sJ7Kt-HK0Z7wD69b3Kwmj2zgCX8lSNqcecHsETY_gYEZSVwgvWjdtil-iBitNPjNIJ-0hsMRuLCrmL_ajF7vxRZ1UYNReY1XRxkJud/s72-c/browse.PNG" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-37848572.post-6967109351914506541</id><published>2012-08-08T23:20:00.000+10:00</published><updated>2012-09-11T21:44:24.879+10:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Apache Camel"/><category scheme="http://www.blogger.com/atom/ns#" term="webservices"/><title type='text'>Calling Web Services with Apache Camel</title><content type='html'>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
Казалось бы, такая простая задача для интеграционной системы как вызов внешнего webservice, для Apache Camel вызывает затруднения. Прольем немного света. Вызов внешнего webservice на Apache Camel реально занимает одну строчку! Возьмем доступный всем открытый сервис погоды:&lt;br /&gt;
&lt;a href=&quot;http://www.webservicex.com/globalweather.asmx?WSDL&quot;&gt;http://www.webservicex.com/globalweather.asmx?WSDL&lt;/a&gt;
&lt;br /&gt;
Вот как вызвать этот сервис на Camel:&lt;br /&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;from(&quot;file:c:/data/inbox3&quot;).to(&quot;cxf:bean:calculatorWSEndpoint?defaultOperationName=GetWeather&quot;).to(&quot;file:c:/data/outbox&quot;);
&lt;/code&gt;
&lt;/pre&gt;
Подробности под катом!
&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;
Подготовим spring-config.xml, в нем нам необходимо зарегистрировать cxf-endpoint для вызываемого webservice
&lt;br /&gt;
&lt;pre&gt;&lt;code class=&quot;language-xml&quot;&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&amp;gt;
&amp;lt;!-- Licensed to the Apache Software Foundation (ASF) under one or more contributor 
 license agreements. See the NOTICE file distributed with this work for additional 
 information regarding copyright ownership. The ASF licenses this file to 
 You under the Apache License, Version 2.0 (the &quot;License&quot;); 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 &quot;AS IS&quot; 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. --&amp;gt;

&amp;lt;!-- Configures the Camel Context --&amp;gt;

&amp;lt;beans xmlns=&quot;http://www.springframework.org/schema/beans&quot;
 xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot; xmlns:cxf=&quot;http://camel.apache.org/schema/cxf&quot;
 xsi:schemaLocation=&quot;
       http://camel.apache.org/schema/cxf http://camel.apache.org/schema/cxf/camel-cxf.xsd
       http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
       http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd&quot;&amp;gt;
 
 &amp;lt;cxf:cxfEndpoint xmlns:tns=&quot;http://www.webserviceX.NET&quot; 
  id=&quot;calculatorWSEndpoint&quot;
  endpointName=&quot;tns:GlobalWeatherSoap&quot;
  address=&quot;http://www.webservicex.com/globalweather.asmx&quot;
  wsdlURL=&quot;http://www.webservicex.com/globalweather.asmx?WSDL&quot;&amp;gt;
  &amp;lt;cxf:properties&amp;gt;
   &amp;lt;entry key=&quot;dataFormat&quot; value=&quot;PAYLOAD&quot; /&amp;gt;
  &amp;lt;/cxf:properties&amp;gt;
 &amp;lt;/cxf:cxfEndpoint&amp;gt;

 &amp;lt;camelContext xmlns=&quot;http://camel.apache.org/schema/spring&quot;&amp;gt;
  &amp;lt;package&amp;gt;com.bssys.fileid&amp;lt;/package&amp;gt;
 &amp;lt;/camelContext&amp;gt;

&amp;lt;/beans&amp;gt;
&lt;/code&gt;
&lt;/pre&gt;
Напомню что взамодествие с webservice может происходить в двух режимах
&lt;br /&gt;
&lt;ul style=&quot;text-align: left;&quot;&gt;
&lt;li&gt;MESSAGE&lt;/li&gt;
&lt;li&gt;PAYLOAD&lt;/li&gt;
&lt;/ul&gt;
Для получения информации о погоде будем использовать объявленый метод GetWeather&lt;br /&gt;
&lt;br /&gt;
В режиме MESSAGE сообщение представляет из себя полный SOAP-запрос.&lt;br /&gt;
В режиме PAYLOAD сообщение из body SOAP-запроса.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;&amp;nbsp;Запрос в PAYLOAD-режиме
&lt;/b&gt;&lt;br /&gt;

&lt;pre&gt;&lt;code class=&quot;language-xml&quot;&gt;&amp;lt;ns1:GetWeather xmlns:ns1=&amp;quot;http://www.webserviceX.NET&amp;quot;&amp;gt;
 &amp;lt;ns1:CityName&amp;gt;Moscow&amp;lt;/ns1:CityName&amp;gt;
 &amp;lt;ns1:CountryName/&amp;gt;
&amp;lt;/ns1:GetWeather&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;b&gt;&lt;br /&gt;&lt;/b&gt;
&lt;b&gt;Запрос в MESSAGE-режиме
&lt;/b&gt;&lt;br /&gt;
&lt;pre&gt;&lt;code class=&quot;language-xml&quot;&gt;&amp;lt;s11:Envelope xmlns:s11=&quot;http://schemas.xmlsoap.org/soap/envelope/&quot;&amp;gt;
  &amp;lt;s11:Body&amp;gt;
    &amp;lt;ns1:GetWeather xmlns:ns1=&quot;http://www.webserviceX.NET&quot;&amp;gt;
      &amp;lt;ns1:CityName&amp;gt;Moscow&amp;lt;/ns1:CityName&amp;gt;
      &amp;lt;ns1:CountryName/&amp;gt;
    &amp;lt;/ns1:GetWeather&amp;gt;
  &amp;lt;/s11:Body&amp;gt;
&amp;lt;/s11:Envelope&amp;gt;
&lt;/code&gt;
&lt;/pre&gt;

Запустив наш маршрут увидим в папке ответ от webservice:
&lt;pre&gt;&lt;code class=&quot;language-xml&quot;&gt;&amp;lt;GetWeatherResponse xmlns=&amp;quot;http://www.webserviceX.NET&amp;quot; xmlns:xsd=&amp;quot;http://www.w3.org/2001/XMLSchema&amp;quot; xmlns:xsi=&amp;quot;http://www.w3.org/2001/XMLSchema-instance&amp;quot;&amp;gt;&amp;lt;GetWeatherResult&amp;gt;&amp;amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;utf-16&amp;quot;?&amp;amp;gt;
&amp;amp;lt;CurrentWeather&amp;amp;gt;
  &amp;amp;lt;Location&amp;amp;gt;Moscow / Vnukovo , Russia (UUWW) 55-39N 037-16E&amp;amp;lt;/Location&amp;amp;gt;
  &amp;amp;lt;Time&amp;amp;gt;Aug 08, 2012 - 04:00 AM EDT / 2012.08.08 0800 UTC&amp;amp;lt;/Time&amp;amp;gt;
  &amp;amp;lt;Wind&amp;amp;gt; from the W (270 degrees) at 9 MPH (8 KT):0&amp;amp;lt;/Wind&amp;amp;gt;
  &amp;amp;lt;Visibility&amp;amp;gt; greater than 7 mile(s):0&amp;amp;lt;/Visibility&amp;amp;gt;
  &amp;amp;lt;SkyConditions&amp;amp;gt; mostly clear&amp;amp;lt;/SkyConditions&amp;amp;gt;
  &amp;amp;lt;Temperature&amp;amp;gt; 73 F (23 C)&amp;amp;lt;/Temperature&amp;amp;gt;
  &amp;amp;lt;DewPoint&amp;amp;gt; 50 F (10 C)&amp;amp;lt;/DewPoint&amp;amp;gt;
  &amp;amp;lt;RelativeHumidity&amp;amp;gt; 43%&amp;amp;lt;/RelativeHumidity&amp;amp;gt;
  &amp;amp;lt;Pressure&amp;amp;gt; 29.94 in. Hg (1014 hPa)&amp;amp;lt;/Pressure&amp;amp;gt;
  &amp;amp;lt;Status&amp;amp;gt;Success&amp;amp;lt;/Status&amp;amp;gt;
&amp;amp;lt;/CurrentWeather&amp;amp;gt;&amp;lt;/GetWeatherResult&amp;gt;&amp;lt;/GetWeatherResponse&amp;gt;
&lt;/code&gt;
&lt;/pre&gt;

&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jser.blogspot.com/feeds/6967109351914506541/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/37848572/6967109351914506541' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/37848572/posts/default/6967109351914506541'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/37848572/posts/default/6967109351914506541'/><link rel='alternate' type='text/html' href='http://jser.blogspot.com/2012/08/calling-web-services-with-apache-camel.html' title='Calling Web Services with Apache Camel'/><author><name>JSer</name><uri>http://www.blogger.com/profile/17455716060338444232</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-37848572.post-7017613972072714090</id><published>2012-08-06T23:40:00.002+10:00</published><updated>2013-05-08T16:07:11.144+10:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Apache Camel"/><category scheme="http://www.blogger.com/atom/ns#" term="OSB"/><category scheme="http://www.blogger.com/atom/ns#" term="xml"/><title type='text'>Apache Camel Message Processing Actions (Like OSB)</title><content type='html'>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgnpHqoXe2qsZspRMm9kNV6MCHORL7Ejrp5Ti_Y_VGnuhOVUq7as5P5w4YpVqa5hiFDHOV5o33livsTlXYx4FlRnCyw-8b_PXwXYT32FfCEr6iOBx89zf5MThcEqr6MNE_x-74R/s1600/1.PNG&quot; imageanchor=&quot;1&quot; style=&quot;clear: left; float: left; margin-bottom: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgnpHqoXe2qsZspRMm9kNV6MCHORL7Ejrp5Ti_Y_VGnuhOVUq7as5P5w4YpVqa5hiFDHOV5o33livsTlXYx4FlRnCyw-8b_PXwXYT32FfCEr6iOBx89zf5MThcEqr6MNE_x-74R/s1600/1.PNG&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
Работая в проекте с OSB, я удивился как легко и просто там работать с xml. Модификация сообщения на Oracle Service Bus действительно делается легко и непринужденно c помощью инструменов Assign, Delete, Insert. Как же наш Apache Camel? А вот в нем не все так просто. Но все необходимое у нас есть, попробуем сделать что-то подобное!&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;
Интерфейс Exchange предоставляет нам метод&lt;br /&gt;
&lt;span style=&quot;background-color: white; text-align: -webkit-auto;&quot;&gt;&lt;table border=&quot;1&quot; cellpadding=&quot;3&quot; cellspacing=&quot;0&quot; style=&quot;color: black; width: 100%px;&quot; summary=&quot;&quot;&gt;&lt;tbody&gt;
&lt;tr bgcolor=&quot;white&quot; class=&quot;TableRowColor&quot;&gt;&lt;td align=&quot;right&quot; valign=&quot;top&quot; width=&quot;1%&quot;&gt;&lt;code&gt;&amp;nbsp;void&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;&lt;b&gt;&lt;a href=&quot;http://camel.apache.org/maven/current/camel-core/apidocs/org/apache/camel/Exchange.html#setProperty(java.lang.String, java.lang.Object)&quot;&gt;setProperty&lt;/a&gt;&lt;/b&gt;(&lt;a href=&quot;http://java.sun.com/javase/6/docs/api/java/lang/String.html?is-external=true&quot; title=&quot;class or interface in java.lang&quot;&gt;String&lt;/a&gt;&amp;nbsp;name,&amp;nbsp;&lt;a href=&quot;http://java.sun.com/javase/6/docs/api/java/lang/Object.html?is-external=true&quot; title=&quot;class or interface in java.lang&quot;&gt;Object&lt;/a&gt;&amp;nbsp;value)&lt;/code&gt;&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Sets a property on the exchange&lt;/td&gt;&lt;/tr&gt;
&lt;tr bgcolor=&quot;white&quot; class=&quot;TableRowColor&quot;&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/span&gt;&lt;br /&gt;
Это позволяет нам сохранять переменные в контексте всего процесса обработки сообщения.&lt;br /&gt;
Вот что представляет собой элемент Assign в OSB:&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh6lchH26kFHn_jVNfScXg3dAoh0TdXE7gZ_OZfQSJESXAeNhVifYY-Ds5D3Me4XCkUoV-B89FLARO8EBjAx8OTYN8J8jme5n86ju1vnXwShE0Q9b7T_XR3BTsvDn7bMHnVAJvq/s1600/assign.PNG&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh6lchH26kFHn_jVNfScXg3dAoh0TdXE7gZ_OZfQSJESXAeNhVifYY-Ds5D3Me4XCkUoV-B89FLARO8EBjAx8OTYN8J8jme5n86ju1vnXwShE0Q9b7T_XR3BTsvDn7bMHnVAJvq/s1600/assign.PNG&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
Данный элемент сохраняет выражение в переменной.Выражение может быть результатом XQuery, XSLT преобразования или просто строго определенным xml.&lt;br /&gt;
Аналогом этого элемента является следющее выражение Java DSL:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;from(&quot;file:c:/data/inbox&quot;).setProperty(&quot;original&quot;).simple(&quot;${body}&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
В данном случае мы в переменную &quot;original&quot; сохранили тело запроса, помимо simple, Camel позволяет позволяет выполнить XPath выражение, XQuery и XSLT, и многое другое. В данном случае, чтобы сохранить тело оригинального запроса нам достаточно &quot;simple&quot;.&lt;br /&gt;
&lt;br /&gt;
C элементами Delete и Insert не все так просто.&lt;br /&gt;
Вот интерфейс элементов OSB&lt;br /&gt;
Delete:&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjA-2Ef8yvpS8fUHz-wlUYunCWl86CQI2lXkER2eG4ll7dnO7aKd_QMyilR-_7mviH3zCz30P53ErKy1uIiwLVYpIlmKvnxzT8EIw64nb3BErTRU-YQALqq89TpEQnctHfJG9T9/s1600/delete.PNG&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjA-2Ef8yvpS8fUHz-wlUYunCWl86CQI2lXkER2eG4ll7dnO7aKd_QMyilR-_7mviH3zCz30P53ErKy1uIiwLVYpIlmKvnxzT8EIw64nb3BErTRU-YQALqq89TpEQnctHfJG9T9/s1600/delete.PNG&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&amp;nbsp;XPath, выражение результат которого будет удален из переменной &quot;original&quot;&lt;br /&gt;
Для Apache Camel это будет реализовано с помощью процессора:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;beanRef(&quot;delete&quot;,&quot;execute(&#39;original&#39;, &#39;//record[@recordUUID=1]&#39;)&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
&lt;ul style=&quot;text-align: left;&quot;&gt;
&lt;li&gt;&quot;delete&quot; -имя java-bean заданное в spring-context.xml&lt;/li&gt;
&lt;li&gt;&#39;execute&#39;- имя метода&amp;nbsp;&lt;/li&gt;
&lt;li&gt;&#39;original&#39; - имя переменной из которой будет произведено удаление&lt;/li&gt;
&lt;li&gt;&#39;//record[@recordUUID=1]&#39;)&#39; - XPath выражение&lt;/li&gt;
&lt;/ul&gt;
Insert:&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEio8go-57K7YsqdSc6rbQ3Z3rhmlA4zAgvQQ9qEwH1aj2dWmQx19TJCiNAnVPfnUOmatYuCqrtvWs0uih7RFzHJkAFr2i1Z6RLpicrOi9GkhAJPhgqzhou3Nc_D4t0TBmbVW_UO/s1600/insert.PNG&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEio8go-57K7YsqdSc6rbQ3Z3rhmlA4zAgvQQ9qEwH1aj2dWmQx19TJCiNAnVPfnUOmatYuCqrtvWs0uih7RFzHJkAFr2i1Z6RLpicrOi9GkhAJPhgqzhou3Nc_D4t0TBmbVW_UO/s1600/insert.PNG&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;beanRef(&quot;insert&quot;,&quot;execute(&#39;raw&#39;,&#39;//records&#39;, &#39;original&#39;)&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
&lt;ul style=&quot;text-align: left;&quot;&gt;
&lt;li&gt;&quot;insert&quot; - имя java-bean заданное в spring-context.xml&lt;/li&gt;
&lt;li&gt;&#39;raw&#39; - имя переменной значение которой будет вставляться&lt;/li&gt;
&lt;li&gt;&#39;//records&#39; - Xpath выражение, в переменной original, обозначает место в которое будет произведена вставка.&lt;/li&gt;
&lt;li&gt;&#39;original&#39; - имя переменной которое будет изменено.&lt;/li&gt;
&lt;/ul&gt;
&lt;br /&gt;
&lt;br /&gt;
На Apache Camel нам&amp;nbsp;потребуется&amp;nbsp;разработать два процессора:&lt;br /&gt;
&lt;br /&gt;
DeleteProcessor:
&lt;br /&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;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 &amp;lt; size; i++) {
    Node child = list.item(i);
    Node parentNode = child.getParentNode();
    parentNode.removeChild(child);
   }
  }
  exchange.setProperty(invariable, docVariable);
 }
}
&lt;/code&gt;
&lt;/pre&gt;
InsertProcessor
&lt;br /&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;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 &amp;lt; list.getLength(); i++) {
    Node item = list.item(i);
    Node importNode = docInVariable.importNode(rootNode, true);
    item.appendChild(importNode);
   }
  }
  exchange.setProperty(invariable, docInVariable);
 }
}
&lt;/code&gt;
&lt;/pre&gt;
Теперь воспользуемся разработаными инструментами и построим следующий маршрут.&lt;br /&gt;
На входе у нас файловый адаптер, который принимает xml-файлы:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;&lt;code class=&quot;language-xml&quot;&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&amp;gt;
&amp;lt;storeRegistryCardsRequest&amp;gt;
 &amp;lt;records&amp;gt;
  &amp;lt;record recordUUID=&quot;1&quot;&amp;gt;
   &amp;lt;cardHolderIdentifiers&amp;gt;cardHolderIdentifiers&amp;lt;/cardHolderIdentifiers&amp;gt;
   &amp;lt;card&amp;gt;card&amp;lt;/card&amp;gt;
  &amp;lt;/record&amp;gt;
  &amp;lt;record recordUUID=&quot;2&quot;&amp;gt;
   &amp;lt;cardHolderIdentifiers&amp;gt;cardHolderIdentifiers&amp;lt;/cardHolderIdentifiers&amp;gt;
   &amp;lt;card&amp;gt;card&amp;lt;/card&amp;gt;
  &amp;lt;/record&amp;gt;
  &amp;lt;record recordUUID=&quot;3&quot;&amp;gt;
   &amp;lt;cardHolderIdentifiers&amp;gt;cardHolderIdentifiers&amp;lt;/cardHolderIdentifiers&amp;gt;
   &amp;lt;card&amp;gt;card&amp;lt;/card&amp;gt;
  &amp;lt;/record&amp;gt;
 &amp;lt;/records&amp;gt;
&amp;lt;/storeRegistryCardsRequest&amp;gt;
&lt;/code&gt;
&lt;/pre&gt;
Удаляет record с UUID=1, и добавляет record c UUID=4

Вот такой маршрут у нас получился в итоге.

&lt;br /&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;from(&quot;file:c:/data/inbox3&quot;).
  setProperty(&quot;original&quot;).simple(&quot;${body}&quot;).
  beanRef(&quot;delete&quot;,&quot;execute(&#39;original&#39;, &#39;//record[@recordUUID=1]&#39;)&quot;).
  setProperty(&quot;raw&quot;).simple(&quot;&amp;lt;record recordUUID=\&quot;4\&quot;&amp;gt;&amp;lt;cardHolderIdentifiers&amp;gt;cardHolderIdentifiers&amp;lt;/cardHolderIdentifiers&amp;gt;&amp;lt;card&amp;gt;card&amp;lt;/card&amp;gt;&amp;lt;/record&amp;gt;&quot;).
  beanRef(&quot;insert&quot;,&quot;execute(&#39;raw&#39;,&#39;//records&#39;, &#39;original&#39;)&quot;).
  setBody().simple(&quot;${property.original}&quot;).
  to(&quot;file:c:/data/outbox&quot;);&lt;/code&gt;&lt;/pre&gt;
Результат обработки:

&lt;br /&gt;
&lt;pre&gt;&lt;code class=&quot;language-xml&quot;&gt;&amp;lt;storeRegistryCardsRequest&amp;gt;
 &amp;lt;records&amp;gt;

  &amp;lt;record recordUUID=&quot;2&quot;&amp;gt;
   &amp;lt;cardHolderIdentifiers&amp;gt;cardHolderIdentifiers&amp;lt;/cardHolderIdentifiers&amp;gt;
   &amp;lt;card&amp;gt;card&amp;lt;/card&amp;gt;
  &amp;lt;/record&amp;gt;
  &amp;lt;record recordUUID=&quot;3&quot;&amp;gt;
   &amp;lt;cardHolderIdentifiers&amp;gt;cardHolderIdentifiers&amp;lt;/cardHolderIdentifiers&amp;gt;
   &amp;lt;card&amp;gt;card&amp;lt;/card&amp;gt;
  &amp;lt;/record&amp;gt;
  &amp;lt;record recordUUID=&quot;4&quot;&amp;gt;
   &amp;lt;cardHolderIdentifiers&amp;gt;cardHolderIdentifiers&amp;lt;/cardHolderIdentifiers&amp;gt;
   &amp;lt;card&amp;gt;card&amp;lt;/card&amp;gt;
  &amp;lt;/record&amp;gt;
 &amp;lt;/records&amp;gt;
&amp;lt;/storeRegistryCardsRequest&amp;gt;
&lt;/code&gt;
&lt;/pre&gt;
&lt;br /&gt;
Удачи!
&lt;/div&gt;
</content><link rel='replies' type='application/atom+xml' href='http://jser.blogspot.com/feeds/7017613972072714090/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/37848572/7017613972072714090' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/37848572/posts/default/7017613972072714090'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/37848572/posts/default/7017613972072714090'/><link rel='alternate' type='text/html' href='http://jser.blogspot.com/2012/08/apache-camel-message-processing-actions.html' title='Apache Camel Message Processing Actions (Like OSB)'/><author><name>JSer</name><uri>http://www.blogger.com/profile/17455716060338444232</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgnpHqoXe2qsZspRMm9kNV6MCHORL7Ejrp5Ti_Y_VGnuhOVUq7as5P5w4YpVqa5hiFDHOV5o33livsTlXYx4FlRnCyw-8b_PXwXYT32FfCEr6iOBx89zf5MThcEqr6MNE_x-74R/s72-c/1.PNG" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-37848572.post-320421806060029131</id><published>2012-07-23T23:59:00.002+10:00</published><updated>2012-09-11T21:45:24.061+10:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="minitroopers"/><title type='text'>Minitroopers Train Bot</title><content type='html'>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
Для тех кто хочет немного отвлечься, есть замечательная браузерка minitroopers.com&lt;br /&gt;
Описание и прочее выкладывать не буду, в интернете информации море. Для тех кто увлекся могут почитать&amp;nbsp;&lt;a href=&quot;http://minitroopers.wikia.com/wiki/Mini_Troopers_Wiki&quot;&gt;http://minitroopers.wikia.com/wiki/Mini_Troopers_Wiki&lt;/a&gt;&lt;br /&gt;
Чтобы не тратить драгоценное время на прокачку твинков своей основной армии, я написал простого бота, который отыгрывает за вас.&lt;br /&gt;
&lt;br /&gt;
&lt;ol style=&quot;text-align: left;&quot;&gt;
&lt;li&gt;Ежедневные бои. Выбирает первого кандидата из списка. По опыту первый самый слабый, но хотя если и проиграете не страшно.&lt;/li&gt;
&lt;li&gt;Миссия из трех попыток. Если она у вас открыта.&lt;/li&gt;
&lt;li&gt;5 шагов рейда. Если рейд у твинка больше 5 боев, то на это стоит поглядеть!&lt;/li&gt;
&lt;/ol&gt;
&lt;div&gt;
Для запуска необходима Java.&lt;/div&gt;
&lt;div&gt;
&lt;pre&gt;&lt;code class=&quot;language-xml&quot;&gt;java -jar minitroopers.jar&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;code class=&quot;language-xml&quot;&gt;
&lt;/code&gt;
&lt;br /&gt;
&lt;div&gt;
Имена твинков &amp;nbsp;заносятся в файлик minitroopers.property в следующем формате:&lt;/div&gt;
&lt;div&gt;
URL^login^password&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
Например:&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
http://dd-dealer.minitroopers.com^dd-dealer^ddpassword&lt;/div&gt;
&lt;div&gt;
http://izilla.minitroopers.com&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
Если ваш твинк запаролен то логин в 99% совпадает с URL, но если у вас это не так, можете посмотреть в браузере какой логин отправляется и записать его в файлик.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;b&gt;Ссылка на бинарник:&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;
&lt;a href=&quot;https://github.com/downloads/Hibernate2009/Minitroopers-Train-Bot/minitroopers.jar&quot;&gt;Minitroopers Bot&lt;/a&gt;&lt;/div&gt;
&lt;div&gt;
&lt;b&gt;Файл с твинками:&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;
&lt;a href=&quot;https://github.com/downloads/Hibernate2009/Minitroopers-Train-Bot/minitroopers.property&quot;&gt;minitroopers.property&lt;/a&gt;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;span style=&quot;background-color: white;&quot;&gt;Ну и по традиции подобных игр. Добавляйтесь в мою армию!&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;background-color: white;&quot;&gt;&lt;a href=&quot;http://hibernate.minitroopers.com/&quot;&gt;http://hibernate.minitroopers.com/&lt;/a&gt;&lt;/span&gt;&lt;br /&gt;
Удачи в прокачке!
&lt;/div&gt;
&lt;/div&gt;
</content><link rel='replies' type='application/atom+xml' href='http://jser.blogspot.com/feeds/320421806060029131/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/37848572/320421806060029131' title='Комментарии: 1'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/37848572/posts/default/320421806060029131'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/37848572/posts/default/320421806060029131'/><link rel='alternate' type='text/html' href='http://jser.blogspot.com/2012/07/minitroopers-train-bot.html' title='Minitroopers Train Bot'/><author><name>JSer</name><uri>http://www.blogger.com/profile/17455716060338444232</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-37848572.post-6012425591508137334</id><published>2012-05-30T22:45:00.000+10:00</published><updated>2013-05-08T16:07:22.266+10:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Apache Camel"/><title type='text'>JDBC Polling Adapter c помощью Apache Camel</title><content type='html'>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
&lt;span style=&quot;font-family: inherit;&quot;&gt;В качестве&amp;nbsp;&lt;span style=&quot;background-color: white; text-align: -webkit-auto;&quot;&gt;wiki and issue tracking system&lt;/span&gt;&amp;nbsp;&lt;/span&gt;у&amp;nbsp;нас используется TRAC&amp;nbsp;&lt;a href=&quot;http://trac.edgewall.org/&quot;&gt;http://trac.edgewall.org/&lt;/a&gt;&amp;nbsp;Появилась задача мониторинга изменения задач в этой системе. TRAC написан на Python и поддерживает из коробки только один вид нотификации-email. &amp;nbsp;Активность по тикетам в системе должна выгружаться в xml-файл для обработки другой системой. Идеальная задача для Camel ;)&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhIz3-xRl3FoC2uTwRkCEzy_c4vdf-8gxSn009wmpTPzpUUonQHi17ZcCx3EV9UX33zp2H9bPpY3jIzk1NYZPw9GUGl0s8CpKyTjxhIZrC1CgxgHAb45HOlxhxlZGoEmweNNwHm/s1600/%D0%A1%D1%85%D0%B5%D0%BC%D0%B0.gif&quot; imageanchor=&quot;1&quot; style=&quot;clear: left; float: left; margin-bottom: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;295&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhIz3-xRl3FoC2uTwRkCEzy_c4vdf-8gxSn009wmpTPzpUUonQHi17ZcCx3EV9UX33zp2H9bPpY3jIzk1NYZPw9GUGl0s8CpKyTjxhIZrC1CgxgHAb45HOlxhxlZGoEmweNNwHm/s320/%D0%A1%D1%85%D0%B5%D0%BC%D0%B0.gif&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
Первоначальная идея решения была основана на работе с email. Однако по причине ее ущербности она была отброшена.&lt;br /&gt;
Быстро окинув взглядом что нам предоставляет Camel я к своему удивлению не нашел такой простой вещи как JDBC-адаптер. Адаптер, в полном смысле этого понятия, с помощью которого возможна передача сообщений из БД в интеграционное решение. Как пример что это такое можно посмотреть на JDBC-адаптер от Websphere ESB&amp;nbsp;&lt;a href=&quot;http://www.ibm.com/developerworks/ru/library/0706_gregory/&quot;&gt;JDBC Adapter&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
Основной принцип двунаправленной коммуникации заключается в следующем. В БД которую необходимо мониторить заводится служебная табличка, которую с некоторым интервалом опрашивает адаптер, забирает данные и передает в интеграционное решение. Данные в служебную табличку складываются с помощью тригера.&lt;br /&gt;
&lt;br /&gt;&lt;/div&gt;
Основные компоненты у на сбудут следующие:&lt;br /&gt;
&lt;br /&gt;
&lt;ol style=&quot;text-align: left;&quot;&gt;
&lt;li&gt;Таймер, инициализирующий событие мониторинга служебной таблички:&amp;nbsp;&lt;/li&gt;
&lt;li&gt;Процессор, который выбирает из таблички сообщения и асинронно передает их на обработку.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;Процессор, непосредственно обрабатывающий сообщения.&amp;nbsp;&lt;/li&gt;
&lt;/ol&gt;
&lt;br /&gt;
&amp;nbsp;Далее нам протребуется  механизм пуллинга, позволяющий отправлять сообщения напрямую из процессора. Для этого в Apache Camel предусмотрен ProducerTemplate.&lt;br /&gt;
&lt;br /&gt;
&amp;nbsp;Итак вот каркас нашего приложения:

camel-context.xml
Внутри camelContext инстанцируется Producer, и далее по ID инжектируется в JDBCAdapter
&lt;br /&gt;
&lt;pre&gt;&lt;code class=&quot;language-xml&quot;&gt;
&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&amp;gt;
&amp;lt;!-- Licensed to the Apache Software Foundation (ASF) under one or more contributor 
 license agreements. See the NOTICE file distributed with this work for additional 
 information regarding copyright ownership. The ASF licenses this file to 
 You under the Apache License, Version 2.0 (the &quot;License&quot;); 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 &quot;AS IS&quot; 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. --&amp;gt;

&amp;lt;!-- Configures the Camel Context --&amp;gt;

&amp;lt;beans xmlns=&quot;http://www.springframework.org/schema/beans&quot;
 xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;
 xsi:schemaLocation=&quot;
       http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
       http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd&quot;&amp;gt;

 &amp;lt;bean id=&quot;JDBCAdapter&quot; class=&quot;foo.JDBCAdapter&quot;&amp;gt;
  &amp;lt;property name=&quot;producer&quot; ref=&quot;change&quot; /&amp;gt;
 &amp;lt;/bean&amp;gt;

 &amp;lt;camelContext xmlns=&quot;http://camel.apache.org/schema/spring&quot;&amp;gt;
  &amp;lt;package&amp;gt;foo&amp;lt;/package&amp;gt;
  &amp;lt;template id=&quot;change&quot; defaultEndpoint=&quot;seda:change&quot; /&amp;gt;
 &amp;lt;/camelContext&amp;gt;

&amp;lt;/beans&amp;gt;
&lt;/code&gt;
&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;
package foo;

import org.apache.camel.Exchange;
import org.apache.camel.Processor;
import org.apache.camel.ProducerTemplate;

public class JDBCAdapter implements Processor {

 private ProducerTemplate producer;

 public void setProducer(ProducerTemplate producer) {
  this.producer = producer;
 }

 @Override
 public void process(Exchange arg0) throws Exception {
  // TODO Auto-generated method stub
  producer.sendBody(&quot;&lt;h&gt;Ok&lt;/h&gt;&quot;);
 }
}
&lt;/code&gt;
&lt;/pre&gt;
Итоговый роутер:
&lt;br /&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;package foo;

import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.spring.Main;

import static org.apache.camel.builder.xml.XPathBuilder.xpath;

/**
 * A Camel Router
 */
public class MyRouteBuilder extends RouteBuilder {

    /**
     * A main() so we can easily run these routing rules in our IDE
     */
    public static void main(String... args) throws Exception {
        Main.main(args);
    }

    /**
     * Let&#39;s configure the Camel routing rules using Java code...
     */
    public void configure() {
     from(&quot;timer://foo?fixedRate=true&amp;amp;period=1000&quot;).processRef(&quot;JDBCAdapter&quot;);
     from(&quot;seda:change&quot;).log(&quot;Yes baby!${body}&quot;);
    }
}
&lt;/code&gt;
&lt;/pre&gt;
Обратите внимание что endpoint у нас определяется как seda вместо direct, что обеспечивает нужную нам асинхронность!
Проверим что все работает как было задумано.
&lt;br /&gt;
&lt;pre&gt;[                          main] MainSupport                    INFO  Apache Camel 2.9.2 starting
[                          main] AnnotationTypeConverterLoader  INFO  Found 3 packages with 15 @Converter classes to load
[                          main] DefaultTypeConverter           INFO  Loaded 170 core type converters (total 170 type converters)
[                          main] DefaultTypeConverter           INFO  Loaded additional 0 type converters (total 170 type converters) in 0.000 seconds
[                          main] SpringCamelContext             INFO  Apache Camel 2.9.2 (CamelContext: camel-1) is starting
[                          main] ManagementStrategyFactory      INFO  JMX enabled. Using ManagedManagementStrategy.
[                          main] ultManagementLifecycleStrategy INFO  StatisticsLevel at All so enabling load performance statistics
[                          main] SpringCamelContext             INFO  Route: route1 started and consuming from: Endpoint[timer://foo?fixedRate=true&amp;amp;period=1000]
[                          main] SpringCamelContext             INFO  Route: route2 started and consuming from: Endpoint[seda://change]
[                          main] SpringCamelContext             INFO  Total 2 routes, of which 2 is started.
[                          main] SpringCamelContext             INFO  Apache Camel 2.9.2 (CamelContext: camel-1) started in 0.234 seconds
[l-1) thread #2 - seda://change] route2                         INFO  Yes baby!&lt;h&gt;Ok&lt;/h&gt;
[l-1) thread #2 - seda://change] route2                         INFO  Yes baby!&lt;h&gt;Ok&lt;/h&gt;
[l-1) thread #2 - seda://change] route2                         INFO  Yes baby!&lt;h&gt;Ok&lt;/h&gt;
[l-1) thread #2 - seda://change] route2                         INFO  Yes baby!&lt;h&gt;Ok&lt;/h&gt;
&lt;/pre&gt;
Как можно догадаться вся логика обработки сообщений будет определятся роутингом:
&lt;br /&gt;
&lt;pre&gt;from(&quot;seda:change&quot;).log(&quot;Yes baby!${body}&quot;);&lt;/pre&gt;
Вернемся к нашей задаче. Trac хранит все изменения по тикетам в табличке ticket_change следующей структуры


Создадим служебную табличку, которая полностью копирует структуру ticket_change, у меня она называется ticket_change_audit
Тригер, который срабатывает при добавлении данных:

&lt;br /&gt;
&lt;pre&gt;CREATE OR REPLACE FUNCTION etk.&quot;notify&quot;()
  RETURNS trigger AS
$BODY$BEGIN
insert into etk.ticket_change_audit (ticket, time, author, field, oldvalue, newvalue) values(NEW.ticket, NEW.time, NEW.author, NEW.field, NEW.oldvalue, NEW.newvalue);
END;$BODY$
  LANGUAGE plpgsql VOLATILE
  COST 100;
ALTER FUNCTION etk.&quot;notify&quot;() OWNER TO tracuser;
COMMENT ON FUNCTION etk.&quot;notify&quot;() IS &#39;dsd&#39;;
&lt;/pre&gt;
&lt;pre&gt;&lt;/pre&gt;
&lt;/div&gt;
Теперь нам осталось доработать логику процессора JDBCAdapter&#39;а.

Создаем следующий бизнес-объект для нашей таблички(JPA и другие ORM не использую специально чтобы не усложнять наш пример)

&lt;br /&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;package com.bssys.trac.bo;

import java.io.Serializable;

public class TicketChange implements Serializable {

 private Integer ticket;

 private Long time;

 private String author;

 private String field;

 private String oldValue;

 private String newValue;

 public Integer getTicket() {
  return ticket;
 }

 public String getField() {
  return field;
 }

 public void setField(String field) {
  this.field = field;
 }

 public void setTicket(Integer ticket) {
  this.ticket = ticket;
 }

 public Long getTime() {
  return time;
 }

 public void setTime(Long time) {
  this.time = time;
 }

 public String getAuthor() {
  return author;
 }

 public void setAuthor(String author) {
  this.author = author;
 }

 public String getOldValue() {
  return oldValue;
 }

 public void setOldValue(String oldValue) {
  this.oldValue = oldValue;
 }

 public String getNewValue() {
  return newValue;
 }

 public void setNewValue(String newValue) {
  this.newValue = newValue;
 }
}&lt;/code&gt;
&lt;/pre&gt;
И добавляем логику работы с БД с помощью спрингового JDBCTemplate. Далее необходимо будет просто реализвать нужную логику обработки сообщения, в теле message Exchange, будет прилетать объект TicketChange

Вот что у нас получилось в итоге:
&lt;br /&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;package com.bssys.trac;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;

import org.apache.camel.Exchange;
import org.apache.camel.Processor;
import org.apache.camel.ProducerTemplate;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;

import com.bssys.trac.bo.TicketChange;

public class JDBCAdapter implements Processor {

 private JdbcTemplate template;
 private ProducerTemplate producer;

 public void setTemplate(JdbcTemplate template) {
  this.template = template;
 }

 public void setProducer(ProducerTemplate producer) {
  this.producer = producer;
 }

 @Override
 public void process(Exchange exchange) throws Exception {
  // TODO Auto-generated method stub

  List&lt;ticketchange&gt; res = template.query(&quot;select * from etk.ticket_change_audit&quot;, new RowMapper&lt;ticketchange&gt;() {

   @Override
   public TicketChange mapRow(ResultSet rs, int rowNum) throws SQLException {
    // TODO Auto-generated method stub
    TicketChange ticketChange = new TicketChange();
    ticketChange.setTicket(rs.getInt(&quot;ticket&quot;));
    ticketChange.setTime(rs.getLong(&quot;time&quot;));
    ticketChange.setAuthor(rs.getString(&quot;author&quot;));
    ticketChange.setField(rs.getString(&quot;field&quot;));
    ticketChange.setOldValue(rs.getString(&quot;oldvalue&quot;));
    ticketChange.setNewValue(rs.getString(&quot;newvalue&quot;));

    return ticketChange;
   }
  });

  for (TicketChange change : res) {

   TicketChangeSendProcessor sendProcessor = new TicketChangeSendProcessor(change);
   producer.send(sendProcessor);

   String delete = &quot;delete from etk.ticket_change_audit WHERE ticket=? and time=? and author=? and field=? and oldvalue=? and newvalue=?&quot;;
   template.update(delete, change.getTicket(), change.getTime(), change.getAuthor(), change.getField(), change.getOldValue(), change.getNewValue());

  }

  exchange.getOut().setBody(res);
 }
}
&lt;code class=&quot;language-java&quot;&gt;
&lt;/code&gt;&lt;/ticketchange&gt;&lt;/ticketchange&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
</content><link rel='replies' type='application/atom+xml' href='http://jser.blogspot.com/feeds/6012425591508137334/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/37848572/6012425591508137334' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/37848572/posts/default/6012425591508137334'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/37848572/posts/default/6012425591508137334'/><link rel='alternate' type='text/html' href='http://jser.blogspot.com/2012/05/jdbc-polling-adapter-c-apache-camel.html' title='JDBC Polling Adapter c помощью Apache Camel'/><author><name>JSer</name><uri>http://www.blogger.com/profile/17455716060338444232</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhIz3-xRl3FoC2uTwRkCEzy_c4vdf-8gxSn009wmpTPzpUUonQHi17ZcCx3EV9UX33zp2H9bPpY3jIzk1NYZPw9GUGl0s8CpKyTjxhIZrC1CgxgHAb45HOlxhxlZGoEmweNNwHm/s72-c/%D0%A1%D1%85%D0%B5%D0%BC%D0%B0.gif" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-37848572.post-4981848454325562303</id><published>2012-03-30T01:44:00.000+10:00</published><updated>2012-09-11T21:45:52.396+10:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Apache Camel"/><title type='text'>Блеск и нищета Apache Camel</title><content type='html'>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
В этой статье я расскажу про фееричный фейл Apache Camel , на реальной бизнес-задаче.&lt;br /&gt;
Возникла задача по обработке 5000000 zip-файлов (O_o). Каждый файлик необходимо распаковать, достать из архива XML файл, распарсить его, и некоторую информацию дописать в лог в определенном формате.&lt;br /&gt;
&lt;br /&gt;
&amp;nbsp;Идеальная задача для Apache Camel подумал я и бросился в бой!&lt;br /&gt;
&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;Один роутинг и один процессор, вот что у меня вышло.
&lt;br /&gt;
&lt;br /&gt;
Роутинг:
&lt;pre&gt;
&lt;code class=&quot;language-java&quot;&gt;public void configure() {
 from(&quot;file:c://data/inbox?noop=true&quot;).
  log(&quot;${file:name}&quot;).
  convertBodyTo(InputStream.class).
  processRef(&quot;ZipProcess&quot;).
 to(&quot;file:c://data/outbox/?fileExist=Append&amp;fileName=report.txt&quot;);
}
&lt;/code&gt;
&lt;/pre&gt;

Процессор:
&lt;pre&gt;
&lt;code class=&quot;language-java&quot;&gt;package com.bssys.zoom.zipbot;

import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

import org.apache.camel.Exchange;
import org.apache.camel.Processor;

public class ZipProcess implements Processor {

 @Override
 public void process(Exchange exchange) throws Exception {
  // TODO Auto-generated method stub
  InputStream is = exchange.getIn().getBody(InputStream.class);
  ZipInputStream zipInputStream = new ZipInputStream(new BufferedInputStream(is));

  String res = null;
  ZipEntry zipEntry = null;
  while ((zipEntry = zipInputStream.getNextEntry()) != null) {
   String fileName = zipEntry.getName();
   if (fileName.endsWith(&quot;.XML&quot;)) {
    ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
    byte buf[] = new byte[1024];
    int count = -1;
    while ((count = zipInputStream.read(buf)) != -1) {
     byteArrayOutputStream.write(buf, 0, count);
    }

    String xml = new String(byteArrayOutputStream.toByteArray());
    //... Тут парсим XML и выбираем нужные данные
    res = String.format(&quot;%s:\r\n&quot;, fileName);
   }
   zipInputStream.closeEntry();
  }
  zipInputStream.close();
  exchange.getOut().setBody(res);
 }
}&lt;/code&gt;
&lt;/pre&gt;

Эта штука еле взлетела отожрав 5гб оперативной памяти. Скорость работы вполне приличная 1000 файлов за 300ms. Производительность вполне достаточная, если учесть что парсер можно оптимизировать или совсем отказаться от него, используя indexOf.
Через пару часов работы, заглядывая в результирующий файлик, всплыло неладное. Данные повторялись! Т.е. Camel брал один и тотже файл по нескольку раз. На моем тестовом экземпляре в 1000 файлов все работало без нареканий. Заглянув документацию заметил следующий комментарий по параметру noop:
&lt;pre&gt;
If true, the file is not moved or deleted in any way. This option is good for readonly data, or for ETL type requirements. If noop=true, Camel will set idempotent=true as well, to avoid consuming the same files over and over again.
&lt;/pre&gt;

Этот параметр я проставляю для конечной точки, что бы Camel не удалял и не перемещал файлы из исходной папки. Если проставляется noop=true то автоматически проставляется также параметр idempotent=true
&lt;pre&gt;
Option to use the Idempotent Consumer EIP pattern to let Camel skip already processed files. Will by default use a memory based LRUCache that holds 1000 entries. If noop=true then idempotent will be enabled as well to avoid consuming the same files over and over again.
&lt;/pre&gt;

Все стало ясно. Camel в режиме мониторинга устанавливает кэш в 1000 файлов, чтобы не забирать уже обработанные файлы. И просто не предназначен для такой ситуации с 5000000 файлов. Можно не устанавливать флаг noop, но при этом сильно режется производительность. Вот этого я никак не ожидал. Хотя по логике ничего работа Camel Не вызывает нареканий. В режиме мониторинга мы упираемся в этуже проблему.

В результате без использования Camel, была написана программка, которая и выполнила эту непосильную задачу, заняв при этом меньшее количество памяти. Привожу часть просто для интереса.
&lt;pre&gt;
&lt;code class=&quot;language-java&quot;&gt;public void go(String inbox, String outbox) throws Exception {

  File inboxDir = new File(inbox);

  if (inboxDir.isDirectory()) {
   String[] listFiles = inboxDir.list();
   System.out.println(&quot;Count files: &quot; + listFiles.length);
   BufferedOutputStream bufferedOutputStream = null;
   try {
    bufferedOutputStream = new BufferedOutputStream(new FileOutputStream(new File(outbox + &quot;/report.txt&quot;)));
    long count = 0;
    for (String fileName : listFiles) {
     count++;
     System.out.println(count + &quot;:&quot; + fileName);
     FileInputStream fileInputStream = new FileInputStream(new File(inbox + &quot;/&quot; + fileName));
     try{
      String res = process(fileInputStream);
      bufferedOutputStream.write(res.getBytes());
     }catch(Exception e){
      FileOutputStream fileOutputStream = new FileOutputStream(new File(outbox + &quot;/fail.txt&quot;), true);
      fileOutputStream.write((fileName+&quot;\r\n&quot;).getBytes());
      fileInputStream.close();
     }
    }
    bufferedOutputStream.flush();

   } finally {
    if (bufferedOutputStream != null) {
     bufferedOutputStream.close();
    }
   }

  }
 }

&lt;/code&gt;
&lt;/pre&gt;

Метод process, собственно на 99% схож с кодом процессора.

Удачи!
&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jser.blogspot.com/feeds/4981848454325562303/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/37848572/4981848454325562303' title='Комментарии: 4'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/37848572/posts/default/4981848454325562303'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/37848572/posts/default/4981848454325562303'/><link rel='alternate' type='text/html' href='http://jser.blogspot.com/2012/03/apache-camel.html' title='Блеск и нищета Apache Camel'/><author><name>JSer</name><uri>http://www.blogger.com/profile/17455716060338444232</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-37848572.post-2302703279055437756</id><published>2012-03-30T00:24:00.002+10:00</published><updated>2012-09-11T21:46:02.257+10:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Apache Camel"/><title type='text'>Файловый интерфейс для Base64 кодирования/декодирования с помощью Apache Camel</title><content type='html'>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
Чем мне нравится Apache Camel, так это готовыми компонентами &amp;nbsp;конечных точек(endpoints) на все случаи жизни. В моей жизни встретилась задача, по Base64 кодированию/декодированию множества файлов.&lt;br /&gt;
&lt;br /&gt;
Схема работы приложения следующая:&lt;br /&gt;
В соответствующие папки будут кидаться файлики, результат будет формироваться в папке output:&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;c://data/encode&lt;/b&gt;&lt;br /&gt;
&lt;b&gt;c://data/decode&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;c://data/output&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
Идеальная задача для Apache Camel!&lt;br /&gt;
&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;
Как обычно создаем Maven Project на основе уже известного нам&amp;nbsp;&lt;span style=&quot;background-color: white; color: #333333; font-family: Verdana, Arial, sans-serif; font-size: 13px; line-height: 16px;&quot;&gt;camel-archetype-java.&lt;/span&gt;&lt;br /&gt;
Добавляем в зависимости к проекту сommon-codec, с помощью которого будет проводиться кодирование/декодирование

&lt;br /&gt;
&lt;pre&gt; 
&lt;code class=&quot;language-xml&quot;&gt;&amp;lt;dependency&amp;gt;
 &amp;lt;groupId&amp;gt;commons-codec&amp;lt;/groupId&amp;gt;
 &amp;lt;artifactId&amp;gt;commons-codec&amp;lt;/artifactId&amp;gt;
 &amp;lt;version&amp;gt;1.6&amp;lt;/version&amp;gt;
&amp;lt;/dependency&amp;gt;&lt;/code&gt;
&lt;/pre&gt;
Создаем два процессора которые будут кодировать и раскодировать поступающие сообщения.&lt;br /&gt;
&lt;br /&gt;
Декодировщик:
&lt;br /&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;package com.bssys.base64.convert;

import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;

import org.apache.camel.Exchange;
import org.apache.camel.Processor;
import org.apache.commons.codec.binary.Base64;

public class Base64DecodeProcessor implements Processor {

 @Override
 public void process(Exchange exchange) throws Exception {
  // TODO Auto-generated method stub
  InputStream is = exchange.getIn().getBody(InputStream.class);
  String fileName = (String)exchange.getIn().getHeader(Exchange.FILE_NAME);
  BufferedInputStream bufferedInputStream = new BufferedInputStream(is);
  ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
  byte buf[] = new byte[1024];
  int pos=-1;
  while((pos= bufferedInputStream.read(buf))!=-1){
   byteArrayOutputStream.write(buf, 0, pos);
  }
  bufferedInputStream.close();
  byte[] encodeBytes = Base64.decodeBase64(byteArrayOutputStream.toByteArray());
  exchange.getOut().setHeader(Exchange.FILE_NAME, fileName);
  exchange.getOut().setBody(encodeBytes);
 }
}
&lt;/code&gt;
&lt;/pre&gt;
Кодировщик:
&lt;br /&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;package com.bssys.base64.convert;

import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;

import org.apache.camel.Exchange;
import org.apache.camel.Processor;
import org.apache.commons.codec.binary.Base64;

public class Base64EncodeProcessor implements Processor {

 @Override
 public void process(Exchange exchange) throws Exception {
  // TODO Auto-generated method stub
  InputStream is = exchange.getIn().getBody(InputStream.class);
  String fileName = (String)exchange.getIn().getHeader(Exchange.FILE_NAME);
  BufferedInputStream bufferedInputStream = new BufferedInputStream(is);
  ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
  byte buf[] = new byte[1024];
  int pos = -1;
  while ((pos = bufferedInputStream.read(buf)) != -1) {
   byteArrayOutputStream.write(buf, 0, pos);
  }
  bufferedInputStream.close();
  byte[] encodeBytes = Base64.encodeBase64(byteArrayOutputStream
    .toByteArray());
  exchange.getOut().setHeader(Exchange.FILE_NAME, fileName);
  exchange.getOut().setBody(encodeBytes);

 }
}
&lt;/code&gt;
&lt;/pre&gt;
&lt;br /&gt;
Добавляем наши 2 процессора в spring-контекст

&lt;br /&gt;
&lt;pre&gt;&lt;code class=&quot;language-xml&quot;&gt;&amp;lt;bean id=&quot;Base64Encode&quot; class=&quot;com.bssys.base64.convert.Base64EncodeProcessor&quot;/&amp;gt;     
&amp;lt;bean id=&quot;Base64Decode&quot; class=&quot;com.bssys.base64.convert.Base64DecodeProcessor&quot;/&amp;gt;&lt;/code&gt;
&lt;/pre&gt;
И модифицируем MyRouteBuilder.java добавив в него 2 роутера:
&lt;br /&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;public void configure() {
    // TODO create Camel routes here.
    from(&quot;file:c://data/encode&quot;).convertBodyTo(InputStream.class).to(&quot;Base64Encode&quot;).to(&quot;file:c://data/outbox&quot;);
    from(&quot;file:c://data/decode&quot;).convertBodyTo(InputStream.class).to(&quot;Base64Decode&quot;).to(&quot;file:c://data/outbox&quot;);
}
&lt;/code&gt;
&lt;/pre&gt;
Запускаем и наслаждаемся. Apache Camel слушает заданные папки, и при поступлении файликов выполняет за нас всю черную работу.

&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jser.blogspot.com/feeds/2302703279055437756/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/37848572/2302703279055437756' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/37848572/posts/default/2302703279055437756'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/37848572/posts/default/2302703279055437756'/><link rel='alternate' type='text/html' href='http://jser.blogspot.com/2012/03/base64-apache-camel.html' title='Файловый интерфейс для Base64 кодирования/декодирования с помощью Apache Camel'/><author><name>JSer</name><uri>http://www.blogger.com/profile/17455716060338444232</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry></feed>