CXF - JMS z wykorzystaniem WebSphere MQ

Autor: Marcin Kasiński
15.08.2012 20:06:49 +0200

Biblioteka CXF pozwala w łatwy sposób implementować usługi Web Services bez zbytniego skupiania się na kwestiach technologicznych. Używając tej biblioteki możemy położyć nacisk na implementacje samej logiki operacji jakie chcemy udostępnić jako Web Service. Wiele narzędzi programistycznych posiada kreatory w łatwy sposób generujący aplikacje webową z biblioteką CXF. Standardowo przy generowaniu takiej aplikacji mamy nasze operacje udostępnione jako Web Service z wykorzystaniem protokołu HTTP.

W poniższym artykule postaram się opisać użycie transportu JMS w aplikacji CXF z wykorzystaniem IBM WebSphere MQ. Zakładam, że czytający znają podstawy CXF i potrafią wygenerować prostą aplikację CXF udostępnianą poprzez Web Service.

Implementacja usługi

Załóżmy, że mamy poniższą implementacje naszej usługi Web Services. Zaimplementowaliśmy prostą operację hello przyjmującą na wejściu pole String i w odpowiedzi zwracającą ten sam typ.

package mkpackage;

import java.io.PrintStream;
import javax.jws.WebService;

@WebService(targetNamespace = "http://mkpackage/", endpointInterface = "mkpackage.HelloMgrInterface", 
portName = "HelloMgrImplPort", serviceName = "HelloMgrImplService")
public class HelloMgrImpl implements HelloMgrInterface {

	public String hello(String in) {
		System.out.println("in");
		
		return "Hello " + in;
	}

}


package mkpackage;


import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebService;

import mkpackage.jaxws.HelloResponse;

@WebService(name = "HelloMgrInterface", targetNamespace = "http://mkpackage/")
public abstract interface HelloMgrInterface {

	@WebMethod(operationName = "hello", action = "urn:Hello")
	public abstract String hello(@WebParam(name = "arg0") String paramString);
}

Konfiguracja usługi

Mając taki kod po wygenerowaniu aplikacji webowej CXF (np. w środowisku eclipse) zostanie wygenerowany poniższy plik beans.xml zawierający konfiguracje naszej usługi.


<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jaxws="http://cxf.apache.org/jaxws" 
	xmlns:context="http://www.springframework.org/schema/context"
	
	xsi:schemaLocation="http://www.springframework.org/schema/beans 
	http://www.springframework.org/schema/beans/spring-beans-2.5.xsd   
	http://www.springframework.org/schema/context 
	http://www.springframework.org/schema/context/spring-context.xsd   
	http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd">

	<jaxws:endpoint xmlns:tns="http://mkpackage/" id="hellomgr"
		implementor="mkpackage.HelloMgrImpl" wsdlLocation="wsdl/hellomgrimpl.wsdl"
		endpointName="tns:HelloMgrImplPort" serviceName="tns:HelloMgrImplService"
		address="/HelloMgrImplPort">
		<jaxws:features>
			<bean class="org.apache.cxf.feature.LoggingFeature" />
		</jaxws:features>
	</jaxws:endpoint>

</beans>

Udostępnienie usługi dodatkowo przez JMS

W ten oto sposób mamy działający Web Service udostępniany poprzez HTTP. Obecnie przechodzimy do zasadniczej części zadania. Postaramy się nasz serwis udostępnić poprzez JMS ( w naszym przypadku WebSphere MQ ).

Pierwszą rzeczą jaką musimy zrobić jest określenie nowej definicji udostępnienia naszej usługi. Jedną definicje już mamy określającą dostęp poprzez HTTP. Teraz określimy, że naszą usługę udostępnimy poprzez usługę JMS.



	<jaxws:endpoint id="hellomgr.jms" address="jms://" transportId="http://cxf.apache.org/transports/jms" 
	implementor="mkpackage.HelloMgrImpl">

		<jaxws:features>
			<bean class="org.apache.cxf.feature.LoggingFeature">
			</bean>

			<bean class="org.apache.cxf.transport.jms.JMSConfigFeature"
				p:jmsConfig-ref="jmsConfigCached" />
		</jaxws:features>

	</jaxws:endpoint>

	

