How to avoid single route in Laravel for Server Sent Events - javascript

So I am trying to use Server Sent Events in my laravel app but the problem is that in SSE, you can only specify single URL as in:
var evtSource = new EventSource("sse.php");
The problem is that I want to send events from different parts/controllers of the entire application not just single sse.php file for example. Example:
// AuthController.php
public function postLogin(Request $request) {
// login logic
// SEND SSE EVENT ABOUT NEW USER LOGIN
}
// FooController.php
public function sendMessage() {
// SEND SSE EVENT ABOUT NEW MESSAGE
}
The only idea that comes to my mind is that from these controllers, create temporary files with event text and then read those text files in sse.php file, send events and then delete files. However, this doesn't look like perfect solution. Or maybe hook sse.php file with Laravel events mechanism somehow but that seems kinda advanced.
I also don't mind creating multiple event source objects with different php files like sse.php but problem is that those controllers methods don't necessarily just send events alone, they do other work as well. For example, I cannot use postLogin action directly in SSE object because it also performs the login logic not just sending event.
Has anyone had similar problem and how to tackle with it please ?

Never used SSE but, according to their docs, two solutions come to my mind:
1.Define multiple EventSource objects and handle them differently in your js:
var loginSource = new EventSource("{!! url("/loginsse") !!}");
var messageSource = new EventSource("{!! url("/messagesse") !!}");
2. Inside your sse.php file, check for updates from the others controller:
//sse.php called function
while(1) {
$login = AuthController::postLogin($request);
if ($login) return $login;
$msg = FooController::sendMessage();
if ($msg) return $msg;
// ...
}
You will need to make sendMessage() and postLogin(Request $request) static methods.
Consider also the adoption of some libraries to use SSE with laravel: a quick google search provided me several possibilities.

You could use Laravel's events and create a listener that would combine all the different events from your app into a single stream, and then you'd have a single controller that would emit that stream of events to the client. You could generate an event anywhere in your code and it would be streamed to the client. You'd need some sort of shared FIFO buffer to allow the communication between the listener and controller, listener would write to it, and controller would read it and send SSE. The simplest solution would be to use just a plain log file for this.
Also Laravel has built-in broadcasting feature using Laravel Echo, so maybe that could help? I'm not sure if Echo is using SSE (have zero experience with both), but it does pretty much the same thing...
Updated: Let me try to add an example:
1) First we need to create an event and a listener, e.g.:
php artisan make:event SendSSE
php artisan make:listener SendSSEListener --event=SendSSE
2) Your event class is just a wrapper around the data that you wish to pass to the listener. Add to SendSSE class as many properties as you need, but lets pass just a string message for this example:
public function __construct($msg)
{
$this->message = $msg;
}
public function getMessage()
{
return $this->message;
}
3) Now you can fire this event like this:
event(new \App\Events\SendSSE('Hello there'));
4) Your listener's handle() method will receive it and then you need to push it into a FIFO buffer that you'll use to communicate with the SSE controller. This can be as simple as writing to a file, and then reading from it in your controller, or you can use a DB or linux fifo pipe or shared memory or whatever you wish. I'll leave that part to you and presume that you have a service for it:
public function handle(\App\Events\SendSSE $event)
{
// let's presume you have a helper method that will pass
// the message to the SSE Controller
FifoBufferService::write($event->getMessage());
}
5) Finally, in your SSE Controller you should read the fifo buffer and when a new message comes pass it to the client:
while(1) {
// if this read doesn't block, than you'll probably need to sleep()
if ($new_msg = FifoBufferService::read()) {
$this->sendSSE($new_msg);
}
}

Related

How to return an object with embedded objects to a jquery get call?

