Is it possible to create in a Mule 4 Module a Scope with access to a connection? - mule4

I'm following the examples included in the documentation related to the creation of a Module and its different components. I've been able to create Operations that can use connections, but now I'm trying to do something similar with a Scope instead.
What I've tried is adding #Connection MyConnection connection as one on the arguments of the methods in my module as seen below.
public void logDecorator(#Connection MyConnection connection, Chain operations,
CompletionCallback<Object, Object> callback) {
logger.debug("Invoking child operations");
operations.process(
result -> {
logger.debug("Done: {}", result.getOutput());
callback.success(result);
},
(error, previous) -> {
logger.error(error.getMessage());
callback.error(error);
});
}
But when I build the module I get the error that this is not allowed.
Error executing: org.mule.runtime.extension.api.exception.IllegalOperationModelDefinitionException: Scope 'logDecorator' requires a connection, but that is not allowed, remove such parameter -> [Help 1]
Is there a way that I can add a reference to a connections that would allow me to use it inside logDecorator?

Scopes can not receive a connection as explained in the documentation:
Scopes have some restrictions that differentiate them from Operations. By definition, Scopes are not allowed to depend on or receive a particular Configuration or Connection.

public void logDecorator(Chain operations, CompletionCallback<Object, Object> callback) {
logger.debug("Invoking child operations");
// Get the connection that is defined for the scope
MyConnection connection = getConnection();
operations.process(
result -> {
logger.debug("Done: {}", result.getOutput());
callback.success(result);
},
(error, previous) -> {
logger.error(error.getMessage());
callback.error(error);
});
}
How about something like this?

Related

What are the "method names" in hub connections?

I've scoured through the .NET documentation and cannot find what these strings representing methods mean. For instance "ReceiveMessage" and "SendMessage" in:
hubConnection = new HubConnectionBuilder();
...
hubConnection.On<string, string>("ReceiveMessage", ..);
and
await hubConnection.SendAsync("SendMessage", userInput, messageInput);
are some examples. I realize in the Hub we have methods that can be these names, but sometimes not? For the toy example I'm using from the .NET documentation, a ChatHub class is defined as follows:
public class ChatHub : Hub
{
public async Task SendMessage(string user, string message)
{
await Clients.All.SendAsync("ReceiveMessage", user, message);
}
}
So here I can see the "SendMesage" method exists. But nowhere is there any "ReceiveMessage" method in the source code. I'm a bit disappointed the documentation doesn't actually explain what these strings representing functions mean in any detail. Do they represent javascript functions? Only locally defined functions in C# (then where is ReceiveMessage?)? Globally defined functions in SignalR? What are they?
They refer to methods on the client.
I guess the exact details vary between languages, but here is one simple example in Dart/Flutter using the signalr_netcore package:
In your server code
// Call the TestMethod method on the client
await Clients.Caller.SendAsync("TestMethod", "some arguments from the server");
In your client code
hub.on('TestMethod', (arguments) async {
print('TestMethod called from server with $arguments');
});
The above code will print TestMethod called from server with [some arguments from the server].

Vertx: How to combine third party events with Standard Verticle and Worker Verticle style event send and reply approach

