= Provides a JavaEE Event <--> RabbitMQ bridge.
Patrick Reinhart https://github.com/reinhapa[@reinhapa]
:project-name: rabbitmq-cdi
:group-name: net.reini
:project-full-path: reinhapa/{project-name}
:github-branch: master
image:https://img.shields.io/badge/license-MIT-blue.svg["MIT License", link="https://github.com/{project-full-path}/blob/{github-branch}/LICENSE"]
image:https://img.shields.io/maven-central/v/{group-name}/{project-name}.svg?label=Maven%20Central["Maven Central", link="https://search.maven.org/search?q=g:%22{group-name}%22%20AND%20a:%22{project-name}%22"]
image:https://javadoc.io/badge2/{group-name}/{project-name}/javadoc.svg["javadoc", link="https://javadoc.io/doc/{group-name}/{project-name}"]
image:https://img.shields.io/codecov/c/github/{project-full-path}/{github-branch}.svg["Code Coverage", link="https://codecov.io/github/{project-full-path}?branch={github-branch}"]
image:https://github.com/{project-full-path}/actions/workflows/gradle.yml/badge.svg["CI", link="https://github.com/{project-full-path}/actions/workflows/gradle.yml"]
Supported Versions:
image:https://img.shields.io/badge/Java-11-blue.svg["Java 11"]
image:https://img.shields.io/badge/Java-17-blue.svg["Java 22"]
This project contains all needed classes to bind JavaEE enterprise events to a
RabbitMQ exchange for outgoing events. Inbound events can also be bound to the
respective queues and will be handed over to all JavaEE event observers.
The RabbitMQ message content is done via JSON serialization of a Java Bean
compatible PoJo object and vice versa.
== Usage example
First you need to define event objects using standard Java Bean syntax:
[source,java]
public class EventOne {
private boolean enabled;
public boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
}
public class EventTwo {
private String value;
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
As second step you have to define the bindings:
[source,java]
@Dependent
public class RabbitBinder extends EventBinder {
@Override
protected void bindEvents() {
ExchangeDeclaration testExchangeOne = declarerFactory()
.createExchangeDeclaration("test.exchange.one")
.withExchangeType(BuiltinExchangeType.DIRECT)
.withAutoDelete(false)
.withDurable(false); // <1>
QueueDeclaration testQueue = declarerFactory()
.createQueueDeclaration("test.queue")
.withAutoDelete(false)
.withDurable(false)
.withExclusiveAccess(false); // <2>
bind(EventOne.class)
.toExchange("test.exchange.one");
.withDeclaration(testExchangeOne) // <3>
bind(EventOne.class)
.toExchange("test.exchange.two")
.withRoutingKey("test.key")
.withEncoder(new MyCustomEncoder()); // <4>
bind(EventTwo.class)
.toQueue("test.queue");
bind(EventTwo.class)
.toQueue("test.queue")
.withDecoder(new MyCustomDecoder())
.withDeclaration(testQueue)
.autoAck(); // <5>
}
}
<1> Create exchange declaration for exchange with name 'test.exchange.one' and type 'direct'
<2> Creates queue declaration for queue with name 'test.queue'
<3> Uses an empty routing key for exchange 'test.exchange.one' and adds exchange declaration for this publisher
<4> Specifies a custom event encoder
<5> Specifies a custom event decoder, enables auto acknowledge and adds queue declaration for this consumer
Note to declarations:
Created declarations are not automatically declared on broker side.
For each consumer/producer own channels exist because of that reason it is necessary to
add the declaration needed to a binding via .withDeclaration(..)
Only these declarations are applied for this consumer/producer
As last step you need to initialize the binder either in a singleton
startup bean or servlet after having also configured the connection settings:
[source,java]
----
@Singleton
@Startup
public class BindingInitializer {
@Inject
private RabbitBinder binder;
@PostConstruct
public void initialize() {
try {
binder.configuration()
.addHost("somehost.somedomain") // <1>
.setUsername("myuser") // <2>
.setPassword("mypassword"); // <3>
.setSecure(true) // <4>
.setConnectTimeout(10000) // <5>
.setConnectRetryWaitTime(10000) // <6>
.setRequestedConnectionHeartbeatTimeout(3) // <7>
.withPrefetchCount(5); //8
binder.initialize();
} catch (IOException e) {
LoggerFactory.getLogger(getClass()).error("Unable to initialize", e);
}
}
}
----
<1> Specifies a AMQP host name (there can be added more than one here)
<2> Specifies a connection user name
<3> Specifies a connection password
<4> Enables the transport layer security (TLS)
<5> Sets the connect timeout to 10 sec
<6> Set the time to wait between connection retries to 10 sec
<7> Set the heartbeat timeout to 3 sec (To detect connection problems)
<8> Set the prefetch count which configures how many messages are downloaded at once from broker
=== Alternative connection definition
Alternatively the connection can also be configured using a respective URI
string:
[source,java]
----
@Singleton
@Startup
public class BindingInitializer {
@Inject
private RabbitBinder binder;
@PostConstruct
public void initialize() {
try {
binder.configuration()
.setUri("amqp://user:mysecret@somehost.somedomain/virtualhost"); // <1>
binder.initialize();
} catch (IOException e) {
LoggerFactory.getLogger(getClass()).error("Unable to initialize", e);
}
}
}
----
<1> Specifies a AMQP connection URI
More information about the detailed URI can be found in the
https://www.rabbitmq.com/uri-spec.html[RabbitMQ URI specification].
=== Multiple server connections
In case you have to support two different servers, create a binder implementation
for each host and initialize them in one single binding initializer:
[source,java]
----
@Singleton
@Startup
public class BindingInitializer {
@Inject
private RabbitBinder binderOne;
@Inject
private RabbitBinder binderTwo;
@PostConstruct
public void initialize() {
try {
binderOne.configuration()
.addHost("hostOne.somedomain")
.setUsername("myuser")
.setPassword("mypassword");
binderTwo.configuration()
.addHost("hostTwo.somedomain")
.setUsername("myuser")
.setPassword("mypassword");
binderOne.initialize();
binderTwo.initialize();
} catch (IOException e) {
LoggerFactory.getLogger(getClass()).error("Unable to initialize", e);
}
}
}
----
=== Usage in a container
Now the events can be used within your JavaEE container:
[source,java]
----
public class EventDemoBean {
@Inject
private Event
eventOnes;
public void submitEvent(boolean enabled) {
EventOne eventOne = new EventOne();
eventOne.setEnabled(enabled);
eventOnes.fire(eventOne);
}
public void receiveEvent(@Observes EventTwo eventTwo) {
String data = eventTwo.getData();
// do some work
}
}
----
== Contribute
Contributions are always welcome. Use https://google.github.io/styleguide/javaguide.html[Google code style format] for your changes.
== License
This project is licensed under the https://github.com/{project-full-path}/blob/{github-branch}/LICENSE[MIT license]