I use jquery.get() to make a call to the Spring-Boot backend, like this:
var url = "http://localhost:8080/api/v1/get_holdings?userId=1";
$.get(url, function(payload,status) {
if (status == "success") {
$.each(payload.data, function (key,entry) {
...
})
}
else {...
In SpringBoot I create a model that has an embedded model. In this case the model is called Holding and the embedded model is called Account, like this:
#Entity
#Table(name="holding")
public class Holding {
...
#ManyToOne //Many holdings to One Account
#JoinColumn(name="account_id", nullable=false)
private Account account;
...
//getters and setters...
The call to the database returns the data correctly.
Then I try to return it to the jquery ajax call like this:
JsonResponse response = new JsonResponse();
List<Holding> holdings = holdingRepo.getUserHoldings(userId);
response.setData(holdings); //response looks correct but browser shows a 500 error. Why?
return response;
JsonResponse is just a wrapper that I use for all my responses. I've used it many times and has worked fine for many other calls:
public class JsonResponse {
private String status = null;
private Object data = null;
//getters and setters...
It returns to the browser ok, but the browser gives a 500 error.
There are two ways that I can get the browser to accept the data.
create a separate model that contains just the data I need with no embedded models
create the json manually using the toString() method (pain in the butt)
I've tried converting the Holding object to JSON using Gson.toJson and JSONArray(holdings) but both of those methods fail. What am I missing?
You are using a relation. I suppose there happens an exception, like following:
org.hibernate.LazyInitializationException: could not initialize proxy
This can happen because Hibernate tries to optimize performance and does not load objects in the relation.
Then there are many answers to this question on SO.
One solution can be to force Hibernate to initialize the proxy via
Hibernate.initialize( entity )
Another approach can be not to send entities to the client, but to transform them into data transfer objects. If you do such transformation within the transaction, Hibernate will resolve all needed objects automatically and you will not need any tricks.

SignalR does not invoke server hub method when calling function outside of .done

I have been trying to implement signalR into my game to handle updating player position real time, and broadcasting that to all connected clients.
However, my client function which is invoking the server hub method does not ever reach the server, unless I call it directly after $.connection.hub.start().done(). Let me give you an example...
This calls the server hub method UpdatePlayerPosition correctly, but I don't want to call this method directly on .done:
$.connection.hub.start().done(function () {
$.connection.playerHub.server.updatePlayerPosition(playerObj)
});
However, if I create a variable on my main gameEngine object and call it later, I.E each time a player moves, nothing happens.
$.connection.hub.start().done(function () {
gameEngine.updatePlayerPosition = function () {
$.connection.playerHub.server.updatePlayerPosition(playerObj)
//playerObj is a global variable
}
});
if (player.isMoving) {
gameEngine.updatePlayerPosition();
}
Calling gameEngine.updatePlayerPosition() does hit the correct function, and goes through all of the signalr.js, but the breakpoint on the server is not hit.
Here is the hub code (I have not added the functionality for updating the player on the hub yet, I just want to get the communication going first)
using Microsoft.AspNet.SignalR;
using RPGGameCS.Models.Player;
namespace RPGGameCS
{
public class PlayerHub : Hub
{
public void UpdatePlayerPosition(IPlayer player)
{
Clients.All.updatePlayerPosition(player);
}
}
}
All the examples of signalR I've seen bind these server calls to click events, which I don't want to do. It seems like this should be possible, but nothing I've tried thus far has worked. Is there something I'm doing wrong? Or maybe it's not possible to delegate singalR functions in this way?
EDIT 1***: So it appears it has something to do with the player object I'm sending to the hub method. When I make the player object null, the communication appears to work fine. But when I send the actual player object, the break point is never hit.
Method signature must match.
In your hub you have a parameter IPlayer and on client you do not set this parameter. gameEngine.updatePlayerPosition(); does not matcht with public void UpdatePlayerPosition(IPlayer player)
If you call for example gameEngine.updatePlayerPosition(); signalr searchs for a method on the connected hub according name and parameters. In the case it will not find a method I think you should find this in the log.
Enable log for testing:
$.connection.hub.logging = true;
$.connection.hub.start();
(https://learn.microsoft.com/en-us/aspnet/signalr/overview/testing-and-debugging/enabling-signalr-tracing)

Update context variables using .js. Its possible?

i'm trying to send a user name to my WCS but i not sure how can i do that using a request. My consersation ask for the user email and use a js script to return a json from my sql server...
I'm doing something like that:
What's your email? // WCS
caique.rodrigues#test.com //USER
Just a minute.<script>getUserInformation('caique.rodrigues#test.com');</script> // WCS
nodeTrue //USER (I sent this after confirmated if the user exist and got the user name.)
Hi <span id='myText'></span><script>document.getElementById('myText').innerHTML = userName;</script>! //WCS
I know this is not the best way to do that but it is working. I'm trying to call my js functions using something like "output:{'action':}" (And handle it in my node.js like the 'car dashboard sample'), but, it's possible send a varible from my .js to a conversation context?
I had the same problem a few months ago. In this case, I used functions for access the context variables on the client side (Using this example from Ashley and accessing the context), but, for security issues of some company data, I did need to use custom code in the server-side... And, for this case, you can use the method call for Watson Conversation, and access the context variables and creates values with custom code in your favor.
Something like this:
conversation.message(payload, function (err, data) {
data.context.yourValue = returnFromYourDatabase;
if (err) {
return res.status(err.code || 500).json(err);
}
updateMessage(payload, data, req, res);
});
});
You can see the function updateMessage, this call is one example from IBM Developers, and this function is used to update the message in every request. You can use this function to set too, because this function get the parameters from the call.
For example:
function updateMessage(input, response) {
response.context.yourValue = returnFromYourDatabase;
var responseText = null;
if (!response.output) {
response.output = {};
}
}
See API Reference for Watson Conversation Service using Node.js.
See the Project with Node.js by IBM Developers.

Recieving a stream from rails 4.0 in JS callback

I'm trying transmit an image file from the server to the client, but my javascript callback becomes active before the stream closes I doing this because sending it in a traditional render json: times out and takes way to long anyway. The stream takes much less time, but i keep can't get all the data before the callback fires up.
controller code
def mytest
image=ImageList.new(AssistMe.get_url(image_url))
response.stream.write image.export_pixels(0, 0, image.columns, image.rows, 'RGBA').to_s
response.stream.close
end
javascript
var getStream, runTest;
runTest = function() {
return $.post('/dotest', getStream);};
getStream = function(params) {
return document.getElementById('whatsup2').innerHTML =
"stream is here " + params.length;};
the response is an array, I can make it an array of arrays by adding a "[" at the front and a "],['finish'] at the end to be able to detect the end of the data, but I haven't been able to figure out how to get javascript to wait until the end of stream to run. I assume i need to set up some kind of pole to check for the end, but how do I attach it to the callback?
Okay, here's a blog that describes this pretty well
blog
But i decided to forgo a stream and use .to_s. Since you can pipe several actions tougher
render object.method.method.to_s you get all the server side benefits of using a stream without the complexity. If you have a slow process where you need to overlap the client and server actions, then go to the blog and do it. Otherwise to_s covers it pretty well

How can Java and JavaScript work together?

I'll preface this by stating that I know Java is not JavaScript and vice versa.
I've got a project where I need to count occurrences of words for each of 1750 document names and document contents. I've got some awesome JavaScript from a colleague that does exactly what I want from a form input on a web page.
I want to use Java's FileReader, BufferedReader, walkFileTree, etc. to traverse the directories in which the documents live.
I'm not sure if this is the most efficient or effective approach, but both the Java and JavaScript parts of the code are working independently of one another now, and I'd like to see if I can get them to pass data between them before I start re-inventing the wheel.
Here's where I am so far. I'm stuck at the CLParse method & have inserted pseudocode:
public static void main(String... aArgs) throws FileNotFoundException {
File startingDirectory= new File("CGT");
List<File> files = FileListing.getFileListingNoSort(startingDirectory);
for(File file : files ) {
CLParse(file.toString());
} }
static private List<File> getFileListingNoSort(File aDirectory) throws FileNotFoundException {
List<File> result = new ArrayList<File>();
File[] filesAndDirs = aDirectory.listFiles();
List<File> filesDirs = Arrays.asList(filesAndDirs);
for(File file : filesDirs) {
result.add(file); //always add, even if directory
if ( ! file.isFile() ) {
List<File> deeperList = getFileListingNoSort(file);
result.addAll(deeperList);
} }
return result;
}
/* is something like this doable and how would I do it?
*/
public static void CLParse(String fn) {
pass fn to JavaScript counter
return an array of {word,occurences} for the string
write array to file
}
I'll be creating another set of methods to extract and pass the document CONTENTS as a string as well. I'd love to know if anyone has any practical experience passing values back and forth between Java and JavaScript, and advice on a good/better way to do it.
You got 2 Options to let them interact with each other, which i know:
1.Applet <-> javascript
2.Serlvet <-> javascript
With option 1, you have to build a Communication with a JSObject: JSObject
or you cann call the Applets Method instanstly with document.appletname.methodname();
with this you can even Parse same simply Formats to each other.
With Option 2 you have to build a communication with a Servlet.
in here you have to send an Ajax request to the the Servlet:
$.post('login',{name:"Peter", pw:"123456"},function()
{
//do whatever
})
JavaServlet class
the first comment, has to written as an Servlet in your web.xml, it´s the servlet pattern.
the second ones, are the parameters which can be read in the servlet. the function describes the stuff, which can be done in the request.
The differences between these two Options are:
1.the Applets runs on the users Computer, so you can access his files. But for this your applet has to be signed.
2.the Servlet runs on the Server. Here you have got full file access(if the system allows you too have it).
I would try to investigate Mozilla Rhino.
http://en.wikipedia.org/wiki/Rhino_%28JavaScript_engine%29
Check out Rhino https://developer.mozilla.org/en-US/docs/Rhino
You can create java objects and use them in javascript. Integration is straightforward
You can use AJAX to send and receive values to server. You can send parameters or JSON to server and get response.
You can use JSONP to serve the data, or if you have no control of the second server, use a reverse proxy to proxy requests to the second server through the first.

Categories

Resources