I'm following this tutorial : https://www.baeldung.com/websockets-spring
I tested the app and it works perfectly when running on the embedded-tomcat server. However, when I try to deploy and run the same app on an external tomcat server it breaks, because instead of the URL being
localhost:8080/chat
it becomes
myhostIP:port/spring-boot-web-jsp/chat
So I modified the javascript file adding /spring-boot-web-jsp in front of the existing URLs. When I run the webapp the sockets connect successfully and send data. However now my Spring MVC Controller doesn't work.
My javascript :
var stompClient = null;
function setConnected(connected) {
document.getElementById('connect').disabled = connected;
document.getElementById('disconnect').disabled = !connected;
document.getElementById('conversationDiv').style.visibility
= connected ? 'visible' : 'hidden';
document.getElementById('response').innerHTML = '';
}
function connect() {
var socket = new SockJS('/spring-boot-web-jsp-1.0/chat');
stompClient = Stomp.over(socket);
stompClient.connect({}, function(frame) {
setConnected(true);
console.log('Connected: ' + frame);
stompClient.subscribe('/spring-boot-web-jsp-1.0/topic/messages', function(messageOutput) {
showMessageOutput(JSON.parse(messageOutput.body));
});
});
}
function disconnect() {
if(stompClient != null) {
stompClient.disconnect();
}
setConnected(false);
console.log("Disconnected");
}
function sendMessage() {
var from = document.getElementById('from').value;
var text = document.getElementById('text').value;
stompClient.send("/spring-boot-web-jsp-1.0/app/chat", {},
JSON.stringify({'from':from, 'text':text}));
}
function showMessageOutput(messageOutput) {
var response = document.getElementById('response');
var p = document.createElement('p');
p.style.wordWrap = 'break-word';
p.appendChild(document.createTextNode(messageOutput.from + ": "
+ messageOutput.text + " (" + messageOutput.time + ")"));
response.appendChild(p);
}
My Controller :
#MessageMapping("/chat")
#SendTo("/topic/messages")
public OutputMessage send(Message message) throws Exception {
String time = new SimpleDateFormat("HH:mm").format(new Date());
return new OutputMessage(message.getFrom(), message.getText(), time);
}
My message broker :
#Configuration
#EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
#Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/topic");
config.setApplicationDestinationPrefixes("/app");
}
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/chat");
registry.addEndpoint("/chat").withSockJS();
}
}
I've tried modifying the Controller to :
#MessageMapping("app/chat")
#SendTo("/topic/messages")
public OutputMessage send(Message message) throws Exception {
String time = new SimpleDateFormat("HH:mm").format(new Date());
return new OutputMessage(message.getFrom(), message.getText(), time);
}
#MessageMapping("spring-boot-web-jsp-1.0/app/chat")
#SendTo("spring-boot-web-jsp-1.0/topic/messages")
public OutputMessage send(Message message) throws Exception {
String time = new SimpleDateFormat("HH:mm").format(new Date());
return new OutputMessage(message.getFrom(), message.getText(), time);
}
and a bunch of other variations but none of them work.
How can I modify the Controller and javascript file to work when testing through external Apache Tomcat as well as embedded (setting a relative URL of some sort)? And how can I get this to work properly on the external Tomcat?
Remove your tomcat/webapps/ROOT directory
Rename your final jar/war/ear file to ROOT.jar/war/ear
Deploy it on tomcat
Tomcat will deploy your app under root directory localhost:8080/chat
Related
I have a blazor server app, that calls an injected service to call a web socket in javascript to get some data. I want the web socket to return the data to a c# instance callback on the service. The c# callback works fine from the javascript method, but not from the any of the websocket callbacks (e.g. onmessge, onerror, onclose, etc.). No errors, just no data.
I have tried making the instance a global javascript and even a static value in the calling class, but still does not work. I have googled to no end and tried every suggestion, but still no luck.
Here is index page:
#page "/"
#inject BlazorJavaScriptCallback.IService1 _service1
<h1>Hello, world!</h1>
<p>And the answer back from the web service is #Message</p>
#code {
protected string Message {get; set;}
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
Message = await _service1.CallWebService();
StateHasChanged();
}
}
}
The service:
using Microsoft.JSInterop;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace BlazorJavaScriptCallback
{
public interface IService1
{
Task<string> CallWebService();
}
public class Service1 : IService1
{
AutoResetEvent _stopWaitHandle = new AutoResetEvent(false);
String _message;
private IJSRuntime _jsruntime;
public Service1(IJSRuntime jsruntime)
{
_jsruntime = jsruntime;
}
public async Task<string> CallWebService()
{
_message = string.Empty;
var dotNetObjRef = DotNetObjectReference.Create(this);
await _jsruntime.InvokeVoidAsync("callWebService", dotNetObjRef, "OutputMessage");
_stopWaitHandle.WaitOne();
return _message;
}
[JSInvokable]
public async Task OutputMessage(string message)
{
_message = message;
Console.WriteLine($"**** OutputMessage {message}");
_stopWaitHandle.Set();
}
}
}
The javascript:
async function callWebService(instance, method) {
instance.invokeMethodAsync(method, "start");
var wsUri = "wss://127.0.0.1:80/Test/";
var websocket = new WebSocket(wsUri);
console.log("websocket has been setup");
websocket.onopen = function (e) {
console.log("websocket connected");
websocket.send("Hellow world")
};
websocket.onclose = function (e) {
instance.invokeMethodAsync(method, "closed");
console.log("websocket closed");
};
websocket.onmessage = function (e) {
console.log("websocket got message" + e.data);
instance.invokeMethodAsync(method, e.data);
websocket.close();
};
websocket.onerror = function (e) {
console.log("websocket error");
instance.invokeMethodAsync(method, "error");
websocket.close();
};
}
You will need to add this to the startup in ConfigureServices:
services.AddScoped<IService1, Service1>();
You will need to add the following to your _Hosts.cshtml:
<script src="~/scripts/Javascript.js"></script>
EDIT: it works when I try this without an injected service (putting the call and callback right in the razor page). Hrmmm.
By using Blazor's [JSInvokable] tag, call the method in .net in the JS WebSocket callback function onMessage(e). can solve the problem
E.g:
JS file
var url = "ws://127.0.0.1:1234";
var websocket;
var connected = false;
/**
* init webSocket
* #param callback
* #param value
* #constructor
*/
function ConnectServer(callback, value) {
if ('WebSocket' in window) {
websocket = new WebSocket(url);
} else if (window.WebSocket) {
websocket = new WebSocket(url);
} else if ('MozWebSocket' in window) {
websocket = new MozWebSocket(url);
} else {
alert("The browser version is too low! Please use Chrome, Firefox, IE10+ browsers!");
}
websocket.onopen = function () {
connected = true;
callback(value);
}
websocket.onclose = function (e) {
connected = false;
}
websocket.onmessage = function (e) {
onMessage(e);
}
websocket.onerror = function (e) {
alert("The websocket server is not connected, please make sure the server is running!")
};
}
function onMessage(e) {
var json = JSON.stringify(e.data);
// call .net method
DotNet.invokeMethodAsync('BlazorServerSample', 'CallBackMethod', json);
}
/**
* A shared method for sending information to the server
* #param jsonStr
*/
function sendMessage(jsonStr) {
connected ? websocket.send(jsonStr) : alert("websocket server is disconnected or not running")
}
function callWebSocket(data){
var json = JSON.stringify(data)
connected ? sendMessage(json ) : ConnectServer(sendMessage, json)
}
BlazorPage
#code{
#inject IJSRuntime JSRuntime
public static string? jsonStr { get; set; }
[JSInvokable]
public static Task<int> produceMessage(string json)
{
jsonStr = json;
return Task.FromResult(1);
}
public async Task CallWebSocket()
{
jsonStr = "";
// mock request data
data = "{'position': 3,'has_more_items': true,'items_html': 'Car'}";
await JSRuntime.InvokeVoidAsync("callWebSocket", data);
bool t = true;
int sec = 0;
do
{
await Task.Delay(1000);
sec++;
if (!string.IsNullOrEmpty(jsonStr) || sec > 10)
{
t= false;
}
} while (t);
// your business
}
}
Hope my answer can help you
I'm making a live connection between a Socket Server (Java) and a Socket Client (NodeJS). This is for a webinterface.
I can send data from NodeJS to Java, but not the other way around. I commented in the code, which positions I mean. I tried it already like you see with out.write("Hello World\n"); (with flush, of course). I tried also with out.println("Hello World"); (with flush, of course).
public class WebHandler {
private ServerSocket server;
private static Socket sock;
public void listen(int port) {
try {
server = new ServerSocket(port);
} catch (IOException e) {
System.out.println("Could not listen on port " + port);
System.exit(-1);
}
Bukkit.getScheduler().scheduleSyncRepeatingTask(Main.getPlugin(), new BukkitRunnable() {
#Override
public void run() {
try {
System.out.println("Waiting for connection");
final Socket socket = server.accept();
sock = socket;
final InputStream inputStream = socket.getInputStream();
final InputStreamReader streamReader = new InputStreamReader(inputStream);
BufferedReader br = new BufferedReader(streamReader);
// readLine blocks until line arrives or socket closes, upon which it returns null
String line = null;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
WebHandler.sendMessage();
} catch (IOException e) {
System.out.println("Accept failed: " + port);
System.exit(-1);
}
}
}, 0, 100);
}
// CRITICAL
public static void sendMessage() {
try {
PrintWriter out = new PrintWriter(sock.getOutputStream());
out.write("Hello World from Java!" + "\n");
out.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
}
var net = require('net');
var client = net.connect(9090, 'localhost');
client.setEncoding('utf8');
setInterval(function() {
console.log("Writing....")
var ret = client.write('Hello from node.js\n');
console.log("Wrote", ret)
}, 5000);
// CRITICAL
client.on('data', function(data) {
console.log('Received: ' + data);
});
Please don't let you distract because of the Bukkit.getScheduler()... It's only a Task Manager. Thanks in advance :D
You don't receive a message from java because you have set an interval which will always send messages to the server and the server will be stuck in the while loop.
I would suggest to stop the interval at some point so that sendMessage() will be called.
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();
My scenario is this:
Server application A saves a assigned task to a database.The change in
database is monitored by a change notification and there is a server hub running.
Client side Javascript code,running along with server application B, has to connect to the remote hub. so that it receives a notification whenever App A inserts into the DB.
Here is my server code
I had downloaded owin.cors package.
[assembly: OwinStartup(typeof(Global))]
namespace Demo
{
public class Global : System.Web.HttpApplication
{
public static void Configuration(IAppBuilder app)
{
app.Map("/signalr", map =>
{
map.UseCors(CorsOptions.AllowAll);
var hubConfiguration = new HubConfiguration
{
EnableDetailedErrors=true,
};
map.RunSignalR(hubConfiguration);
});
}
notificationHub.cs
public class NotificationHub : Hub
{
public static Hashtable UserIdLookupTable = new Hashtable(20);
public static Dictionary<string,Job> PendingNotificationTable = new
Dictionary<string,Job>(20);
public void OnChange(string userId,string task,string description,string
duration)
{
if (UserIdLookupTable.ContainsKey(userId))
{
this.Clients.Client(UserIdLookupTable[userId].ToString()).Notify(userId,
task);
UserIdLookupTable.Remove(userId);
if (PendingNotificationTable.ContainsKey(userId))
PendingNotificationTable.Remove(userId);
}
else
PendingNotificationTable.Add(userId, new Job(userId, task,
description, duration));
}
public override Task OnConnected()
{
string name =Context.QueryString["userId"];
registerConnectionId(name);
return base.OnConnected();
}
public void registerConnectionId(string userId)
{
if (UserIdLookupTable.ContainsKey(userId))
UserIdLookupTable[userId] = Context.ConnectionId;
else
UserIdLookupTable.Add(userId, Context.ConnectionId);
if(PendingNotificationTable.ContainsKey(userId))
{
Job j=PendingNotificationTable[userId];
OnChange(j.UserId, j.Description, j.EmployeeName, j.Duration);
}
}
Client Side Code connecting to the remote hub
My script includes
<script src="~/Scripts/jquery-1.6.4.min.js"></script>
<script src="~/Scripts/jquery.signalR-2.2.2.min.js"></script>
<script src="~/Scripts/HubConnection.js"></script>
HubConnection.js
function ConnectToHub()
{
jQuery.support.cors = true;
$.connection.hub.url = "http://myip:56698";
$.connection.hub.qs = { 'UserId' : '35' };
var connection = $.hubConnection();
var hub = connection.createHubProxy('NotificationHub');
hub.on('Notify', function(userName, message) {
console.log(userName + ' ' + message);
});
connection.logging = true;
connection.start().done(function () {
console.log('Now connected, connection ID=' + connection.id);
})
.fail(function (a)
{
console.log('Could not connect'+ a );
});
}
While debugging using chrome, it comes to connection.start and does not go in to success or fail.Just leaves the script.There is no sign that it has connected to the server.
It has to hit OnConnected on server right?
Wondering whether i have missed something.
The above client code is all i did on the client project (apart from installing signalr.client package.) Thats enough right?
i just completed a web based chat application based on ajax/php. But the problem with this app is that it has to continuously poll server to check for new messages, which in turn overloads the server if many people are using this app simultaneously.
now i want to implement a socket based chat app in JavaScript. I know there is no support for sockets in JavaScript so i decided to use "Flash as a socket gateway for JavaScript" i am using Linux and and new to flash. can someone help me with how to achieve this.
basically,
1) I want to make a small SWF object that just handles socket logic(minimum width and height so i can hide it easily with -ve margin.
2) I want to access this swf object with JavaScript
i got a code for simple socket in actionscript (from internet) but i cannot get it to compile using mxmlc(free flash compiler).
heres the code...
myXML = new XMLSocket;
myXML.onConnect = handleConnect;
myXML.onXML = handleXML;
myXML.onClose = handleDisconnect;
myXML.connect("http://www.yourServer.com", 12345);
function handleConnect(connectionStatus){
connectionStatus ? trace("Connected.") : trace("Connection failed.");
}
function handleXML(xmlObject){
trace("Object recieved:: "+xmlObject);
}
function sendXML(textToSend){
myXML.send(new XML('"+textToSend+""));
}
function handleDisconnect(){
trace("Connection lost.");
}
function closeConnection(){
trace("Closing connection to server.");
myXML.close();
}
i got a better code but this also does not compile
package
{
import flash.errors.*;
import flash.events.*;
import flash.net.Socket;
public class ChatSocket extends Socket
{
public var host:String;
public var port:uint;
private var socket:Socket;
public static var SOCK_CONNECTED:String = "onSockConnect";
public static var SOCK_IOERROR:String = "onSockIOError";
function ChatSocket(h:String, p:uint)
{
host = h;
port = p;
socket = this;
super(host, port);
initListeners();
}
public function sendMessage(str:String):void
{
if(connected)
{
socket.writeUTFBytes(str + "\n");
}
else
{
trace("Not connected, message not sent!");
}
}
public function readMessage():void
{
if(connected)
{
var str:String = socket.readUTFBytes(socket.bytesAvailable);
trace("Socket Server Response: " + str);
}
else
{
trace("No message read, not connected!");
}
}
private function initListeners():void
{
socket.addEventListener(Event.CLOSE, closeHandler);
socket.addEventListener(Event.CONNECT, connectHandler);
socket.addEventListener(IOErrorEvent.IO_ERROR,
ioErrorHandler);
}
private function closeHandler(event:Event):void
{
trace("Connection to [" + host + "] closed");
}
private function ioErrorHandler(event:IOErrorEvent):void
{
dispatchEvent(new Event(SOCK_IOERROR));
}
private function connectHandler(event:Event):void
{
trace("Connected to [" + host + "]");
dispatchEvent(new Event(SOCK_CONNECTED));
}
private function socketDataHandler(event:ProgressEvent):void
{
readMessage();
}
}
}
var sock:ChatSocket;
sock = new ChatSocket('127.0.0.1', 9990);
sock.addEventListener(ChatSocket.SOCK_CONNECTED, connected);
sock.addEventListener(ChatSocket.SOCK_IOERROR, ioError);
function ioError(e:Event):void
{
trace("Cant connect to " + sock.host + " on port " + sock.port);
}
function connected(e:Event):void
{
sock.sendMessage("are you hungry?");
}
ERROR IS:
localhost bin]$ ./mxmlc ChatSocket.as
Loading configuration file /home/lk/Documents/flex_sdk_3.4/frameworks/flex-config.xml
/home/lk/Documents/flex_sdk_3.4/bin/ChatSocket.as: Error: A file found in a source-path can not have more than one externally visible definition. ChatSocket;sock;ioError;connected
You may wish to check out gimite's web-socket-js. This is a socket gateway that conforms to the work-in-progress Web Socket API, so in future as browsers implement native WebSocket it will automatically switch over to the Flash-free alternative.
The following code lies outside the class and package {} blocks. That is not allowed.
var sock:ChatSocket;
sock = new ChatSocket('127.0.0.1', 9990);
sock.addEventListener(ChatSocket.SOCK_CONNECTED, connected);
sock.addEventListener(ChatSocket.SOCK_IOERROR, ioError);
function ioError(e:Event):void
{
trace("Cant connect to " + sock.host + " on port " + sock.port);
}
function connected(e:Event):void
{
sock.sendMessage("are you hungry?");
}
Declare a document class (that extends Sprite) and move ioError and connected methods to it. Make sock an instance variable instead of a local variable and add the declaration part of sock into its constructor.
//DocClass.as
package
{
public class DocClass
{
private var sock:ChatSocket;
public function DocClass()
{
sock = new ChatSocket('127.0.0.1', 9990);
sock.addEventListener(ChatSocket.SOCK_CONNECTED, connected);
sock.addEventListener(ChatSocket.SOCK_IOERROR, ioError);
}
private function ioError(e:Event):void
{
trace("Cant connect to " + sock.host + " on port " + sock.port);
}
private function connected(e:Event):void
{
sock.sendMessage("are you hungry?");
}
}
}