I am trying to solve an issue whereby I need to combine third party events with the eventBus send and reply approach that Vertx provides for Standard and Worker Verticle setups. I am not sure if what I have laid out below is necessarily the correct approach.
Problem Statement:
I want to have standard verticle that sends a message to a worker verticle.
The worker verticle does some preprocessing and then uses a client method provided by a third party state management lib to publish an even (in an async manner). The result of which is only whether or not the event was successfully received or not (but does not contain any further info around processing etc).
Further processing takes place when the third party state management lib receives the event(this all happens on a separate thread) and a success or failure can occur at which point another event will be published to the cluster management tools output channel.
From the output channel listener I then want to be able to use the event to somehow use the message.reply() on the worker verticle to send back a response to the standard verticle that made the original request, thereby closing the loop of the entire request lifecycle but also using the async approach that vertx is built to use.
Now I conceptually know how to do 90% of what is described here but the missing piece for me is how to coordinate the event on the output channel listener and connect this to the worker verticle so that I can trigger the message.reply.
I have looked at possibly using SharedData and Clustering that Vertx has but was wondering if there is possibly another approach.
I have put a possible example implementation but would really appreciate if anyone has any insights/thoughts into how this can be accomplished and if I am on the right track.
class Order(val id: String)
class OrderCommand(val order: Order) : Serializable {
companion object {
const val name = "CreateOrderCommand"
}
}
class SuccessOrderEvent(val id: String) : Serializable {
companion object {
const val name = "OrderSuccessfulEvent"
}
}
interface StateManagementLib {
fun <T> send(
value: T,
success: Handler<AsyncResult<Int>>,
failure: Handler<AsyncResult<Exception>>
) {
val output = publish(value)
if (output == 1) {
success.handle(Future.succeededFuture())
} else {
failure.handle(Future.failedFuture("Failed"))
}
}
// non-blocking
fun <T> publish(value: T): Int // returns success/failure only
}
class WorkVerticle constructor(private val lib: StateManagementLib) : AbstractVerticle() {
override fun start(startPromise: Promise<Void>) {
workerHandler()
startPromise.complete()
}
private fun workerHandler() {
val consumer = vertx.eventBus().consumer<OrderCommand>(OrderCommand.name)
consumer.handler { message: Message<OrderCommand> ->
try {
vertx.sharedData().getClusterWideMap<String, Message<OrderCommand>>("OrderRequest") { mapIt ->
if (mapIt.succeeded()) {
lib.send(message.body(), {
// The StateManagementLib successfully propagated the event so, we try and store in this map (id -> Message)
mapIt.result().put(message.body().order.id, message)
}, {
message.fail(400, it.result().message)
})
}
}
} catch (e: Exception) {
message.fail(
HttpResponseStatus.INTERNAL_SERVER_ERROR.code(), "Failed to encode data."
)
}
}
// A consumer that will pick up an event that is received from the clusterTool output channel
vertx.eventBus().addInboundInterceptor { context: DeliveryContext<SuccessOrderEvent> ->
// Retrieve cluster map to get the previously stored message and try to respond with Message.reply
// This should go back to the Standard Verticle that sent the message
vertx.sharedData().getClusterWideMap<String, Message<OrderCommand>>("OrderRequest") {
if (it.succeeded()) {
val id = context.message().body().id
val mapResult = it.result().get(id)
it.result().remove(id)
// Try and reply so the original eventloop thread can pickup and respond to calling client
mapResult.result().reply(id)
}
}
}
}
}

Why does _session.Use method not work properly?

I have a job like this:
[UnitOfWork]
public override void Execute(CompleteIRHJobArgs args)
{
var robotUserId = _userRepo.GetAll().Where(p => p.UserName == TestaLIMSWPConsts.LIMSRobot).Select(p => p.Id).First();
using (_session.Use(args.TenantId, robotUserId))
{
_instanceReciptHeaderDomainService.SetIRHToCompleteState(args.IRHIds);
}
}
I find robotUserId and set it as the current user. But after I step into method SetIRHToCompleteState, _session.UserId.Value is null. I think it is wrong behavior. My ABP version is 4.0.0.
public async Task SetIRHToCompleteState(List<int> irhIds)
{
var irhs = await _instanceHeaderRepo.GetAll().Where(p => irhIds.Contains(p.Id)).ToListAsync();
foreach (var t in irhs)
{
t.FlowState = FlowState.Completed;
t.CompleteDate = Clock.Now;
t.CompleteUserId = _session.UserId.Value;
}
}
And sometimes,
var irhs = await _instanceHeaderRepo.GetAll()...
throws exception:
System.Transactions.TransactionInDoubtException: The transaction is in doubt. ---> System.Data.SqlClient.SqlException: There is already an open DataReader associated with this Command which must be closed first. ---> System.ComponentModel.Win32Exception: The wait operation timed out
But after step into method SetIRHToCompleteState, _session.UserId.Value is null.
SetIRHToCompleteState is async and continued running after the using scope was disposed.
Since Execute is not async, you cannot await but you can call AsyncHelper.RunSync instead.
// using Abp.Threading;
using (_session.Use(args.TenantId, robotUserId))
{
AsyncHelper.RunSync(() => _instanceReciptHeaderDomainService.SetIRHToCompleteState(args.IRHIds));
}
This would also avoid the "open DataReader" error.
From aspnetboilerplate/aspnetboilerplate#1646:
it's called in a background thread which is not inside an async context. But it's not a problem since background job manager is already single threaded and does not cause to block many threads.
Hangfire implementation is also like that.

Actually what is the init parameter in servlet?