Następnie musimy określić podstawowe parametry naszej usługi takie jak kolejka , z której usługa będzie odczytywała zapytania, na którą kolejkę będzie wysyłała odpowiedzi, ilość wątków obsługujących zlecenia oraz definicje połączenia do WebSphere MQ.

	<bean id="jmsConfigCached" class="org.apache.cxf.transport.jms.JMSConfiguration"
		p:connectionFactory-ref="jmsConnectionFactoryCached"
		p:targetDestination="MQSI.APPCXFQM1.QIN" p:replyDestination="MQSI.APPCXFQM3.QIN"
		p:concurrentConsumers="10" p:maxConcurrentConsumers="10" p:cacheLevel="3"
		p:wrapInSingleConnectionFactory="false" />

Ważniejsze parametry to:

  • connectionFactory-refdefinicja parametrów połączeniowych JMS
  • targetDestinationKolejka na której nasłuchuje serwis
  • replyDestinationKolejka na którą serwis wysyła odpowiedzi
  • concurrentConsumersMinimalna ilość tątków czytających z kolejki
  • maxConcurrentConsumersMaksymalna ilość tątków czytających z kolejki
  • cacheLevelPoziom cache jaki obsługuje listener JMS
  • wrapInSingleConnectionFactoryCzy używa jednego połączenia JMS

Definicje połączenia do WebSphere MQ opisujemy poniższym beanem. Parametry będą pobierane z dodatkowego pliku o którym później.

	<bean id="jmsConnectionFactoryCached"
		class="org.springframework.jms.connection.CachingConnectionFactory">

		<property name="targetConnectionFactory">
			<bean class="com.ibm.mq.jms.MQQueueConnectionFactory">
				<property name="queueManager" value="${mq.queueManager}" />
				<property name="hostName" value="${mq.hostName}" />
				<property name="channel" value="${mq.channel}" />
				<property name="transportType">
					<util:constant static-field="com.ibm.mq.jms.JMSC.MQJMS_TP_CLIENT_MQ_TCPIP" />
				</property>
				<property name="port" value="${mq.port}" />
			</bean>
		</property>

		<property name="sessionCacheSize" value="10" />
		<property name="cacheProducers" value="false" />
		<property name="cacheConsumers" value="false" />

	</bean>

Ważniejsze parametry to:

  • queueManagerMenadzer kojejek MQ
  • hostNameHost menadzer kojejek MQ
  • channelKanał połączeniowy menadzera kojejek MQ
  • transportTypeTyp połączenia do menadzera kojejek MQ
  • portPort na którym nasłuchuje menadzer kojejek MQ

Całą definicje CXF JMS możemy opisać poprzez poniższą konfigurację umieszczoną w pliku beansjms.xml .

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:p="http://www.springframework.org/schema/p" 
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:tx="http://www.springframework.org/schema/tx" 
	xmlns:util="http://www.springframework.org/schema/util"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
	xmlns:jaxws="http://cxf.apache.org/jaxws"
	xsi:schemaLocation="
      http://cxf.apache.org/core http://cxf.apache.org/schemas/core.xsd
      http://www.springframework.org/schema/context 
	  http://www.springframework.org/schema/context/spring-context.xsd
      http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
      http://www.springframework.org/schema/beans 
	  http://www.springframework.org/schema/beans/spring-beans.xsd
      http://www.springframework.org/schema/util 
	  http://www.springframework.org/schema/util/spring-util.xsd
      http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd">

	<bean id="jmsConnectionFactoryCached"
		class="org.springframework.jms.connection.CachingConnectionFactory">

		<property name="targetConnectionFactory">
			<bean class="com.ibm.mq.jms.MQQueueConnectionFactory">
				<property name="queueManager" value="${mq.queueManager}" />
				<property name="hostName" value="${mq.hostName}" />
				<property name="channel" value="${mq.channel}" />
				<property name="transportType">
					<util:constant static-field="com.ibm.mq.jms.JMSC.MQJMS_TP_CLIENT_MQ_TCPIP" />
				</property>
				<property name="port" value="${mq.port}" />
			</bean>
		</property>

		<property name="sessionCacheSize" value="10" />
		<property name="cacheProducers" value="false" />
		<property name="cacheConsumers" value="false" />

	</bean>

	<bean id="jmsConfigCached" class="org.apache.cxf.transport.jms.JMSConfiguration"
		p:connectionFactory-ref="jmsConnectionFactoryCached"
		p:targetDestination="MQSI.APPCXFQM1.QIN" p:replyDestination="MQSI.APPCXFQM3.QIN"
		p:concurrentConsumers="10" p:maxConcurrentConsumers="10" p:cacheLevel="3"
		p:wrapInSingleConnectionFactory="false" />

	<jaxws:endpoint id="hellomgr.jms" address="jms://"
		transportId="http://cxf.apache.org/transports/jms" implementor="mkpackage.HelloMgrImpl">

		<jaxws:features>
			<bean class="org.apache.cxf.feature.LoggingFeature">
			</bean>

			<bean class="org.apache.cxf.transport.jms.JMSConfigFeature"
				p:jmsConfig-ref="jmsConfigCached" />
		</jaxws:features>

	</jaxws:endpoint>

