Fabric also allows a bundle to publish services and let them be consumed by other bundles. Not just by bundles in the same container, but in other bundles as long as publishers and consumers are connected in one Fabric.
I'm here not to to tell you how to use JBoss Fuse or what it is. But to tell you that it's been hard to find references about how to publish services and consume them in distributed environment. So, I want to share my experience to handle that with blueprint or with Spring DM 1.x.x based on scattered sources of information I have read for weeks.
Let's get started!
Assume that you have a publisher which publishes a service to reverse text. It is published in container child1. Not only that, but you also have a consumer to the service in other container named child2. Child1 and child2 are children from a Fabric. You can create them in the root container by using
fabric:create --clean
fabric:container-create-child root child 2
Back to the service, you have to create 3 bundles:
- An API jar, which defines interfaces used by the publisher and the consumer.
- A publisher OSGi bundle, which creates the implementations and publish them as services.
- A consumer OSGi bundle, which consumes the services.
Note that is is recommended to split APIs and services into two separated bundles.
The API jar
It only contains one java file: src/main/java/**/StringReverseService.java
package id.web.michsan.fuse.api; public interface StringReverseService { public String reverse(String text); }
The Publisher
It contains the implementation of the interface which is also one java file src/main/java/**/StringReverseServiceImpl.java
package id.web.michsan.fuse.publisher; import id.web.michsan.fuse.api.StringReverseService; public class StringReverseServiceImpl implements StringReverseService { public String reverse(String text) { return new StringBuffer(text).reverse().toString(); } }
It also contains OSGi service definition in blueprint format. You should prefer Blueprint to Spring DM as it's standard and widely supported by OSGi containers.
src/main/resources/OSGI-INF/blueprint/bundle-context.xml:
<?xml version="1.0" encoding="UTF-8"?> <blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"> <bean id="myStringReverseService" class="id.web.michsan.fuse.publisher.StringReverseServiceImpl"> <!-- <property name="verbose" value="${verbose}" /> --> </bean> </blueprint>
src/main/resources/OSGI-INF/blueprint/bundle-context-osgi.xml:
The key service.exported.interfaces in <service-properties> <?xml version="1.0" encoding="UTF-8"?> <blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0" xmlns:cm="http://aries.apache.org/blueprint/xmlns/blueprint-cm/v1.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <!-- Load configuration file defined in persistent-id --> <cm:property-placeholder persistent-id="id.web.michsan.fuse.publisher"> <cm:default-properties> <cm:property name="verbose" value="false" /> </cm:default-properties> </cm:property-placeholder> <!-- Export as OSGi services --> <service ref="myStringReverseService" interface="id.web.michsan.fuse.api.StringReverseService"> <service-properties> <entry key="service.exported.interfaces" value="*" /> </service-properties> </service> </blueprint>
In the pom.xml, aside of using <packaging>bundle</packaging>, you need to include:
<build> <plugins> <!-- OSGi bundle generation --> <plugin> <groupId>org.apache.felix</groupId> <artifactId>maven-bundle-plugin</artifactId> <version>2.3.7</version> <extensions>true</extensions> <configuration> <instructions> <Private-Package>id.web.michsan.fuse.publisher.*</Private-Package> <Import-Package> org.springframework.aop, org.springframework.aop.framework, org.aopalliance.aop, * </Import-Package> <Export-Package></Export-Package> <_removeheaders>Import-Service,Export-Service</_removeheaders> <Bundle-Description>${project.description}</Bundle-Description> </instructions> </configuration> </plugin> </plugins> </build>
The Consumer
In consumer bundle, you have to create a class which consumes the service:
package id.web.michsan.fuse.consumer; import id.web.michsan.fuse.api.StringReverseService; import java.net.InetSocketAddress; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; import org.apache.mina.core.service.IoHandlerAdapter; import org.apache.mina.core.session.IoSession; import org.apache.mina.filter.codec.ProtocolCodecFilter; import org.apache.mina.filter.codec.textline.TextLineCodecFactory; import org.apache.mina.transport.socket.SocketAcceptor; import org.apache.mina.transport.socket.nio.NioSocketAcceptor; public class RequestHandler { private int port; private StringReverseService stringReverseService; private SocketAcceptor acceptor; @PostConstruct public void afterPropertiesSet() throws Exception { acceptor = new NioSocketAcceptor(); acceptor.getFilterChain().addLast("textLineCodec", new ProtocolCodecFilter(new TextLineCodecFactory())); acceptor.setHandler(new IoHandlerAdapter() { @Override public void messageReceived(IoSession session, Object message) throws Exception { String text = (String) message; String result = stringReverseService.reverse(text); session.write(result); } }); acceptor.setReuseAddress(true); acceptor.bind(new InetSocketAddress(port)); } @PreDestroy public void destroy() throws Exception { if (acceptor != null) acceptor.unbind(); } /** ---- Accessors ---------------------------------- */ public void setPort(int port) { this.port = port; } public void setStringReverseService( StringReverseService stringReverseService) { this.stringReverseService = stringReverseService; } }
For the src/main/resources/OSGI-INF/blueprint/bundle-context-osgi.xml you should create like the following:
<?xml version="1.0" encoding="UTF-8"?> <blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0" xmlns:cm="http://aries.apache.org/blueprint/xmlns/blueprint-cm/v1.0.0"> <!-- Load configuration from file defined by persistent-id attribute --> <cm:property-placeholder persistent-id="id.web.michsan.fuse.consumer"> <cm:default-properties> <cm:property name="port" value="4321" /> </cm:default-properties> </cm:property-placeholder> <!-- Import OSGi services --> <reference id="bokuNoStringReverseService" interface="id.web.michsan.fuse.api.StringReverseService" /> </blueprint>
While the src/main/resources/OSGI-INF/blueprint/bundle-context.xml is:
<?xml version="1.0" encoding="UTF-8"?> <blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"> <bean class="id.web.michsan.fuse.consumer.RequestHandler" init-method="afterPropertiesSet"> <property name="port" value="${port}" /> <property name="stringReverseService" ref="bokuNoStringReverseService" /> <property name="userDao" ref="bokuNoUserDao" /> </bean> </blueprint>The features definition file
When using Fabric, we have to install all features with profile
fuse-features.xml:
<features> <feature name="michsan-publisher" version="1.0.0"> <bundle>fab:mvn:id.web.michsan.fuse/fuse-api/0.0.1-SNAPSHOT</bundle> <bundle>mvn:id.web.michsan/fuse-publisher/0.0.1-SNAPSHOT</bundle> </feature> <feature name="michsan-consumer" version="1.0.0"> <bundle>fab:mvn:id.web.michsan.fuse/fuse-api/0.0.1-SNAPSHOT</bundle> <bundle>mvn:org.apache.mina/mina-core/2.0.7</bundle> <bundle>mvn:id.web.michsan.fuse/fuse-consumer/0.0.1-SNAPSHOT</bundle> </feature> </features>
Installation
From a fresh JBoss Fuse Full zip file, start a default container (./bin/fuse)
fabric:create --clean
After setting your password, wait for a while until all required bundles started (check from command: list)
Create children containers.
fabric:container-create-child root child 2
As we know, we should use profile to install features
fabric:profile-create --parents jboss-fuse-minimal --parents dosgi michsan-base fabric:profile-edit -r file:///path/to/michsan-features.xml michsan-base fabric:profile-create --parents michsan-base michsan-publisher fabric:profile-edit -f michsan-publisher michsan-publisher fabric:profile-create --parents michsan-base michsan-consumer fabric:profile-edit -f michsan-consumer michsan-consumer
Install into two different containers after both of them started well (check from command: container-list)
fabric:container-add-profile child1 michsan-publisher fabric:container-add-profile child2 michsan-consumer
1 comment:
Fabric also allows a bundle to publish services and let them be consumed by other bundles. Not just by bundles in the same container, but in other bundles as long as publishers and consumers are connected in one Fabric. kontraktor murah di jogja
Post a Comment