I have some service method which is executing for 3 seconds. I would like to send information to the client when processing starts (HTTPStatus.PROCESSING), and after (3 seconds) when its done(HTTPSTATUS.OK). Now i have this. Can't realize how to improove. It doesn't work correctly
Controller
#RestController
public class MainController {
private ExecutorService nonBlockingService = Executors
.newCachedThreadPool();
#Async
#CrossOrigin
#GetMapping("/sse")
public SseEmitter handleSse() throws IOException, InterruptedException {
SseEmitter emitter = new SseEmitter();
emitter.send(HttpStatus.PROCESSING);
TestService.doSMG();
emitter.send(HttpStatus.OK);
return emitter;
}
}
Service
public class TestService {
public static void doSMG() throws InterruptedException {
TimeUnit.SECONDS.sleep(1);
}
}
Client
<html>
<head>
<script>
var sse = new EventSource('http://localhost:8080/sse');
sse.onmessage = function (evt) {
var el = document.getElementById('sse');
el.appendChild(document.createTextNode(evt.data));
el.appendChild(document.createElement('br'));
};
</script>
</head>
<body>
<p id = "sse">
</p>
</body>
</html>
Your current solution is fully sequential which means it executes everything in order. You should return the SseEmitter as soon as possible and run everything else in a background thread.
To run things in a background thread you want to inject a TaskExecutor or even better an AsyncTaskExecutor so you can submit tasks to it for later execution (as soon there is a thread available for processing).
This would look something like this
#RestController
public class MainController {
private final AsyncTaskExecutor taskExecutor;
public MainController(AsyncTaskExecutor taskExecutor) {
this.tashExecutor=taskExecutor;
}
#CrossOrigin
#GetMapping("/sse")
public SseEmitter handleSse() throws IOException, InterruptedException {
SseEmitter emitter = new SseEmitter();
taskExecutor.submit(() -> {
emitter.send(HttpStatus.PROCESSING);
TestService.doSMG();
emitter.send(HttpStatus.OK);
});
return emitter;
}
}
This will execute the task in the background, while immediatly returning the SseEmitter.
Related
I have a problem. I am doing exactly as mentioned here: Progress bar for long running server calls in ASP.Net MVC, but it's not working. There is no exceptions nor errors, the problem is that is not doing anything. Why is not my code reporting the progress to the client side? Another thing is that i don't know why signalR does not generate the script
<script src="~/signalr/hubs"></script>
controller
ProgressHub.SendMessage("Iniciando proceso", 2);
startup class
using Microsoft.Owin;
using Owin;
using Microsoft.AspNet.SignalR;
[assembly: OwinStartup(typeof(DimexCEUI.Startup))]
namespace DimexCEUI
{
public class Startup
{
public void Configuration(IAppBuilder app)
{
//ConfigureAuth(app);
app.MapSignalR();
}
}
}
my hub
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Web;
using Microsoft.AspNet.SignalR;
namespace DimexCEUI
{
public class ProgressHub : Hub
{
public string msg = "Initializing and Preparing...";
public int count = 1;
public static void SendMessage(string msg, int count)
{
var message = "Proceso completado " + msg;
var hubContext = GlobalHost.ConnectionManager.GetHubContext<ProgressHub>();
hubContext.Clients.All.sendMessage(string.Format(message), count);
}
public void GetCountAndMessage()
{
Clients.Caller.sendMessage(string.Format(msg), count);
}
}
}
Client:
function StartInvoicing() {
var progressNotifier = $.connection.progressHub;
// client-side sendMessage function that will be called from the server-side
progressNotifier.client.sendMessage = function (message, count) {
// update progress
UpdateProgress(message, count);
alert(message);
};
// establish the connection to the server and start server-side operation
$.connection.hub.start().done(function () {
// call the method CallLongOperation defined in the Hub
progressNotifier.server.getCountAndMessage();
});
}
// Update the progress bar
function UpdateProgress(message, count) {
var result = $("#progress-data");
result.html(message);
$("#progress-count").html(count);
}
So I'm trying to make a webservice that allows someone to obtain data from a server. Right now, the server I'm using is written using java's HttpServer class. I plan to make the server accessible using fetch() in javascript, but it's not working.
When I was first testing out my server, I used Apache's HttpComponents library, and that client(written in java). was able to receive the test json that came from my server. However, when I used fetch() on my javascript client, nothing is received when I console.log everything. It doesn't make sense to me why it would work in Java, but not javascript. Does anyone know why this is not working? Am I just doing the javascript part wrong, and it does actually work? Thanks!
Code snippets for reference:
java server:
public class Main {
private static final int PORT = 1337;
private static final int BACKLOG = 1;
public static void main(String[] args) {
try {
HttpServer server = HttpServer.create(new InetSocketAddress(PORT), BACKLOG);
System.out.print("started on" + PORT);
HttpContext context = server.createContext("/ex", new Handler());
server.start();
} catch (IOException e) {
e.printStackTrace();
}
}
}
class Handler implements HttpHandler {
#Override
public void handle(HttpExchange he) throws IOException {
System.out.println("handled");
JSONObject obj = new JSONObject();
obj.put("name", "value");
obj.put("num", new Integer(100));
obj.put("balance", new Double(1000.21));
obj.put("is_vip", new Boolean(true));
obj.put("array", new int[]{1, 2, 3});
String response = obj.toJSONString();
he.sendResponseHeaders(200, response.length());
he.getResponseBody().write(response.getBytes());
}
}
java client:
public class PostTest {
public static void main(String[] args) throws IOException {
CloseableHttpClient httpClient = HttpClients.createDefault();
HttpPost httppost = new HttpPost("http://localhost:1337/ex");
CloseableHttpResponse response = httpClient.execute(httppost);
System.out.println("STATUS LINE");
System.out.println(response.getStatusLine().toString());
System.out.println("HEADER");
Header[] h = response.getAllHeaders();
for(int i = 0; i < h.length; i++) {
System.out.println(h[i]);
}
System.out.println("ENTITY.CONTENT");
try(BufferedReader br = new BufferedReader(new InputStreamReader(response.getEntity().getContent(), "UTF-8"))) {
String s;
while((s = br.readLine()) != null) {
System.out.println(s);
}
}
}
}
Javascript:
var init = {mode: "no-cors"};
async function f() {
return fetch("http://localhost:1337/ex", init).then(res => res.text()).then(posts => console.log(posts));
}
f();
I've been working on a problem with doing a synchronous call to JavaScript in a WebView (with a return value) and trying to narrow down the where and why of why it's not working. It seems to be that the WebView thread is blocking while the main thread is waiting for a response from it -- which shouldn't be the case since theWebView runs on a separate thread.
I've put together this small sample that demonstrates it (I hope) fairly clearly:
main.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:weightSum="1">
<WebView
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:id="#+id/webView"/>
</LinearLayout>
MyActivity.java:
package com.example.myapp;
import android.app.Activity;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.JavascriptInterface;
import android.webkit.WebViewClient;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
public class MyActivity extends Activity {
public final static String TAG = "MyActivity";
private WebView webView;
private JSInterface JS;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
webView = (WebView)findViewById(R.id.webView);
JS = new JSInterface();
webView.addJavascriptInterface(JS, JS.getInterfaceName());
WebSettings settings = webView.getSettings();
settings.setJavaScriptEnabled(true);
webView.setWebViewClient(new WebViewClient() {
public void onPageFinished(WebView view, String url) {
Log.d(TAG, JS.getEval("test()"));
}
});
webView.loadData("<script>function test() {JSInterface.log(\"returning Success\"); return 'Success';}</script>Test", "text/html", "UTF-8");
}
private class JSInterface {
private static final String TAG = "JSInterface";
private final String interfaceName = "JSInterface";
private CountDownLatch latch;
private String returnValue;
public JSInterface() {
}
public String getInterfaceName() {
return interfaceName;
}
// JS-side functions can call JSInterface.log() to log to logcat
#JavascriptInterface
public void log(String str) {
// log() gets called from Javascript
Log.i(TAG, str);
}
// JS-side functions will indirectly call setValue() via getEval()'s try block, below
#JavascriptInterface
public void setValue(String value) {
// setValue() receives the value from Javascript
Log.d(TAG, "setValue(): " + value);
returnValue = value;
latch.countDown();
}
// getEval() is for when you need to evaluate JS code and get the return value back
public String getEval(String js) {
Log.d(TAG, "getEval(): " + js);
returnValue = null;
latch = new CountDownLatch(1);
final String code = interfaceName
+ ".setValue(function(){try{return " + js
+ "+\"\";}catch(js_eval_err){return '';}}());";
Log.d(TAG, "getEval(): " + code);
// It doesn't actually matter which one we use; neither works:
if (Build.VERSION.SDK_INT >= 19)
webView.evaluateJavascript(code, null);
else
webView.loadUrl("javascript:" + code);
// The problem is that latch.await() appears to block, not allowing the JavaBridge
// thread to run -- i.e., to call setValue() and therefore latch.countDown() --
// so latch.await() always runs until it times out and getEval() returns ""
try {
// Set a 4 second timeout for the worst/longest possible case
latch.await(4, TimeUnit.SECONDS);
} catch (InterruptedException e) {
Log.e(TAG, "InterruptedException");
}
if (returnValue == null) {
Log.i(TAG, "getEval(): Timed out waiting for response");
returnValue = "";
}
Log.d(TAG, "getEval() = " + returnValue);
return returnValue;
}
// eval() is for when you need to run some JS code and don't care about any return value
public void eval(String js) {
// No return value
Log.d(TAG, "eval(): " + js);
if (Build.VERSION.SDK_INT >= 19)
webView.evaluateJavascript(js, null);
else
webView.loadUrl("javascript:" + js);
}
}
}
When running, the following results:
Emulator Nexus 5 API 23:
05-25 13:34:46.222 16073-16073/com.example.myapp D/JSInterface: getEval(): test()
05-25 13:34:50.224 16073-16073/com.example.myapp I/JSInterface: getEval(): Timed out waiting for response
05-25 13:34:50.224 16073-16073/com.example.myapp D/JSInterface: getEval() =
05-25 13:34:50.225 16073-16073/com.example.myapp I/Choreographer: Skipped 239 frames! The application may be doing too much work on its main thread.
05-25 13:34:50.235 16073-16150/com.example.myapp I/JSInterface: returning Success
05-25 13:34:50.237 16073-16150/com.example.myapp D/JSInterface: setValue(): Success
(16073 is 'main'; 16150 is 'JavaBridge')
As you can see, the main thread times out waiting for theWebView to call setValue(), which it doesn't until latch.await() has timed out and main thread execution has continued.
Interestingly, trying with an earlier API level:
Emulator Nexus S API 14:
05-25 13:37:15.225 19458-19458/com.example.myapp D/JSInterface: getEval(): test()
05-25 13:37:15.235 19458-19543/com.example.myapp I/JSInterface: returning Success
05-25 13:37:15.235 19458-19543/com.example.myapp D/JSInterface: setValue(): Success
05-25 13:37:15.235 19458-19458/com.example.myapp D/JSInterface: getEval() = Success
05-25 13:37:15.235 19458-19458/com.example.myapp D/MyActivity: Success
(19458 is 'main'; 19543 is 'JavaBridge')
Things work correctly in sequence, with getEval() causing the WebView to call setValue(), which then exits latch.await() before it times out (as you'd expect/hope).
(I've also tried with an even earlier API level, but things crash out due to what may be, as I understand it, an emulator-only bug in 2.3.3 that never got fixed.)
So I'm at a bit of a loss. In digging around, this seems like the correct approach to doing things. It certainly seems like the correct approach because it works properly on API level 14. But then it's failing on later versions — and I've tested on 5.1 and 6.0 without success.
Look more about migration WebView with Android 4.4.
See description on Android Docs I think you need to use another method for funning your JS action.
For example, base on that doc - Running JS Async Asynchronously evaluates JavaScript in the context of the currently displayed page. If non-null, |resultCallback| will be invoked with any result returned from that execution. This method must be called on the UI thread and the callback will be made on the UI thread.
I am implementing GTM on a website. Website has 400+ pages on which GTM script has to be placed. Is there any way to inject GTM script on every page right after the body tag? Website uses mixed technology, some pages are in ASP.NET 4.0 and some in MVC 4.0. Below is the sample script to be added:
<!-- Google Tag Manager -->
<noscript><iframe src="//www.googletagmanager.com/ns.html?id=GTM-#####"
height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
<script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
'//www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
})(window,document,'script','dataLayer','GTM-#####');</script>
<!-- End Google Tag Manager -->
Based on the edit you did in your question, you need an HTTP Module that will modify the code that is generated injecting your script.
First you need to create a class that derive from Stream, that will wrap you original Stream from Response.Filter.
public class GtmStream : Stream
{
private static string gtmScript = #"<!-- Google Tag Manager -->...";
private Stream _base;
public GtmStream(Stream stream)
{
_base = stream;
}
public override void Flush()
{
_base.Flush();
}
public override int Read(byte[] buffer, int offset, int count)
{
return _base.Read(buffer, offset, count);
}
public override void Write(byte[] buffer, int offset, int count)
{
var editedBuffer = GetByteArrayWithGtmScriptInjected(buffer);
_base.Write(editedBuffer, offset, editedBuffer.Length);
}
public byte[] GetByteArrayWithGtmScriptInjected(byte[] buffer)
{
var stringValue = System.Text.Encoding.UTF8.GetString(buffer);
if (!string.IsNullOrWhiteSpace(stringValue))
{
var position = stringValue.IndexOf("</body>");
if (position != -1)
{
stringValue = stringValue.Insert(position + 7, gtmScript);
}
}
return System.Text.Encoding.UTF8.GetBytes(stringValue.ToCharArray());
}
public override bool CanRead
{
get { throw new NotImplementedException(); }
}
public override bool CanSeek
{
get { throw new NotImplementedException(); }
}
public override bool CanWrite
{
get { throw new NotImplementedException(); }
}
public override long Seek(long offset, SeekOrigin origin)
{
throw new NotImplementedException();
}
public override void SetLength(long value)
{
throw new NotImplementedException();
}
public override long Length
{
get { throw new NotImplementedException(); }
}
public override long Position
{
get
{
throw new NotImplementedException();
}
set
{
throw new NotImplementedException();
}
}
}
Every time the method Write will be called, the Stream will check if it contains the tag and if it exists it will inject the script code just after it.
Then it will return the new byte array and call the Write method on the base Stream.
To plug it into your web application you need to create an HTTP Module, as follows:
public class GtmScriptModule : IHttpModule
{
private GtmStream gtmStream;
public void Init(HttpApplication context)
{
context.BeginRequest += new EventHandler(context_BeginRequest);
}
void context_BeginRequest(object sender, EventArgs e)
{
HttpApplication application = (HttpApplication)sender;
gtmStream = new GtmStream(application.Context.Response.Filter);
application.Context.Response.Filter = gtmStream;
}
public void Dispose()
{
}
}
This will simply set the Response.Filter to your custom Stream.
Finally you need to plug your HTTP Module in your web application:
<system.web>
...
<httpModules>
<add name="GtmScriptModule" type="TestMvcApplication.Modules.GtmScriptModule, TestMvcApplication" />
</httpModules>
</system.web>
If you want to dig deeper in the theory here are some useful links:
Filtering HTTP Requests with .NET
How to retrieve the HTML response generated by ASP.NET pages using HTTP module
How to inject HTML before closing html tag in a HTTPModule
How do I retrieve response html from within a HttpModule? (from here I took the skeleton for my custom Stream)
For the MVC pages uou should place your script in the default layout file of your web application and then you set the layout for every view in the _ViewStart.cshtml file.
For your Web Form pages you can do the same with a master page.
try creating an HTTP Module that will run on every request, check if the request is for a page, if so send back your javascript block
https://msdn.microsoft.com/en-us/library/vstudio/ms227673(v=vs.100).aspx
I want to know how to define the subscriber path.
For instance, declaration of subscribing path
stompClient.subscribe("/topic/simplemessagesresponse", function(servermessage) {
Why there are two parts 'topic' and 'simplemessageresponse' .. what they refere. How many such domain parts can be there and why ? My question is on not only for the client side, but also server side . SimpMessagingTemplate.convertAndSend("/topic/simplemessagesresponse", "Message to client");
There are tutorials showing the websocket server and client samples. But no enough details of rules to declare the subscriber path and how the subscriber path could be found.
What are the dependencies to change the path when it is declared in server and client side. I think another similar question is raised because of the a location change of a page where the websocket client is written.
Quoting the STOMP spec documentation:
Note that STOMP treats this destination as an opaque string and no
delivery semantics are assumed by the name of a destination. You
should consult your STOMP server's documentation to find out how to
construct a destination name which gives you the delivery semantics
that your application needs.
That means that destination semantics is broker specific:
For RabbitMQ: check out the Destinations section under the STOMP
plugin documentation- http://www.rabbitmq.com/stomp.html For
For ActiveMQ: check out the Working with Destinations with Stomp -
https://activemq.apache.org/stomp.html
I have implemented the websocket stomp by following this blog.
I replaced #SendTo by SimpMessagingTemplate.
Here is my sample ChatController
#Autowired
private SimpMessagingTemplate simpMessagingTemplate;
#MessageMapping("/dualchart")
#ResponseBody
public void dualchat(MessageDTO message) {
// forward message to destination
String destination = "/topic/dualchat/" + message.getToUser();
simpMessagingTemplate.convertAndSend(destination, message);
}
MessageDTO
#JsonIgnoreProperties
public class MessageDTO extends BaseModel {
private String fromUser;
private String toUser;
private String message;
public String getFromUser() {
return fromUser;
}
public void setFromUser(String fromUser) {
this.fromUser = fromUser;
}
public String getToUser() {
return toUser;
}
public void setToUser(String toUser) {
this.toUser = toUser;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
Web Socket Config
<websocket:message-broker application-destination-prefix="/app">
<websocket:stomp-endpoint path="/dualchat">
<websocket:sockjs />
</websocket:stomp-endpoint>
<websocket:simple-broker prefix="/topic" />
</websocket:message-broker>
Javascript
var socket = new SockJS("/starter.web.admin/dualchat");
var stompClient = Stomp.over(page.socket);
stompClient.connect({}, socketJsConnectedCallback, socketJsErrorCallback);
function socketJsConnectedCallback() {
var myId = "111"; // replace this Id
stompClient.subscribe('/topic/dualchat/' + myId, function(message) {
console.log("you reveived a message::::::::::" + JSON.stringify(message));
// you have message, and you can do anything with it
});
}
function socketJsErrorCallback(error){console.log(error);}
function sendMessage(message) {
var data = {
toUser : "1",
message : message
}
stompClient.send("/app/dualchat", {}, JSON.stringify(data );
}
Hope this will help next search...