I want to connect to ServiceBus subscription from Quarkus. I found this article, which suggests to use ServiceBusJmsConnectionFactory and I am trying to make it work with smallrye
So far I tried:
import com.microsoft.azure.servicebus.jms.ServiceBusJmsConnectionFactory
import com.microsoft.azure.servicebus.primitives.ConnectionStringBuilder
import javax.enterprise.context.ApplicationScoped
import javax.enterprise.inject.Produces
import javax.jms.ConnectionFactory
#ApplicationScoped
class ConnectionFactoryBean {
#Produces
fun factory(): ConnectionFactory {
var connectionStringBuilder = ConnectionStringBuilder("Endpoint=sb://mynamespace.servicebus.windows.net/;SharedAccessKeyName=NAME;SharedAccessKey=KEY");
return ServiceBusJmsConnectionFactory(connectionStringBuilder, null)
}
}
Then in my application.properties:
mp.messaging.incoming.my_topic_name.connector=smallrye-jms
And finally to receive messages:
#Incoming("my_topic_name")
protected suspend fun receiveMyEvent(myEvent: String) {
//process
}
In terms of versions:
<dependency>
<groupId>com.microsoft.azure</groupId>
<artifactId>service-bus-jms-connection-factory</artifactId>
<version>0.0.1</version>
</dependency>
<dependency>
<groupId>io.smallrye.reactive</groupId>
<artifactId>smallrye-reactive-messaging-jms</artifactId>
<version>3.4.0</version>
</dependency>
It is failing with "An API incompatibility" error.
What is the correct way to receive messages using Kotlin/Quarkus from Azure ServiceBus?
UPDATE
I also tried to follow approach from Reactive messaging AMQP and Service Bus with Open Liberty
For that I dropped 2 listed dependencies, replaced them with
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-smallrye-reactive-messaging-amqp</artifactId>
</dependency>
And completely removed ConnectionFactoryBean.
For that I face
javax.net.ssl.SSLHandshakeException: Failed to create SSL connection
error. Adding quarkus.tls.trust-all=true or quarkus.ssl.native=true to properties did not work...
And it is still unclear, how to connect to the subscription? Will it be a change to the #Incoming attribute?
You can use Azure SDK libraries (maven artefact - com.azure.messaging.servicebus) to connect to Service Bus subscription.
For that you will need to create a ServiceBusProcessorClient connect to your namespace using usual connection string (available from the azure portal
under access policies), specify topicName and subscriptionName of it and then wait for messages. Here is an example code:
#ApplicationScoped
class ServiceBusClient() {
private val scope = CoroutineScope(SupervisorJob())
fun onStart(#Observes event: StartupEvent) {
scope.startClient()
}
fun onStop(#Observes event: ShutdownEvent) {
scope.cancel()
}
private fun CoroutineScope.startClient() = launch {
val processorClient: ServiceBusProcessorClient = ServiceBusClientBuilder()
.connectionString("CONNECTION_STRING")
.processor()
.topicName("TOPIC_NAME")
.subscriptionName("SUBSCRIPTION_NAME")
.processMessage { receivedMessageContext -> onMessage(receivedMessageContext) }
.processError { errorContext -> onError(errorContext) }
.buildProcessorClient()
processorClient.start()
}
private fun onMessage(context: ServiceBusReceivedMessageContext) {
//Process message
}
private fun onError(context: ServiceBusErrorContext) {
//Handle errors
}
}
You could get the content of the message using context.message.applicationProperties["PROPERTY_NAME"], context.message.body or context.message.subject as required.
Related
I am migrating off of mongobee to mongock so we can use Atlas. I've followed the commits on the suggested changes that have been merged into master and have modified CloudDatabaseConfiguration, DatabaseConfiguration, and InitialSetupMigration classes. I've also updated the pom to import the mongock 4.1.17 dependencies.
Running the app there seems to be no issues. I've tested the change log and everything operates as it should. When i run my tests, however, i am getting an error stating it cannot find the class org/springframework/data/mongodb/MongoDatabaseFactory.
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'mongockInitializingBeanRunner' defined in class path resource [com/ioi/helpdesk/gateway/config/DatabaseConfiguration.class]: Invocation of init method failed; nested exception is java.lang.NoClassDefFoundError: org/springframework/data/mongodb/MongoDatabaseFactory
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1796)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:595)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:517)
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:323)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:226)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:321)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
<dependency>
<groupId>com.github.cloudyrock.mongock</groupId>
<artifactId>mongock-spring-v5</artifactId>
<version>4.1.17</version>
</dependency>
<dependency>
<groupId>com.github.cloudyrock.mongock</groupId>
<artifactId>mongodb-springdata-v3-driver</artifactId>
<version>4.1.17</version>
</dependency>
I have not changed the starter data dependency
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
#Configuration
#EnableMongoRepositories("com.ioi.helpdesk.gateway.repository")
#Profile("!" + JHipsterConstants.SPRING_PROFILE_CLOUD)
#Import(value = MongoAutoConfiguration.class)
#EnableMongoAuditing(auditorAwareRef = "springSecurityAuditorAware")
public class DatabaseConfiguration {
private final Logger log = LoggerFactory.getLogger(DatabaseConfiguration.class);
#Bean
public ValidatingMongoEventListener validatingMongoEventListener() {
return new ValidatingMongoEventListener(validator());
}
#Bean
public LocalValidatorFactoryBean validator() {
return new LocalValidatorFactoryBean();
}
#Bean
public MongoCustomConversions customConversions() {
List<Converter<?, ?>> converters = new ArrayList<>();
converters.add(DateToZonedDateTimeConverter.INSTANCE);
converters.add(ZonedDateTimeToDateConverter.INSTANCE);
return new MongoCustomConversions(converters);
}
#Bean
public MongockSpring5.MongockInitializingBeanRunner mongockInitializingBeanRunner(ApplicationContext springContext,
MongoTemplate mongoTemplate,
#Value("${mongock.lockAcquiredForMinutes:5}") long lockAcquiredForMinutes,
#Value("${mongock.maxWaitingForLockMinutes:3}") long maxWaitingForLockMinutes,
#Value("${mongock.maxTries:3}") int maxTries) {
try {
log.info("INITIALIZING MONGOCK!");
SpringDataMongo3Driver driver = SpringDataMongo3Driver.withLockSetting(mongoTemplate, lockAcquiredForMinutes, maxWaitingForLockMinutes, maxTries);
MongockSpring5.MongockInitializingBeanRunner runner = MongockSpring5.builder()
.setDriver(driver)
.addChangeLogsScanPackage("com.ioi.helpdesk.gateway.config.dbmigrations")
.setSpringContext(springContext)
.buildInitializingBeanRunner();
log.info("MONGOCK INITIALIZED!");
return runner;
} catch(Exception e) {
log.info("Error during Mongock initalization - " + ExceptionUtils.getStackTrace(e));
}
return null;
}
}
Am I missing a test dependency or incorrectly included one?
I configured a TCP Client with the Java DSL of Spring Integration. It looks like this
#Bean
public TcpSendingMessageHandler tcpClient()
{
return Tcp
.outboundAdapter(
Tcp.nioClient("localhost", 9060)
.deserializer(new ByteArrayLfSerializer())
.soKeepAlive(false)
.leaveOpen(false)
.taskExecutor(Executors.newSingleThreadExecutor())
.get()
)
.clientMode(false)
.get();
}
And I am using it in a Service to send messages to the TCP socket the client is connected to:
#Slf4j
#Service
public class TcpClientConnectionService
{
private final TcpSendingMessageHandler messageHandler;
#Autowired
public TcpClientConnectionService(final TcpSendingMessageHandler messageHandler)
{
this.messageHandler = messageHandler;
this.messageHandler.start();
}
public void sendMessage(final String message)
{
messageHandler.handleMessage(new GenericMessage<>(message));
log.debug("Message: " + message + " send");
}
}
But in production I am getting the follwing warning rather regulary and I do not know what the issue is and how to fix it.
o.s.i.i.tcp.connection.TcpNioConnection : No publisher available to
publish TcpConnectionOpenEvent
o.s.i.i.tcp.connection.TcpNioConnection : No publisher available to
publish TcpConnectionCloseEvent
It would be great if somebody could help me out since I was not able to find anything by googling.
The nested factory is not initialized properly because you are incorrectly calling .get() on the spec, which subverts Spring initialization.
I configured a TCP Client with the Java DSL of Spring Integration. It looks like this
#Bean
public TcpSendingMessageHandler tcpClient()
{
return Tcp
.outboundAdapter(
Tcp.nioClient("localhost", 9060)
.deserializer(new ByteArrayLfSerializer())
.soKeepAlive(false)
.leaveOpen(false)
.taskExecutor(Executors.newSingleThreadExecutor()))
.clientMode(false)
.get();
}
Or move the factory definition to a top level #Bean.
What is the best practice for creating multiple queueclients for listening to different service bus queues? There is a MessagingFactory class however Microsoft.ServiceBus.Messaging not seems to be available as a nuget package anymore (.net core console application).
Considering QueueClient as static object what would be the recommended pattern to create multiple queueclients from a singleton host process?
Appreciate the feedback.
For .net core applications, you can make use of Microsoft.Azure.ServiceBus instead of Microsoft.ServiceBus.Messaging nuget. As this is build over .net standard, this can be used in both framework and core applications. Methods and classes similar to Microsoft.ServiceBus.Messaging are available under this. Check here for samples.
Able to get it working however could not use dependency injection. Any suggestions on improving this implementation would be much appreciated.
Startup.cs
// Hosted services
services.AddSingleton();
ServiceBusListener.cs
public class ServiceBusListener : BackgroundService, IServiceBusListener
{
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
Console.WriteLine($"ServiceBusListener is starting.");
Dictionary<string, QueueClient> queueClients = new Dictionary<string, QueueClient>();
foreach (var queue in _svcBusSettings.Queues)
{
var svcBusQueueClient = new ServiceBusQueueClient(queue.Value, queue.Key);
queueClients.Add(queue.Key, svcBusQueueClient.QueueClient);
}
}
}
ServiceBusQueueClient.cs
public class ServiceBusQueueClient : IServiceBusQueueClient
{
private IQueueClient _queueClient;
public QueueClient QueueClient
{
get { return _queueClient as QueueClient; }
}
public ServiceBusQueueClient(string serviceBusConnection, string queueName)
{
_queueClient = new QueueClient(serviceBusConnection, queueName);
RegisterOnMessageHandlerAndReceiveMessages();
}
}
I'm trying to use the sample code for sending a simple event to an Azure EventHub (https://learn.microsoft.com/en-us/azure/event-hubs/event-hubs-java-get-started-send). It seems to go fine, I'm all configured, but when I get to the:
ehClient.sendSync(sendEvent);
Part of the code, it just hangs there, and never gets past the sendAsync method. I'm using a personal computer, and have no firewall running. Is there some networking configuration I have to make maybe in Azure to allow this simple send to occur? Anyone have any luck making this work?
final ConnectionStringBuilder connStr = new ConnectionStringBuilder()
.setNamespaceName("mynamespace")
.setEventHubName("myeventhubname")
.setSasKeyName("mysaskename")
.setSasKey("mysaskey");
final Gson gson = new GsonBuilder().create();
final ExecutorService executorService = Executors.newSingleThreadExecutor();
final EventHubClient ehClient = EventHubClient.createSync(connStr.toString(), executorService);
print("Event Hub Client Created");
try {
for (int i = 0; i < 100; i++) {
String payload = "Message " + Integer.toString(i);
byte[] payloadBytes = gson.toJson(payload).getBytes(Charset.defaultCharset());
EventData sendEvent = EventData.create(payloadBytes);
// HANGS HERE - NEVER GETS PAST THIS CALL
ehClient.sendSync(sendEvent);
}
} finally {
ehClient.closeSync();
executorService.shutdown();
}
Try to use different executor service. For example 'work-stealing thread'
final ExecutorService executorService = Executors.newWorkStealingPool();
Below code should work for you.
<dependency>
<groupId>com.microsoft.azure</groupId>
<artifactId>azure-eventhubs</artifactId>
<version>2.0.0</version>
</dependency>
<dependency>
<groupId>com.microsoft.azure</groupId>
<artifactId>azure-eventhubs-spark_2.11</artifactId>
<version>2.3.7</version>
</dependency>
import java.util.concurrent.{Executors, ScheduledExecutorService}
import com.google.gson.Gson
import com.microsoft.azure.eventhubs.{EventData, EventHubClient}
object callToPushMessage{
private var executorService : ScheduledExecutorService = null
def writeMsgToSink(message: PushMessage):Unit={
val connStr = ConnectionStringBuilder()
.setNamespaceName("namespace")
.setEventHubName("name")
.setSasKeyName("policyname")
.setSasKey("policykey").build
// The Executor handles all asynchronous tasks and this is passed to the EventHubClient instance.
// This enables the user to segregate their thread pool based on the work load.
// This pool can then be shared across multiple EventHubClient instances.
// The following code uses a single thread executor, as there is only one EventHubClient instance,
// handling different flavors of ingestion to Event Hubs here.
if (executorService == null) {
executorService = Executors.newSingleThreadScheduledExecutor()
}
val ehclient = EventHubClient.createSync(connStr,executorService)
try {
val jsonMessage = new Gson().toJson(message,classOf[PushMessage])
val eventData: EventData = EventData.create(jsonMessage.getBytes())
ehclient.sendSync(eventData)
}
finally {
ehclient.close()
executorService.shutdown()
}
}}
I'm trying to figure out how to implement pub/sub using ServiceStack MQ abstraction
Let's say I have a publisher app publishing a Hello request that will have n subscribers (different apps)
// Publisher
namespace Publisher
{
public class RabbitPublisherAppHost : AppHostHttpListenerBase
{
public RabbitPublisherAppHost() : base("Rabbit Publisher Server", typeof(MainClass).Assembly) { }
public override void Configure(Container container)
{
Routes
.Add<Publish>("/publish/{Text}");
container.Register<IMessageService>(c => new RabbitMqServer());
var mqServer = container.Resolve<IMessageService>();
mqServer.Start();
}
}
}
namespace Publisher
{
public class PublishService : Service
{
public IMessageService MessageService { get; set; }
public void Any(Publish request)
{
PublishMessage(new Hello{Id=request.Text});
}
}
}
than I create the first subscriber
// Sub1
public class RabbitSubscriberAppHost : AppHostHttpListenerBase
{
public RabbitSubscriberAppHost() : base("Rabbit Subscriber 1", typeof(MainClass).Assembly) { }
public override void Configure(Container container)
{
container.Register<RabbitMqServer>(c => new RabbitMqServer());
var mqServer = container.Resolve<RabbitMqServer>();
mqServer.RegisterHandler<Hello>(ServiceController.ExecuteMessage, noOfThreads: 3);
mqServer.Start();
}
}
namespace Subscriber1
{
public class HelloService : Service
{
public object Any(Hello req)
{
//..
}
}
}
Now If I create a similar app acting as the second subscriber, the 2 subscribers are sharing the same queue, so instead of the pub/sub a race condition is happening.
In other word, what has to be done to implement a registration for each subscribers? I'd like all subscribers recevive the Hello request published, not just one of them according to a race condition.
ServiceStack's Messaging API follows the Services Request / Reply pattern (i.e. essentially using MQ instead of HTTP transport) and doesn't support Pub/Sub itself.
For RabbitMQ Pub/Sub you would have to implement it outside of the IMessaging API abstraction, e.g. publishing it to a Rabbit MQ topic from within your Service implementation.
Related ServiceStack does include a Pub/Sub library using Redis. Also depending on your use-case you may be able to make use of notifying multiple subscribers with Server Events .