Atmosphere Request Scoped Broadcasts - atmosphere

I'm having trouble restricting the scope of my broadcasts, using Atmosphere 0.7.2.
I have a Jersey POJO, with an open method, and I want to pass the broadcaster to a non-webservice class. This is to allow separation between WAR and EJB layers.
#Path("/")
public class MyJerseyPojo {
#Context
private Broadcaster broadcaster;
#GET
#Suspend
public String open() {
Manager.register(/* Session ID */, new NonWebservice(this.broadcaster));
return "";
}
}
public class NonWebService implements Sender {
private Broadcaster broadcaster;
public NonWebService(Broadcaster b) {
this.broadcaster = b;
}
#Override
public void send(String thing) {
this.broadcaster.broadcast(thing);
}
}
The idea is that update events will call send, and this will notify the client with the suspended response. Each client should be associated with a separate broadcaster.
The problem is, this solution uses the same broadcaster for all clients. I have tried adding #Suspend(scope = Suspend.SCOPE.REQUEST) to the open method, but this causes no broadcast messages to be received.
I also tried the following in the open method:
#GET
#Suspend
public String open() {
Broadcaster b = BroadcasterFactory.getDefault().get(JerseyBroadcaster.class, UUID.randomUUID());
b.setScope(Broadcaster.SCOPE.REQUEST);
Manager.register(/* Session ID */, new NonWebservice(b));
}
This didn't work, either using #Suspend or #Suspend(scope = Suspend.SCOPE.REQUEST). In each case, the client didn't receive any broadcast messages. I did once get a message that the broadcaster had been destroyed and couldn't be used, but I can't remember how I did this!
I have seen a similar question but I'm not sure how to translate it to my POJO, as I'm not extending AtmosphereHandler.
Thanks

It turns out, after a lot of digging, that it's not safe to cache broadcasters, as the AtmosphereFilter was creating a new, Request-scoped broadcaster after my open() method completed.
Hence, in the NonWebservice class now looks like this:
public class NonWebService implements Sender {
private Class<? extends Broadcaster> clazz;
private Object broadcasterId;
public NonWebService(Class<? extends Broadcaster> clazz, Object broadcasterId) {
this.clazz = clazz;
this.broadcasterId = broadcasterId;
}
#Override
public void send(String thing) {
Broadcaster b = BroadcasterLookup.getDefault().get(this.clazz, this.broadcasterId);
b.broadcast(thing);
}
}
Now I could do with figuring out how to schedule request-scoped fixed broadcasts...

Actually, in the end I found that I needed to return a broadcastable from my suspended method. The broadcaster was recreated as session-scoped but kept available for later broadcasts. Just make sure that you don't broadcast anything until the suspended method has returned, as that might get lost.

Related

Using Akka.net with Asp.net on a Modular Monolith architecture

