There are multiple ways to transfer data between Java applications, this library offers an easy way to connect with them using common methods.
Currently supporting the brokers:
- ActiveMQ using topic producers and consumers.
- Kafka using empty-key records to producers.
- NATS using subject subscription.
- PostgreSQL using
LISTENandNOTIFYstatement. - RabbitMQ using queue and consumer via exchange.
- Redis using publish and subscribe (also compatible with KeyDB).
- SQL polling (not a real broker, but can be used as one).
- Valkey using publish and subscribe (same as Redis, but with an older API).
PostgreSQL and SQL are also compatible with Hikari.
Delivery4j contains the following artifacts:
delivery4j- The main project.delivery4j-broker-activemq- ActiveMQ broker.delivery4j-broker-kafka- Kafka broker.delivery4j-broker-nats- NATS broker.delivery4j-broker-postgresql- PostgreSQL broker using plain Java connections.delivery4j-broker-postgresql-hikari- PostgreSQL broker using Hikari library.delivery4j-broker-rabbitmq- RabbitMQ broker.delivery4j-broker-redis-jedis- Redis broker using jedis library.delivery4j-broker-redis-lettuce- Redis broker using lettuce library.delivery4j-broker-sql- SQL broker using plain Java connections.delivery4j-broker-sql-hikari- SQL broker using Hikari library.delivery4j-broker-valkey- Valkey broker.
Note
If you still on Java 8 and want to use a -hikari artifact, filter the hikari sub-dependency and manually add hikari 4.x to your project.
build.gradle
dependencies {
implementation 'com.saicone:delivery4j:1.1.6'
}build.gradle.kts
dependencies {
implementation("com.saicone:delivery4j:1.1.6")
}pom.xml
<dependencies>
<dependency>
<groupId>com.saicone</groupId>
<artifactId>delivery4j</artifactId>
<version>1.1.6</version>
<scope>compile</scope>
</dependency>
</dependencies>How to use Delivery4j library.
Using brokers is pretty simple, you just need to create a broker instance (depending on the implementation) and set a consumer.
Broker broker = // Create instance from any implementation
// Subscribe to channels
broker.subscribe("hello:world", "myChannel1");
broker.setConsumer((channel, data) -> {
// do something
});
// Start connection
broker.start();
// Send data
byte[] data = ...;
broker.send("myChannel1", data);Some brokers require to convert bytes to String and viceversa, Base64 is used by default.
Broker broker = // Create instance from any implementation
broker.setCodec(new ByteCodec<>() {
@Override
public @NotNull String encode(byte[] src) {
// convert bytes to String
}
@Override
public byte[] decode(@NotNull String src) {
// convert String to bytes
}
});Some brokers have blocking operations or repetitive tasks, it's suggested to implement your own executor.
Broker broker = // Create instance from any implementation
broker.setExecutor(new DelayedExecutor<MyTaskObject>() {
@Override
public @NotNull MyTaskObject execute(@NotNull Runnable command) {
// run task and return itself
}
@Override
public @NotNull MyTaskObject execute(@NotNull Runnable command, long delay, @NotNull TimeUnit unit) {
// run delayed task and return itself
}
@Override
public @NotNull MyTaskObject execute(@NotNull Runnable command, long delay, long period, @NotNull TimeUnit unit) {
// run repetitive task and return itself
}
@Override
public void cancel(@NotNull MyTaskObject unused) {
// cancel task
}
});And also a logging instance to log information about connection and exceptions, by default it use the best available implementation.
It uses a number terminology for logging levels:
- Error
- Warning
- Information
- Debug
Broker broker = ...;
// --- Using existing logger instance
Logger logger = ...;
broker.setLogger(LogFilter.valueOf(logger, 3)); // using 3 as max logging level
broker.setLogger(LogFilter.valueOf(logger, () -> 3)); // you can also supply the max level dynamically
// --- Creating a logger instance from name
broker.setLogger(LogFilter.valueOf("LogName", 3));
broker.setLogger(LogFilter.valueOf("LogName", () -> 3));
// --- Creating a logger instance from class
broker.setLogger(LogFilter.valueOf(MyObject.class, 3));
broker.setLogger(LogFilter.valueOf(MyObject.class, () -> 3));Probably the reason why you are here, it's a simple usage of brokers to send and receive multi-line String messages.
First you need to extend AbstractMessenger and provide a broker.
public class Messenger extends AbstractMessenger {
@Override
protected Broker loadBroker() {
// Create instance from any implementation
}
}And then use the Messenger.
Messenger messenger = new Messenger();
// Start connection
messenger.start();
// Send multi-line message to channel
messeger.send("myChannel1", "Hello", "World");
// Subscribe to channel
messenger.subscribe("myChannel1").consume((channel, lines) -> {
// do something
});The subscribed message channels can have an identifier instance to avoid receive outbound messages.
Messenger messenger = new Messenger();
// Subscribe to channel
MessageChannel channel = messenger.subscribe("myChannel1").consume((channel, lines) -> {
// do something
});
// Provide a 32-bit identifier
chanel.identifier(DataIdentifier.bit32());
// Provide a 64-bit identifier (only needed if your application can send more than 32767 messages per second)
chanel.identifier(DataIdentifier.bit64());And also can have an end-to-end encryption.
Messenger messenger = new Messenger();
// Subscribe to channel
MessageChannel channel = messenger.subscribe("myChannel1").consume((channel, lines) -> {
// do something
});
// Your key
SecretKey key = ...;
channel.encryptor(Encryptor.of(key));