</beans>

Dodatkowo będzie nam potrzebny plik mq.properties z konfiguracją połączenia MQ


mq.queueManager=QM_WBRK
mq.hostName=localhost
mq.channel=MKSVRCONN
mq.port=1417

Powyższe pliki powinny być uwzględnione w głownej konfiguracji CXF. Zakłdając, że pliki beansjms.xml oraz mq.properties znajdują się w katalogu WEB-INF naszej aplikacji webowej, gdzie również znajduje się nasza główna konfiguracja CXF plik beans.xml importu dokonujemy poprze poniższy kod umieszczony wewnątzr sekcji beans XML.


	<context:property-placeholder
		location="WEB-INF/mq.properties" />

			<import resource="beansjms.xml" />

Mając tak zdefiniowaną konfiguracje nasza aplikacja webowa udostępni naszą usługę poprzez HTTP oraz JMS.

Klient CXF

Dla przetestowania usługi za pomocą transportu JMS możemy napisać prostego klienta:

		ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(
				"file:in/client_context_JMS.xml");
		HelloMgrInterface client = (HelloMgrInterface) ctx
				.getBean("helloClient");

		System.out.println("client doitSynch " + client.hello("Marcin"));

W pliku konfiguracyjnym musimy zdefiniwać definicje klienta wskazującą na to jakiego transportu klient użyje do komunkacji z serwisem oraz jakiej definicji klasu użyje do opisu usługi. Klas ata to w naszym przypadku interjes JAVA HelloMgrInterface jaki musimy "przekazać" ze strony serwerowej do naszego klienta.

	<jaxws:client id="helloClient" serviceClass="mkpackage.HelloMgrInterface"
		address="jms://">
		<jaxws:features>
			<bean xmlns="http://www.springframework.org/schema/beans" 
			class="org.apache.cxf.transport.jms.JMSConfigFeature"
				p:jmsConfig-ref="jmsConfig" />
			<cxf:logging />
		</jaxws:features>
	</jaxws:client>

Cała konfiguracja client_context_JMS.xml jest bardzo podobna do konfiguracji apliakcji która udostępnia usługi, więc dodatkowo nie będą jej omawiał.

<?xml version="1.0" encoding="UTF-8"?>