Iwould like to implement a rest service using Akka and Asp.net.
Following the example here
I create my AkkaService containing the FooActor ref and a controller who transform the http request to a RunProcess message which is sent to the FooActor.
[Route("api/[controller]")]
[ApiController]
public class MyController : Controller
{
private readonly ILogger<MyController> _logger;
private readonly IAkkaService Service;
public RebalancingController(ILogger<MyController> logger, IAkkaService bridge)
{
_logger = logger;
Service = bridge;
}
[HttpGet]
public async Task<ProcessTerminated> Get()
{
var cts = new CancellationTokenSource(TimeSpan.FromSeconds(60));
return await Service.RunProcess(cts.Token);
}
}
public class AkkaService : IAkkaService, IHostedService
{
private ActorSystem ActorSystem { get; set; }
public IActorRef FooActor { get; private set; }
private readonly IServiceProvider ServiceProvider;
public AkkaService(IServiceProvider sp)
{
ServiceProvider = sp;
}
public async Task StartAsync(CancellationToken cancellationToken)
{
var hocon = ConfigurationFactory.ParseString(await File.ReadAllTextAsync("app.conf", cancellationToken));
var bootstrap = BootstrapSetup.Create().WithConfig(hocon);
var di = DependencyResolverSetup.Create(ServiceProvider);
var actorSystemSetup = bootstrap.And(di);
ActorSystem = ActorSystem.Create("AkkaSandbox", actorSystemSetup);
// </AkkaServiceSetup>
// <ServiceProviderFor>
// props created via IServiceProvider dependency injection
var fooProps = DependencyResolver.For(ActorSystem).Props<FooActor>();
FooActor = ActorSystem.ActorOf(rebalProps.WithRouter(FromConfig.Instance), "foo");
// </ServiceProviderFor>
await Task.CompletedTask;
}
public async Task<ProcessTerminated> RunProcess(CancellationToken token)
{
return await FooActor.Ask<ProcessTerminated>(new RunProcess(), token);
}
public FooActor(IServiceProvider sp)
{
_scope = sp.CreateScope();
Receive<RunProcess>(x =>
{
var basketActor = Context.ActorOf(Props.Create<BarActor>(sp), "BarActor");
basketActor.Tell(new BarRequest());
_log.Info($"Sending a request to Bar Actor ");
});
Receive<BarResponse>(x =>
{
...... Here I need to send back a ProcessTerminated message to the controller
});
}
Now, let's imagine the FooActor send a message to the BarActor telling him to perform a given task and wait the BarResponse. How could I send back the ProcessTerminated message to the controller?
Few points to take into considerations:
I want to ensure no coupling between BarActor and FooActor.
By example, I could add the original sender ActorRef to the BarRequest and
BarResponse. But the BarActor musn't know about the fooActor and
MyController. The structure of the messages an how the barActor
respond should not be dependent of what the FooActor do with the
BarResponse.
In the example I only use BarActor, but we can imagine to have many different actors
exchanging messages before returning the final result to the controller.
Nitpick: you should use Akka.Hosting and avoid creating this mock wrapper service around the ActorSystem. That will allow you to pass in the ActorRegistry directly into your controller, which you can use to then access FooActor without the need for additional boilerplate. See "Introduction to Akka.Hosting - HOCONless, "Pit of Success" Akka.NET Runtime and Configuration" video for a fuller explanation.
Next: to send the ProcessTerminated message back to your controller you need to save the Sender (the IActorRef that points to the temporary actor created by Ask<T>, in this instance) during your Receive<RunProcess> and make sure that this value is available inside your Receive<BarResponse>.
The simple ways to accomplish that:
Store the Sender in a field on the FooActor, use behavior-switching while you wait for the BarActor to respond, and then revert back to your original behavior.
Build a Dictionary<RunProcess, IActorRef> (the key should probably actually be some unique ID shared by RunProcess and BarResponse - a "correlation id") and reply to the corresponding IActorRef stored in the dictionary when BarResponse is received. Remove the entry after processing.
Propagate the Sender in the BarRequest and BarResponse message payloads themselves.
All three of those would work. If I thought there were going to be a large number of RunProcess requests running in parallel I'd opt for option 2.
Another way of doing it is by simply forwarding the next message to the next actor. The Tell operation have a second parameter that can be used to override the message sender. If you're sure that all path has to respond back to the original Ask inside the Http controller, you can do this inside the FooActor:
Receive<RunProcess>(x =>
{
var basketActor = Context.ActorOf(Props.Create<BarActor>(sp), "BarActor");
basketActor.Tell(new BarRequest(), Sender);
_log.Info($"Sending a request to Bar Actor ");
});
This way, the original Ask actor is considered as the sender of the new BarRequest message instead of the FooActor, and if BarActor decide to reply by doing a Sender.Tell(new ProcessTerminated()). the ProcessTerminated message will be sent to the Http controller.

Multi-entity Aggregates command handling

I have an aggregate root like this:
Aggregate root:
#NoArgsConstructor
#Aggregate(repository = "positionAggregateRepository")
#AggregateRoot
#XSlf4j
#Data
public class HopAggregate {
#AggregateIdentifier
private String hopId;
private FilteredPosition position;
private LocalDate positionDate;
#AggregateMember
private Security security;
#CommandHandler
public HopAggregate(NewHopCommand cmd) {
log.info("creating new position , {}", cmd.getDateId());
apply(new HopEvent(cmd.getHopId(), cmd.getDateId(), cmd.getFilteredPosition(), cmd.getSecurity(), false));
}
#CommandHandler
public void handle(UpdateHopCommand cmd) {
log.info("creating hop update event {}", cmd);
apply(new HopEvent(this.hopId, this.positionDate, cmd.getFilteredPosition(), this.security, true));
}
#CommandHandler
public void handle(SecurityUpdate cmd) {
log.info("updating security {}", cmd);
apply(new SecurityUpdateEvent(this.hopId, cmd.getFilteredSecurity()));
}
#EventSourcingHandler
public void on(HopEvent evt) {
if (evt.getIsUpdate()) {
log.info("updating position {}", evt);
this.position = evt.getFilteredPosition();
} else {
log.info("adding new position to date {}", evt);
this.hopId = evt.getHopId();
this.positionDate = evt.getDate();
this.position = evt.getFilteredPosition();
this.security= evt.getSecurity();
}
}
#EventSourcingHandler
public void on(SecurityUpdateEvent evt) {
log.info("hop id {}, security update {}", this.hopId, evt.getFilteredSecurity().getSecurityId());
}
}
Child entity:
#XSlf4j
#Data
#RequiredArgsConstructor
#NoArgsConstructor
public class IpaSecurity implements Serializable {
#EntityId
#NonNull
private String id;
#NonNull
private FilteredSecurity security;
}
My issue is that when i am pushing and update like this:
#EventHandler
public void handleSecurityEvent(SecurityUpdate securityUpdate) {
log.info("got security event {}", securityUpdate);
commandGateway.send(securityUpdate);
}
and my command being:
#Data
#RequiredArgsConstructor
#NoArgsConstructor
#ToString
public class SecurityUpdate {
#NonNull
#TargetAggregateIdentifier
private String id;
#NonNull
private FilteredSecurity filteredSecurity;
}
I am getting aggregate root not found exception:
Command 'com.hb.apps.ipa.events.SecurityUpdate' resulted in org.axonframework.modelling.command.AggregateNotFoundException(The aggregate was not found in the event store)
I am not sure how to handle this scenario. My requirement is that each aggregate should check whether it contains the security and then update it if the command was issued. What am i missing? let me know if you need any more info on the code.
Thanks for your help.
A Command is always targeted towards a single entity.
This entity can be an Aggregate, an entity contained in an Aggregate (what Axon Framework calls an Aggregate Member) or a simple singleton component.
Important to note though, is that there will only be one entity handling the command.
This is what requires you to set the #TargetAggregateIdentifier in your Command for Axon to be able to route it to a single Aggregate instance if the Command Handler in question is part of it.
The AggregateNotFoundException you're getting signals that the #TargetAggregateIdentifier annotated field in your SecurityUpdate command does no correspond to any existing Aggregate.
I'd thus suspect that the id field in the SecurityUpdate does not correspond to any #AggregateIdentifier annotated field in your HopAggregate aggregates.
A part from the above, I have a couple of other suggestions when looking at your snippets which I'd like to share with you:
#Aggregate is meta-annotated with #AggregateRoot. You're thus not required to specify both on an Aggregate class
For logging messages being handled, you can utilize LoggingInterceptor. You can configure this on any component capable of handling messages, thus providing a universal way of logging. This will omit the necessity to add log lines in your message handling functions
You're publishing a HopEvent on both the create and update commands. Doing so makes your HopEvent very generic. Ideally, your events clarify business operations occurring in your system. My rule of thumb typically is such: "If I tell my business manager/customer about the event class, he/she should know exactly what it does". I'd thus suggest to rename the event to something more specific
Just as with the HopEvent, the UpdateHopCommand is quite generic. Your commands should express the intent to perform an operation in your application. Users will typically not desire an update, they desire an address change for example. Your commands classes ideally reflect this
The suggested naming convention for commands is to start with verb in the present tense. Thus, it should no be SecurityUpdate, but UpdateSecurity. A command is a request expressing intent, the messages ideally reflect this
Hope this helps you out #juggernaut!

How can I using ConcurrentWebSocketSessionDecorator to send my message with websocket

I have a concurrent issue, my WebSocket was used spring-message,
when my chatroom has a lot of people it will very slow.
So I try to find how, and I found some problem where the WebSocketSession using sendMessage, it has a synchronized in class websocketServerSockJsSession
#Override
public void sendMessageInternal(String message) throws SockJsTransportFailureException {
// Open frame not sent yet?
// If in the session initialization thread, then cache, otherwise wait.
if (!this.openFrameSent) {
synchronized (this.initSessionLock) {
if (!this.openFrameSent) {
this.initSessionCache.add(message);
return;
}
}
}
so if they have 200 chat in one second it will be very slow,
I found the implement of WebSocketSession call ConcurrentWebSocketSessionDecorator.
But I can't cast WebSocketServerSockJsSession to ConcurrentWebSocketSessionDecorator, I can't find how to set my WebSocketSession.
I can't change the sockJS.
So how can I do if I use the sockJS and I want to use ConcurrentWebSocketSessionDecorator method?
is another way to implement the ConcurrentWebSocketSessionDecorator sendMessage or I can do some property setting and make my WebSocketSession turn to ConcurrentWebSocketSessionDecorator?
this my config setting
#Configuration
#EnableWebMvc
#EnableWebSocket
public class SpringWebSocketConfig extends WebMvcConfigurerAdapter implements WebSocketConfigurer {
#Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(webSocketHandler(), "/websocket/send").addInterceptors(new HandshakeInterceptor()).setAllowedOrigins("*");
registry.addHandler(webSocketHandler(), "/websocket/sockjs").addInterceptors(new HandshakeInterceptor()).setAllowedOrigins("*").withSockJS();
}
As its name suggests, the ConcurrentWebSocketSessionDecorator is a Decorator.
This decorator overrides some functions such as sendMessage of WebSocketSession, and WebSocketServerSockJSession performs its actions through its webSocketSession, so changing webSocketSession to ConcurrentWebSocketSessionDecorator can solve the problem.
ConcurrentWebSocketSessionDecorator currentWebSocketSessionDecorator = new ConcurrentWebSocketSessionDecorator (session, 1000, 1024);
wsSockJsSession.initializeDelegateSession((WebSocketSession)concurrentWebSocketSessionDecorator);

TelemetryProcessor - Multiple instances overwrite Custom Properties

I have a very basic http-POST triggered api which creates a TelemetryClient. I needed to provide a custom property in this telemetry for each individual request, so I implemented a TelemtryProcessor.
However, when subsequent POST requests are handled and a new TelemetryClient is created that seems to interfere with the first request. I end up seeing maybe a dozen or so entries in App Insights containing the first customPropertyId, and close to 500 for the second, when in reality the number should be split evenly. It seems as though the creation of the 2nd TelemetryClient somehow interferes with the first.
Basic code is below, if anyone has any insight (no pun intended) as to why this might occur, I would greatly appreciate it.
ApiController which handles the POST request:
public class TestApiController : ApiController
{
public HttpResponseMessage Post([FromBody]RequestInput request)
{
try
{
Task.Run(() => ProcessRequest(request));
return Request.CreateResponse(HttpStatusCode.OK);
}
catch (Exception)
{
return Request.CreateErrorResponse(HttpStatusCode.InternalServerError, Constants.GenericErrorMessage);
}
}
private async void ProcessRequest(RequestInput request)
{
string customPropertyId = request.customPropertyId;
//trace handler creates the TelemetryClient for custom property
CustomTelemetryProcessor handler = new CustomTelemetryProcessor(customPropertyId);
//etc.....
}
}
CustomTelemetryProcessor which creates the TelemetryClient:
public class CustomTelemetryProcessor
{
private readonly string _customPropertyId;
private readonly TelemetryClient _telemetryClient;
public CustomTelemetryProcessor(string customPropertyId)
{
_customPropertyId = customPropertyId;
var builder = TelemetryConfiguration.Active.TelemetryProcessorChainBuilder;
builder.Use((next) => new TelemetryProcessor(next, _customPropertyId));
builder.Build();
_telemetryClient = new TelemetryClient();
}
}
TelemetryProcessor:
public class TelemetryProcessor : ITelemetryProcessor
{
private string CustomPropertyId { get; }
private ITelemetryProcessor Next { get; set; }
// Link processors to each other in a chain.
public TelemetryProcessor(ITelemetryProcessor next, string customPropertyId)
{
CustomPropertyId = customPropertyId;
Next = next;
}
public void Process(ITelemetry item)
{
if (!item.Context.Properties.ContainsKey("CustomPropertyId"))
{
item.Context.Properties.Add("CustomPropertyId", CustomPropertyId);
}
else
{
item.Context.Properties["CustomPropertyId"] = CustomPropertyId;
}
Next.Process(item);
}
}
It's better to avoid creating Telemetry Client per each request, isntead re-use single static Telemetry Client instance. Telemetry Processors and/or Telemetry Initializers should also typically be registered only once for the telemetry pipeline and not for every request. TelemetryConfiguration.Active is static and by adding new Processor with each request the queue of processor only grows.
The appropriate setup would be to add Telemetry Initializer (Telemetry Processors are typically used for filtering and Initializers for data enrichment) once into the telemetry pipeline, e.g. though adding an entry to ApplicationInsights.config file (if present) or via code on TelemetryConfiguration.Active somewhere in global.asax, e.g. Application_Start:
TelemetryConfiguration.Active.TelemetryInitializers.Add(new MyTelemetryInitializer());
Initializers are executed in the same context/thread where Track..(..) was called / telemetry was created, so they will have access to the thread local storage and or local objects to read parameters/values from.

Why is my RestEasy WebService blocked when my #Asynchronous method is working?

I am trying to run heavy tasks asynchronously. The client then polls the server to know when the job is done. This seemed to work, but I noticed that my WebService that responds to the polling is blocked when I put a breakpoint in my #Asynchronous Method.
This is what I did:
JobWS.java // Used to start a job
#RequestScoped
#Path("/job")
#Produces(MediaType.APPLICATION_JSON)
public class JobWS {
#POST
#Path("/run/create")
public Response startJob(MyDTO dto) {
return ResponseUtil.ok(jobService.createJob(dto));
}
}
JobService.java // Creates the job in the DB, starts it and returns its ID
#Stateless
public class JobService {
#Inject
private AsyncJobService asyncJobService;
#Inject
private Worker worker;
public AsyncJob createJob(MyDTO dto) {
AsyncJob asyncJob = asyncJobService.create();
worker.doWork(asyncJob.getId(), dto);
return asyncJob; // With this, the client can poll the job with its ID
}
}
Worker.java // Working hard
#Stateless
public class Worker {
#Asynchronous
public void doWork(UUID asyncJobId, MyDTO dto) {
// Do work
// ...
// Eventually update the AsyncJob and mark it as finished
}
}
Finally, my Polling Webservice, which is the one being blocked
#RequestScoped
#Path("/polling")
#Produces(MediaType.APPLICATION_JSON)
public class PollingWS {
#Inject
AsyncJobService asyncJobService;
#GET
#Path("/{id}")
public Response loadAsyncJob(#PathParam("id") #NotNull UUID id) {
return ResponseUtil.ok(asyncJobService.loadAsyncJob(id));
}
}
If I put a breakpoint somwhere in doWork(), the PollingWS does not respond to HTTP requests anymore. When I debug through doWork(), occasionally I get a response, but only when jumping from one breakpoint to another, never when waiting at a breakpoint.
What am I missing here ? Why is my doWork() method blocking my Webservice, despite it running asynchronously ?
I found the culprit. A breakpoint suspends all threads by default. In IntelliJ, a right click on it will open the following dialog:
When changing the "Suspend" property to "Thread", my WS is not blocked anymore and everything works as expected. In retrospect, I feel a bit stupid for asking this. But hey... maybe it will help others :)

Resources