I am new to blackberry development and I am creating a native blackberry application. On every screen of my application, I need to send and receive data to the server on the same connection.
What I have done so far is I have made a ConnectToServer class which has a bunch of methods for sending and receiving. I instantiate it on the main screen and I pass it to each screen as a parameter.
That class in not a thread because I only read and write when the user types in information and presses a button. So basically I am using the inputStream and outputStream on the event thread which I hear is BAD. Then I ask ConnectToServer to get me what the server sent. For instance, I get a vector which I use to make a ListField.
How can I make these UI updates?
public class Screen3 extends MainScreen {
ConnectToServer con;
Vector v;
public Screen3(String exerciseName, ConnectToServer connect)
{
con = connect;
con.send(exerciseName);
v = con.receiveVector();
mylist = new listField();
mylist.setSize(v.size());
add(mylist);
}
public void drawListRow(...)
{
graphics.drawText((String) v.elementAt(index)
}
}
So, there's many ways to approach this. First of all, since it seems like you only want one instance of ConnectToServer, and you are currently having to pass that around, you might try making that class a Singleton object. This is not necessary, and does not have anything to do with your threading problem, but I only offer it as a solution, for situations where you want to enforce that there's only one instance of something, and want to avoid having to pass it around everywhere. A simple Singleton implementation might be this:
public class ConnectToServer {
private static ConnectToServer _instance;
/** use this static method to get the one and only instance */
public static ConnectToServer getInstance() {
if (_instance == null) {
_instance = new ConnectToServer();
}
return _instance;
}
/** private to enforce Singleton pattern */
private ConnectToServer() {
}
}
And use it in your screens like this (no need to pass it into the constructor any more):
ConnectoToServer connection = ConnectToServer.getInstance();
connection.blahBlahBlah();
Now, on to the threading problem. You're right that you should not be performing network requests on the main (aka "UI", aka "Event") thread. If you have a nice separate ConnectToServer class, that makes it easier to encapsulate this behaviour. Instead of UI clients using a synchronous send() and receiveVector() method, make one method that just kicks off the request, and another callback method that the ConnectToServer class will call when the response comes back. The ConnectToServer class will use a Thread to perform this work, and thus avoid freezing the UI during the request.
I'll define an interface that the UI clients will implement:
public interface RequestListener {
/** listeners must implement this method to get data. method will be called on the UI thread */
void onDataReceived(Vector response);
}
And then the new (partial) ConnectToServer class:
public class ConnectToServer {
private Thread _worker;
private RequestListener _listener;
public void setRequestListener(RequestListener listener) {
// note: this implementation only allows one listener at once.
// make it a list if you need something more
_listener = listener;
}
/** initiate a network request on a background thread */
public void sendRequest(final String request) {
_worker = new Thread(new Runnable() {
public void run() { // run on the background/worker thread
send(request);
final Vector response = receiveVector();
if (_listener != null) {
// this assumes all our listeners are UI objects, so we pass
// data back to them on the UI thread:
UiApplication.getUiApplication().invokeLater(new Runnable() {
public void run() { // run on UI thread
_listener.onDataReceived(response);
}
});
}
}
});
_worker.start();
}
}
Note that you should also make your original send() and receiveVector() methods in this class private. They should only be called from inside the class now, not directly from UI clients.
Then, you need to code your Screen classes like this:
public class Screen3 extends MainScreen implements RequestListener {
public Screen3(String exerciseName) {
ConnectToServer connection = ConnectToServer.getInstance();
connection.setRequestListener(this);
// kick off the request (on a background thread)
connection.sendRequest(exerciseName);
}
public void onDataReceived(Vector response) {
if (mylist == null) {
// first time data has been received, so create and add the list field:
mylist = new listField();
add(mylist);
}
mylist.setSize(response.size());
// TODO: presumably, you would copy the contents of 'response' into 'mylist' here
}
}
Also, you might also want to code the server class to protect against multiple UI clients making concurrent requests, allow current requests to be cancelled, etc. But the above should get you started on a solution that provides a responsive app, without freezing your UI.
Related
I created an asynchronous thread to navigate from one UI class to another UI class after 30 seconds by showing a timer(H1 tag) to the user. Thread successfully shows updates on H1 tag but does not navigate to the next UI class after the end of 30 seconds. I'm getting an error Exception call "java.lang.IllegalStateException: Cannot access state in VaadinSession or UI without locking the session." for ui.navigate(ScoreBoard.class); call.
#Override
protected void onAttach(AttachEvent attachEvent) {
// Start the data feed thread
thread = new FeederThread(attachEvent.getUI(),timerc);
thread.start();
}
//Thread
private static class FeederThread extends Thread {
private final com.vaadin.flow.component.UI ui;
private final H1 element;
private int count = 30;
public FeederThread(com.vaadin.flow.component.UI ui,H1 element) {
this.ui = ui;
this.element = element;
}
#Override
public void run() {
while (count>-1){
try {
Thread.sleep(1000);
ui.access(()-> {
element.setText(String.valueOf(count)+" sec");
});
count--;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//Exception in thread "Thread-46" java.lang.IllegalStateException: //Cannot access state in VaadinSession or UI without locking the session.
ui.navigate(ScoreBoard.class);
}
}
//Exception in thread "Thread-46" java.lang.IllegalStateException: //Cannot access state in VaadinSession or UI without locking the session.
ui.navigate(ScoreBoard.class);
UI.getCurrent() is returning null when called in Thread, this is intentional. This way it can be ensured that no wrong UI is returned.
The correct pattern would be for example add a method in your view, which updates the Text. In the method you can use getUi().ifPresent(ui -> ui.access(..)) . Then you can call that method from the Thread safely. Same can be applied with navigation.
Alternatively you can pass ui as parameter to your Thread as you have done. When you do so, getCurrent() call is obsolote.
You need to enable push by using #Push in your class. Also, since the navigation action is part of the UI state, you need to use UI.access. Finally, you don't need to call getCurrent() if you already have the instance. So this is what you need in short:
...
#Push
public class MainView extends VerticalLayout {
...
ui.access(() -> ui.navigate(ScoreBoard.class));
...
}
I have an app that's using Boot 2.0 with webflux, and has an endpoint returning a Flux of ServerSentEvent. The events are created by leveraging spring-amqp to consume messages off a RabbitMQ queue. My question is: How do I best bridge the MessageListener's configured listener method to a Flux that can be passed up to my controller?
Project Reactor's create section mentions that it "can be very useful to bridge an existing API with the reactive world - such as an asynchronous API based on listeners", but I'm unsure how to hook into the message listener directly since it's wrapped in the DirectMessageListenerContainer and MessageListenerAdapter. Their example from the create section:
Flux<String> bridge = Flux.create(sink -> {
myEventProcessor.register(
new MyEventListener<String>() {
public void onDataChunk(List<String> chunk) {
for(String s : chunk) {
sink.next(s);
}
}
public void processComplete() {
sink.complete();
}
});
});
So far, the best option I have is to create a Processor and simply call onNext() each time in the RabbitMQ listener method to manually produce an event.
I have something like this:
#SpringBootApplication
#RestController
public class AmqpToWebfluxApplication {
public static void main(String[] args) {
ConfigurableApplicationContext applicationContext = SpringApplication.run(AmqpToWebfluxApplication.class, args);
RabbitTemplate rabbitTemplate = applicationContext.getBean(RabbitTemplate.class);
for (int i = 0; i < 100; i++) {
rabbitTemplate.convertAndSend("foo", "event-" + i);
}
}
private TopicProcessor<String> sseFluxProcessor = TopicProcessor.share("sseFromAmqp", Queues.SMALL_BUFFER_SIZE);
#GetMapping(value = "/sseFromAmqp", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<String> getSeeFromAmqp() {
return this.sseFluxProcessor;
}
#RabbitListener(id = "fooListener", queues = "foo")
public void handleAmqpMessages(String message) {
this.sseFluxProcessor.onNext(message);
}
}
The TopicProcessor.share() allows to have many concurrent subscribers which we get when we return this TopicProcessor as a Flux to our /sseFromAmqp REST request via WebFlux.
The #RabbitListener just delegates its received messages to that TopicProcessor.
In the main() I have a code to confirm that I can publish to the TopicProcessor even if there is no subscribers.
Tested with two separate curl sessions and published messages to the queue via RabbitMQ Management Plugin.
By the way I use share() because of: https://projectreactor.io/docs/core/release/reference/#_topicprocessor
from multiple upstream Publishers when created in the shared configuration
That' because that #RabbitListener really can be called from different ListenerContainer threads, concurrently.
UPDATE
Also I moved this sample to my Sandbox: https://github.com/artembilan/sendbox/tree/master/amqp-to-webflux
Let's suppose you want to have a single RabbitMQ listener that somehow puts messages to one or more Flux(es). Flux.create is indeed a good way how to create such a Flux.
Let's start with Messaging with RabbitMQ Spring guide and try to adapt it.
The original Receiver would have to be modified in order to be able to put received messages to a FluxSink.
#Component
public class Receiver {
/**
* Collection of sinks enables more than one subscriber.
* Have to keep in mind that the FluxSink instance that the emitter works with, is provided per-subscriber.
*/
private final List<FluxSink<String>> sinks = new ArrayList<>();
/**
* Adds a sink to the collection. From now on, new messages will be put to the sink.
* Method will be called when a new Flux is created by calling Flux.create method.
*/
public void addSink(FluxSink<String> sink) {
sinks.add(sink);
}
public void receiveMessage(String message) {
sinks.forEach(sink -> {
if (!sink.isCancelled()) {
sink.next(message);
} else {
// If canceled, don't put any new messages to the sink.
// Sink is canceled when a subscriber cancels the subscription.
sinks.remove(sink);
}
});
}
}
Now we have a receiver that puts RabbitMQ messages to sink. Then, creating a Flux is rather simple.
#Component
public class FluxFactory {
private final Receiver receiver;
public FluxFactory(Receiver receiver) { this.receiver = receiver; }
public Flux<String> createFlux() {
return Flux.create(receiver::addSink);
}
}
Receiver bean is autowired to the factory. Of course, you don't have to create a special factory. This only demonstrates the idea how to use the Receiver to create the Flux.
The rest of the application from Messaging with RabbitMQ guide may stay the same, including the bean instantiation.
#SpringBootApplication
public class Application {
...
#Bean
SimpleMessageListenerContainer container(ConnectionFactory connectionFactory,
MessageListenerAdapter listenerAdapter) {
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer();
container.setConnectionFactory(connectionFactory);
container.setQueueNames(queueName);
container.setMessageListener(listenerAdapter);
return container;
}
#Bean
MessageListenerAdapter listenerAdapter(Receiver receiver) {
return new MessageListenerAdapter(receiver, "receiveMessage");
}
...
}
I used similar design to adapt Twitter streaming API sucessfuly. Though, there may be a nicer way how to do it.
I have an application that does not recieve ordinary HTTP requests through a controller, instead it listens to and receives messages (AMQP protocol) in order to initiate it's logic flow.
My application may receive and handle more than 1 message at a time. I have an object that will be collecting information/data throughout the process, in several different services/classes, in order for me to use it at the end.
But I need the data to be seperated per message received, as a "Scoped" injection would seperate the injected instance from other HTTP requests.
My usecase is therefor very similar to how I would use a Scoped injected object in an ordinary API, but instead of a new HTTP request, I receive a message in my listeners.
Is there any way that I can create a custom scope, for every message received, either through some kind of configuration, or having the code create a new scope as the first thing in my Listener.MessageReceived(Message message) method?
Imagine a flow like this:
public class Listener {
ServiceClassA serviceClassA //injected in constructor
CustomLogger customLogger // (HAS TO BE SAME OBJECT INJECTED INTO ServiceClassA, ServiceClassB and Listener)
public void ReceiveMessage(Message message) {
using (var scope = CreateNewScope()) {
try {
serviceClassA.DoStuff();
} catch(Exception e) {
Console.Write(customLogger.GetLogs())
}
}
}
}
public class ServiceClassA {
ServiceClassB serviceClassB //injected in constructor
CustomLogger customLogger //(HAS TO BE SAME OBJECT INJECTED INTO ServiceClassA, ServiceClassB and Listener)
public void DoStuff() {
customLogger = ResolveCustomLogger(); // how do I make sure I can get/resolve the same object as in Listener (without having to pass parameters)
var data = // does stuff
customLogger.Log(data);
serviceClassB.DoStuff();
}
}
public class ServiceClassB {
CustomLogger customLogger //(HAS TO BE SAME OBJECT INJECTED INTO ServiceClassA, ServiceClassB and Listener)
public void DoStuff() {
customLogger = ResolveCustomLogger(); // how do I make sure I can get/resolve the same object as in Listener (without having to pass parameters)
var data = // does other stuff
customLogger.Log(data);
}
}
My CustomLogger may not only be used 1 or 2 service layers down, there might be many layers, and I might only want to use the CustomLogger in the bottom on, yet I want it accessible in the top level afterwards, to retrieve the data stored in it.
Thank you very much.
You can inject a ServiceScopyFactory in the class that reacts to messages from the queue, then for each message it receives it can create a scope, from which it requests a MessageHandler dependency.
The code sample below does exactly this (and it also deals with sessions on the queue, but that should make no difference for creating the scope).
public class SessionHandler : ISessionHandler
{
public readonly string SessionId;
private readonly ILogger<SessionHandler> Logger;
private readonly IServiceScopeFactory ServiceScopeFactory;
readonly SessionState SessionState;
public SessionHandler(
ILogger<SessionHandler> logger,
IServiceScopeFactory serviceScopeFactory,
string sessionId)
{
Logger = logger;
ServiceScopeFactory = serviceScopeFactory;
SessionId = sessionId
SessionState = new SessionState();
}
public async Task HandleMessage(IMessageSession session, Message message, CancellationToken cancellationToken)
{
Logger.LogInformation($"Message of {message.Body.Length} bytes received.");
// Deserialize message
bool deserializationSuccess = TryDeserializeMessageBody(message.Body, out var incomingMessage);
if (!deserializationSuccess)
throw new NotImplementedException(); // Move to deadletter queue?
// Dispatch message
bool handlingSuccess = await HandleMessageWithScopedHandler(incomingMessage, cancellationToken);
if (!handlingSuccess)
throw new NotImplementedException(); // Move to deadletter queue?
}
/// <summary>
/// Instantiate a message handler with a service scope that lasts until the message handling is done.
/// </summary>
private async Task<bool> HandleMessageWithScopedHandler(IncomingMessage incomingMessage, CancellationToken cancellationToken)
{
try
{
using IServiceScope messageHandlerScope = ServiceScopeFactory.CreateScope();
var messageHandlerFactory = messageHandlerScope.ServiceProvider.GetRequiredService<IMessageHandlerFactory>();
var messageHandler = messageHandlerFactory.Create(SessionState);
await messageHandler.HandleMessage(incomingMessage, cancellationToken);
return true;
}
catch (Exception exception)
{
Logger.LogError(exception, $"An exception occurred when handling a message: {exception.Message}.");
return false;
}
}
private bool TryDeserializeMessageBody(byte[] body, out IncomingMessage? incomingMessage)
{
incomingMessage = null;
try
{
incomingMessage = IncomingMessage.Deserialize(body);
return true;
}
catch (MessageDeserializationException exception)
{
Logger.LogError(exception, exception.Message);
}
return false;
}
}
Now whenever a MessageHandlerFactory is instantiated (which happens for each message received from the queue), any scoped dependencies requested by the factory will live until the MessageHandler.HandleMessage() task finishes.
I created a message handler factory so that the SessionHandler could pass non-DI-service arguments to the constructor of the MessageHandler (the SessionState object in this case) in addition to the DI-services. It is the factory who requests the (scoped) dependencies and passes them to the MessageHandler. If you are not using sessions then you might not need the factory, and you can instead fetch a MessageHandler from the scope directly.
So,
Here is the code setup.
There is a driver application, which starts the HTTP server(ASP.NET core Web API project).
The method called by driver application for starting HTTP server is
this:
public class Http_Server
{
public static ConcurrentQueue<Object> cq = new ConcurrentQueue<Object>();
public static void InitHttpServer(ConcurrentQueue<Object> queue)
{
cq = queue;
var host = new WebHostBuilder()
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseStartup<Startup>()
.UseApplicationInsights()
.Build();
host.Run();
}
}
Controller Action Task:
[HttpPost]
[Route("XYZ")]
public virtual IActionResult AddXYZ([FromBody]List<Resourcemembers> resourcemembers)
{
//add something to ds
Http_Server.cq.Enqueue(new object());
//respond back
return new ObjectResult(example);
}
The data structure(a concurrent queue) being passed is to be made visible at controller level(like a global variable accessible across all controllers).
Is it fine to make the ds a static variable and access it across controllers?
Or Is there a way to pass this ds across to different layers?
This is my go at a better solution for this. What you are trying to do doesn't seem like the best way to approach this.
First, you want to enable caching in the application by calling the AddMemoryCache in the application StartUp.ConfigureServices method.
public void ConfigureServices(IServiceCollection services)
{
// Add framework services.
services.AddMemoryCache();
...
}
Then, you want to use the cache. Something like this should get you going in the right direction.
public class XYZController : Controller {
private IMemoryCache _memoryCache;
private const string xyzCacheKey = "XYZ";
public XYZController(IMemoryCache memoryCache)
{
_memoryCache = memoryCache;
}
[HttpPost("XYZ")]
public IActionResult AddXYZ([FromBody]ResourceMember[] resourceMembers)
{
try
{
if (!_memoryCache.TryGetValue(xyzCacheKey, out ConcurrentQueue<Object> xyz))
{
xyz = new ConcurrentQueue<Object>();
_memoryCache.Set(xyzCacheKey, xyz, new MemoryCacheEntryOptions()
{
SlidingExpiration = new TimeSpan(24, 0, 0)
});
}
xyz.Enqueue(resourceMembers);
return Ok();
}
catch (Exception ex)
{
return BadRequest(ex);
}
}
}
public class ResourceMember { }
What this does is allow you to use a Memory Cache to hold your object(s), and what ever you Enqueue in the ConcurrentQueue object, should you stay with that as your main object within the Cache. Now, you can cache any object type in the MemoryCache, and pull the value when you need to based on the key you gave it when you added it to the cache. In the case above, I created a const named xyzCacheKey with a string value of XYZ to use as the key.
That static global variable thing you are trying is just not... good.
If this doesn't help, let me know in a comment, I will delete the answer.
Good luck!
I need to achieve the impact of waitForConfirmsOrDie in core java implementation in spring . In core java it is achievable request wise ( channel.confirmSelect , set Mandatory , publish and Channel.waitForConfirmsOrDie(10000) will wait for 10 sec)
I implemented template.setConfirmCallback ( hope it is same as PublisherCallbackChannel.Listener) and it works great , but ack/nack is at a common place ( confirm call back ) , for the individual sender no idea like waitForConfirmsOrDie , where he is sure within this time ack hasn't came and can take action
do send methods wait for specified period internally like waitForConfirmsOrDie in spring if ack hasn't came and if publisherConfirms is enabled.
There is currently no equivalent of waitForConfirmsOrDie in the Spring API.
Using a connection factory with publisher confirms enabled calls confirmSelect() on its channels; together with a template confirm callback, you can achieve the same functionality by keeping a count of sends yourself and adding a method to your callback to wait - something like...
#Autowired
private RabbitTemplate template;
private void runDemo() throws Exception {
MyCallback confirmCallback = new MyCallback();
this.template.setConfirmCallback(confirmCallback);
this.template.setMandatory(true);
for (int i = 0; i < 10; i++) {
template.convertAndSend(queue().getName(), "foo");
}
confirmCallback.waitForConfirmsOrDie(10, 10_000);
System.out.println("All ack'd");
}
private static class MyCallback implements ConfirmCallback {
private final BlockingQueue<Boolean> queue = new LinkedBlockingQueue<>();
#Override
public void confirm(CorrelationData correlationData, boolean ack, String cause) {
queue.add(ack);
}
public void waitForConfirmsOrDie(int count, long timeout) throws Exception {
int remaining = count;
while (remaining-- > 0) {
Boolean ack = queue.poll(timeout, TimeUnit.MILLISECONDS);
if (ack == null) {
throw new TimeoutException("timed out waiting for acks");
}
else if (!ack) {
System.err.println("Received a nack");
}
}
}
}
One difference, though is the channel won't be force-closed.
Also, in a multi-threaded environment, you either need a dedicated template/callback per thread, or use CorrelationData to correlate the acks to the sends (e.g. put the thread id into the correlation data and use it in the callback).
I have opened AMQP-717 for us to consider providing something like this out of the box.