<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
	xmlns:http="http://cxf.apache.org/transports/http/configuration"
	xmlns:p="http://www.springframework.org/schema/p" 
	xmlns:jms="http://cxf.apache.org/transports/jms"
	xmlns:jaxws="http://cxf.apache.org/jaxws" 
	xmlns:httpj="http://cxf.apache.org/transports/http-jetty/configuration"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:util="http://www.springframework.org/schema/util" 
	xmlns:sec="http://cxf.apache.org/configuration/security"
	xmlns:cxf="http://cxf.apache.org/core"
	xsi:schemaLocation="
        http://www.springframework.org/schema/beans                 
		http://www.springframework.org/schema/beans/spring-beans.xsd
        http://cxf.apache.org/transports/http/configuration         
		http://cxf.apache.org/schemas/configuration/http-conf.xsd
        http://cxf.apache.org/transports/http-jetty/configuration   
		http://cxf.apache.org/schemas/configuration/http-jetty.xsd
        http://cxf.apache.org/configuration/security                
		http://cxf.apache.org/schemas/configuration/security.xsd
        http://www.springframework.org/schema/context 				
		http://www.springframework.org/schema/context/spring-context.xsd
        http://cxf.apache.org/jaxws 								
		http://cxf.apache.org/schemas/jaxws.xsd
        http://cxf.apache.org/transports/jms 						
		http://cxf.apache.org/schemas/configuration/jms.xsd
        http://www.springframework.org/schema/util 					
		http://www.springframework.org/schema/util/spring-util.xsd
        http://cxf.apache.org/core 									
		http://cxf.apache.org/schemas/core.xsd
        ">

		<context:property-placeholder location="file:in/mq.properties" />

	<jaxws:client id="helloClient" serviceClass="mkpackage.HelloMgrInterface"
		address="jms://">
		<jaxws:features>
			<bean xmlns="http://www.springframework.org/schema/beans" 
			class="org.apache.cxf.transport.jms.JMSConfigFeature"
				p:jmsConfig-ref="jmsConfig" />
			<cxf:logging />
		</jaxws:features>


	</jaxws:client>

	<bean id="jmsConnectionFactory"
		class="org.springframework.jms.connection.SingleConnectionFactory">


		<property name="targetConnectionFactory">
			<bean class="com.ibm.mq.jms.MQQueueConnectionFactory">
				<property name="queueManager" value="${mq.queueManager}" />
				<property name="hostName" value="${mq.hostName}" />
				<property name="channel" value="${mq.channel}" />
				<property name="transportType">
					<util:constant static-field="com.ibm.mq.jms.JMSC.MQJMS_TP_CLIENT_MQ_TCPIP" />
				</property>
				<property name="port" value="${mq.port}" />
			</bean>
		</property>
	</bean>


	<bean id="jmsConnectionFactorycaching"
		class="org.springframework.jms.connection.CachingConnectionFactory">

		<property name="targetConnectionFactory">
			<bean class="com.ibm.mq.jms.MQQueueConnectionFactory">
				<property name="queueManager" value="${mq.queueManager}" />
				<property name="hostName" value="${mq.hostName}" />
				<property name="channel" value="${mq.channel}" />
				<property name="transportType">
					<util:constant static-field="com.ibm.mq.jms.JMSC.MQJMS_TP_CLIENT_MQ_TCPIP" />
				</property>
				<property name="port" value="${mq.port}" />
			</bean>
		</property>
		<!--property name="username" value="${noe.mqseries.username}" /> </bean> 
			</property -->

		<property name="sessionCacheSize" value="10" />
		<property name="cacheProducers" value="false" />
		<property name="cacheConsumers" value="false" />

	</bean>

	<!-- p:deliveryMode="2" persistent p:deliveryMode="2" non persistent -->

	<bean id="jmsConfig" class="org.apache.cxf.transport.jms.JMSConfiguration"
		p:connectionFactory-ref="jmsConnectionFactory" p:targetDestination="MQSI.APPCXFQM1.QIN"
		p:replyDestination="MQSI.APPCXFQM3.QIN" p:concurrentConsumers="10"
		p:maxConcurrentConsumers="10" p:cacheLevel="3"
		p:wrapInSingleConnectionFactory="false" p:receiveTimeout="14000"
		p:deliveryMode="2" />



</beans>

W ten oto sposób mamy usługę udostępnioną poprzez protokół HTTP oraz JMS oraz klienta tej usługi wywołującą ją poprzez JMS.


powrót
Zachęcam do przedstawienia swoich uwag i opinii w polu komentarzy.

Komentarze

Dodaj Komentarz