Friends tell me what is the core meaning of init parameter in case of a servlet.
I know that how to initialize it in a web.xml but I don't know what is the actual purpose of it why it is required? Please tell me with a good example.
The Javadoc says: "A convenience method which can be overridden so that there's no need to call super.init(config)."
The init method's main purpose is to allow customization while you are initializing the servlet.
The simplest implementation is when you don't want to do any customization according to your application you can always call super.init method.
To understand meaning of what different init params can be there and how init method is useful:
Imagine a system Of BookManagement system, here for adding books and removing books from db you will be needing Database connection over which you can access the data. Now as Servlet's init method is called for the first request and database connection also needs be created only once(or n number of time if doing connection pooling) then initializing the database connection is something that you should do in init method.
A code snippet from Softlab example , let's assume that getInitParameter method reads the databaseUrl and other properties from web.xml
public class DBServlet ... {
Connection connection = null;
public void init() throws ServletException {
// Open a database connection to prepare for requests
try {
databaseUrl = getInitParameter("databaseUrl");
... // get user and password parameters the same way
connection = DriverManager.getConnection(databaseUrl,
user, password);
} catch(Exception e) {
throw new UnavailableException (this,
"Could not open a connection to the database");
}
}
...
}
One more example of counting the number of time servlet was accessed: https://docstore.mik.ua/orelly/java-ent/servlet/ch03_03.htm
So in Summary: To do customization like read the initial values of variable or to initialize resources(like db connection) you can use init method.
Below is the source code of init methods :
public void init(ServletConfig config)throws ServletException
{
this.config = config;
int();
}
public void init() throws ServletException;
It is recommended to override to init() method, not init(ServletConfig).
When overriding init(ServletConfig), the first thing that must be done is to call:
super.init(config);
If you do this then calling directly to getServletContext() in your method will no longer result in an NPE.

VertX : Check if database config is available

I'm quite new to the Microservice world and particularly vertX. I want my verticle to start anyway even there is no database connection available (e.g. database URL missing in configuration). I already managed to do this and my verticle is starting.
The issue now is that I want my verticle to notice when the database connection is available again and connect to it. How can I do this ?
I thought about creating another Verticle "DatabaseVerticle.java" which would send the current DB config on the event bus and my initial verticle would consume this message and check whether the config info is consistent (reply with success) or still missing some data (reply with fail and make the DatabaseVerticle check again).
This might work (and might not) but does not seem to be the optimal solution for me.
I'd be very glad if someone could suggest a better solution. Thank you !
For your use case, I'd recommend to use the vertx-config. In particular, have a look at the Listening to configuration changes section of the Vert.x Config documentation.
You could create a config retriever and set a handler for changes:
ConfigRetrieverOptions options = new ConfigRetrieverOptions()
.setScanPeriod(2000)
.addStore(myConfigStore);
ConfigRetriever retriever = ConfigRetriever.create(vertx, options);
retriever.getConfig(json -> {
// If DB config available, start the DB client
// Otherwise set a "dbStarted" variable to false
});
retriever.listen(change -> {
// If "dbStarted" is still set to false
// Check the config and start the DB client if possible
// Set "dbStarted" to true when done
});
The ideal way would be some other service telling your service about database connection. Either through event bus or HTTP, what you can do is when someone tries to access your database when connection is not made just try to make some DB call and handle the exception, return a boolean as false. Now when you get a message on event bus, consume it and save it in some config pojo. Now when someone tries to access your database, look for config and if available make a connection.
Your consumer:
public void start(){
EventBus eb = vertx.eventBus();
eb.consumer("database", message -> {
config.setConfig(message.body());
});
}
Your db client(Mongo for this eg):
public class MongoService{
private MongoClient client;
public boolean isAvailable = false;
MongoService(Vertx vertx){
if(config().getString("connection")){
client = MongoClient.createShared(vertx, config().getString("connection"));
isAvailable = true;
}
}
}
Not everything in Vertx should be solved by another verticle.
In this case, you can use .periodic()
http://vertx.io/docs/vertx-core/java/#_don_t_call_us_we_ll_call_you
I assume you have some function that checks the DB for the first time.
Let's call it checkDB()
class PeriodicVerticle extends AbstractVerticle {
private Long timerId;
#Override
public void start() {
System.out.println("Started");
// Should be called each time DB goes offline
final Long timerId = this.vertx.setPeriodic(1000, (l) -> {
final boolean result = checkDB();
// Set some variable telling verticle that DB is back online
if (result) {
cancelTimer();
}
});
setTimerId(timerId);
}
private void cancelTimer() {
System.out.println("Cancelling");
getVertx().cancelTimer(this.timerId);
}
private void setTimerId(final Long timerId) {
this.timerId = timerId;
}
}
Here I play a bit with timerId, since we cannot pass it to cancelTimer() right away. But otherwise, it's quite simple.

Resources