I am working on a Single Page Chat Application that uses Web Socket. My question is :Is there a way to pass more than the message to the function on event #OnMessage? like passing also the user's nickname and photo.
I have tried the following code (added the parameters _nickname and _photo),but after I run it I get the problem :
Server Tomcat v7.0 Server at localhost failed to start.
JavaScript in HTML :
function sendMessage() {
console.log("0000000");
if (websocket != null) {
websocket.send(msg,_nicknname,_photo);
}
msg = "";
}
Web Socket ChatEndPoint.java:
#OnMessage
public void deliverChatMessege(Session session, String msg ,String _nickname,String _photo) throws IOException{
try {
if (session.isOpen()) {
//deliver message
ChatUser user = chatUsers.get(session);
doNotify(user.username, msg, _nickname,_photo, null);
}
} catch (IOException e) {
session.close();
}
}
I was thinking about a way to pass the message, nickname and photo Json-like from JavaScript but I don't know how to get it in the side of the web socket server.
Am I missing something ?
Please help me.
Thanks
With a send method you can only send strings (see docs). However, you can send a JSON object if you use JSON.stringify. Then in the server you can decode the string and you will have your data.
Example
function sendMessage() {
console.log("0000000");
if (websocket != null) {
var data = {
msg: msg,
nickname: _nickname,
photo: _photo
};
websocket.send(JSON.stringify(data));
}
msg = "";
}
Related
I'm trying to find a way to emit from my client an instruction to the server which is inside a JSON object.
Here's my problem, my server receive my first instruction. But my second instruction 'deleteFile' is inside a json object,and the server never received this second instruction.
I would like to know if this is possible, and if i'm doing it in the wrong way.
I want to do something like this:
Client: I emit 'instruction' with my var "message"
service.deleteFile = function (peer, filename, callback) {
if (! LoginService.connected || ! LoginService.socket) {
console.log("deleteFile : not connected to server");
callback("no server");
var message = {
message : 'deleteFile',
dest_list : _.flattenDeep([peer]),
filename : filename,
};
LoginService.socket.emit('instruction',(message));
console.log("service.deleteFile : " , message);
callback(200);
};
And on server app.js for 'instruction':
socket.on('instruction', function(jsonMessage){
var dest_list = jsonMessage.dest_list;
var message = jsonMessage.message;
var filename = jsonMessage.filename;
var user_id = dest_list;
var instruction = {
message : message,
user_id : user_id,
filename : filename,
};
if (dest_list.length){
for (var i = 0; i < dest_list.length; i++) {
var user_id = dest_list[i].toLowerCase();
if (user_id in socket_clients){
var socketId = socket_clients[user_id].socketId;
socket.broadcast.to(socketId).emit('instruction', instruction);
console.log(instruction); //print "{message:'deleteFile', user_id: ['emitter'], filename: 'thegoodfile'}
}
else{
console.log("Error", user_id);
}
}
} else{
console.log("Error");
} });
Then on server app.js for 'deleteFile'(this instruction is inside my JSON object emited from client):
socket.on('deleteFile', function(jsonMessage) {
console.log("Test message"); };
I think my server don't understand my instruction 'deleteFile', but I don't find a way to tell him that it is an instruction.
Tell me if I missed some informations.
Thank you if you can help.
Found a solution with this post: socket, emit event to server from server
I can't send from my server to himself with 'broadcast'. From socket.io doc:
// sending to all clients except sender
socket.broadcast.emit('broadcast', 'hello friends!');
It was written ..
So I used Event handler in Node (doc: https://nodejs.org/api/events.html) and it works.
I need to pass phonenumber from web page to desktop dialer. I have host, that catches phone number from page that passed from background.js:
var responseMessage = "";
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
console.log(request.phonenumber);
chrome.runtime.sendNativeMessage('com.avvd.microsipcallfield', {text: request.phonenumber},function(response) {
if (chrome.runtime.lastError) {
responseMessage = chrome.runtime.lastError.message
console.log(responseMessage);
} else {
responseMessage = response;
}} )
sendResponse(responseMessage);
});
and sending it to host:
{
"name": "com.avvd.microsipcallfield",
"description": "Call Field Microsip host",
"path": "C:\\Users\\All Users\\callFieldHost\\host.bat",
"type": "stdio",
"allowed_origins": [
"chrome-extension://lbhgehbjeemkjmooaeopmfljahdoombd/"
]
}
Host.bat
java -jar HostDecoder.jar %*
in general, for the time being there is no host, there will be an application that opens an object from JSON and sends it to the dialer. However
I wrote simple recorder and this is an output:
1argument: chrome-extension://lbhgehbjeemkjmooaeopmfljahdoombd/
2argument: --parent-window=0
While i try to send number i get in console
"Error when communicating with the native messaging host." and how we can see there is no number passed to host listener.
Can somebody advice me to look for to repair this?
I resolve issue. Trouble was in absent #echo off in launcher.
Ok. There is another question. I try to read incoming messages but get stability zero size.
private String read(InputStream in) throws IOException {
byte[] length = new byte[4];
in.read(length);
int size = getInt(length);
if (size == 0) {
throw new InterruptedIOException("Zero size message");
}
byte[] message = new byte[size];
in.read(b);
return new String(message, "UTF-8");
}
private int getInt(byte[] bytes) {
return (bytes[3]<<24) & 0xff000000|
(bytes[2]<<16) & 0x00ff0000|
(bytes[1]<< 8) & 0x0000ff00|
(bytes[0]<< 0) & 0x000000ff;
}
I send with native message an object
{text: 4325345423}
What i miss in this case?
Im trying to make a simple application. That is When I write a word at edittext in android app such as "Hi", Then android app send message "Hi" to node.js server and node.js server send message "Hi has sent successflly" to android app. This is just a example, actually my object is android send a data(message) to server, and receive another data(message) from server.
The problem is this. When I write a word at android app and press button, the message transmitted successfully(I can confirm by console at node.js). But I cant send message to android from node.js .. When I press send button, My android app shut down..
What android says is "java.lang.NullPointerException: Attempt to invoke virtual method 'void android.app.Activity.runOnUiThread(java.lang.Runnable)' on a null object reference" ..
Yesterday, this error didn't happened and another error occured. "cannot cast string to JSONObject."
I will show you my code.
Server Side(Node.js)
var app = require('express')();
var http = require('http').Server(app);
var io = require('socket.io')(http);
var port = 12000;
app.get('/', function(req, res) {
res.sendFile('index.html');
})
io.on('connection', function(socket) {
console.log('Android device has been connected');
socket.on('message', function(data) {
console.log('message from Android : ' + data);
Object.keys(io.sockets.sockets);
Object.keys(io.sockets.sockets).forEach(function (id) {
console.log("ID : ", id );
io.to(id).emit('message', data);
console.log(data + ' has sent successfully');
})
/*if (data != null) {
io.emit('message', {message : data + ' has received successfully'});
}*/
})
socket.on('disconnect', function() {
console.log('Android device has been disconnected');
})
})
http.listen(port, function() {
console.log('Server Start at port number ' + port);
})
Client Side (Android)
private Emitter.Listener handleIncomingMessages = new Emitter.Listener(){
#Override
public void call(final Object... args){
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
JSONObject data = (JSONObject) args[0];
String message;
try {
message = data.getString("text").toString();
Log.i("result", message);
addMessage(message);
} catch (JSONException e) {
Log.e("result", "Error : JSONException");
return;
} catch (ClassCastException e) {
Log.e("result", "Error : ClassCastException");
e.printStackTrace();
return;
}
}
});
}
};
private void sendMessage(){
String message = mInputMessageView.getText().toString().trim();
mInputMessageView.setText("");
addMessage(message);
JSONObject sendText = new JSONObject();
try{
sendText.put("text", message);
socket.emit("message", message);
}catch(JSONException e){
}
}
private void addMessage(String message) {
mMessages.add(new Message.Builder(Message.TYPE_MESSAGE)
.message(message).build());
// mAdapter = new MessageAdapter(mMessages);
mAdapter = new MessageAdapter( mMessages);
mAdapter.notifyItemInserted(0);
scrollToBottom();
}
private void scrollToBottom() {
mMessagesView.scrollToPosition(mAdapter.getItemCount() - 1);
}
I already searched similar problems that other people asked, but It didn't give me solution. Please help me. Thank you for reading long question.
p.s Because Im not English speaker, Im not good at English .. There will be many problems at grammar and writing skills. Thanks for understanding...
Reason this happens is because method getActivity() returns null. This might happen if you run this on a fragment after it is detached from an activity or activity is no longer visible. I would do a normal null check before like:
Activity activity = getActivity();
if(activity != null) {
activity.runOnUiThread(new Runnable() {...}
}
I'm not familiar with socket.emit() method but it might throw network exception since it's running on UI thread and you are not allowed to do that. I recommend using RxJava/RxAndroid if you want to do this on another thread.
If you want to do network operation just use it like this:
Observable
.fromRunnable(new Runnable {
void run() {
// here do your work
}
})
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<Void>() {
#Override
public void onCompleted() {
// not really needed here
}
#Override
public void onError(Throwable e) {
// handle errors on UI thread
}
#Override
public void onNext(Void void) {
// do something on UI thread after run is done
}
});
Basically what it does it calls method call from Callable you just made on separate thread and when it's over it invokes onNext method if no exception was thrown or onError method if exception was thrown from Subscriber class.
Note that Response class isn't part of the RxJava/RxAndroid API and you can make it if you want. You can make it a simple POJO class or anything else you need it to be. If you don't need to have response you can use Runnable instead of Callable and it will work just fine.
In order for this to work you need to add this dependencies to your modules Gradle file:
dependencies {
compile 'io.reactivex:rxandroid:1.2.1'
compile 'io.reactivex:rxjava:1.1.6'
}
I'm running a spring 3.1.2 backend on a weblogic 12.1.3 server.
In order to accept websocket connections, my configurator as follows:
public class SpringConfigurator extends Configurator {
private static final Logger LOGGER = LoggerFactory.make();
private static final Map<String, Map<Class<?>, String>> cache = new ConcurrentHashMap<String, Map<Class<?>, String>>();
private static final String MAGIC_STR = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
private static final String NO_VALUE = ObjectUtils.identityToString(new Object());
#SuppressWarnings("unchecked")
#Override
public <T> T getEndpointInstance(Class<T> endpointClass) throws InstantiationException {
WebApplicationContext wac = ContextLoader.getCurrentWebApplicationContext();
if (wac == null) {
String message = "Failed to find the root WebApplicationContext. Was ContextLoaderListener not used?";
LOGGER.error(message);
throw new IllegalStateException(message);
}
String beanName = ClassUtils.getShortNameAsProperty(endpointClass);
if (wac.containsBean(beanName)) {
T endpoint = wac.getBean(beanName, endpointClass);
if (LOGGER.isTraceEnabled()) {
LOGGER.trace("Using #ServerEndpoint singleton " + endpoint);
}
return endpoint;
}
Component annot = AnnotationUtils.findAnnotation(endpointClass, Component.class);
if ((annot != null) && wac.containsBean(annot.value())) {
T endpoint = wac.getBean(annot.value(), endpointClass);
if (LOGGER.isTraceEnabled()) {
LOGGER.trace("Using #ServerEndpoint singleton " + endpoint);
}
return endpoint;
}
beanName = getBeanNameByType(wac, endpointClass);
if (beanName != null) {
return (T) wac.getBean(beanName);
}
if (LOGGER.isTraceEnabled()) {
LOGGER.trace("Creating new #ServerEndpoint instance of type " + endpointClass);
}
return wac.getAutowireCapableBeanFactory().createBean(endpointClass);
}
// modifyHandshake() is called before getEndpointInstance()
#Override
public void modifyHandshake(ServerEndpointConfig sec, HandshakeRequest request, HandshakeResponse response) {
super.modifyHandshake(sec, request, response);
}
private String getBeanNameByType(WebApplicationContext wac, Class<?> endpointClass) {
String wacId = wac.getId();
Map<Class<?>, String> beanNamesByType = cache.get(wacId);
if (beanNamesByType == null) {
beanNamesByType = new ConcurrentHashMap<Class<?>, String>();
cache.put(wacId, beanNamesByType);
}
if (!beanNamesByType.containsKey(endpointClass)) {
String[] names = wac.getBeanNamesForType(endpointClass);
if (names.length == 1) {
beanNamesByType.put(endpointClass, names[0]);
} else {
beanNamesByType.put(endpointClass, NO_VALUE);
if (names.length > 1) {
String message = "Found multiple #ServerEndpoint's of type " + endpointClass + ", names=" + names;
LOGGER.error(message);
throw new IllegalStateException(message);
}
}
}
String beanName = beanNamesByType.get(endpointClass);
return NO_VALUE.equals(beanName) ? null : beanName;
}
}
The problem is when I try to open websocket connection via a javascript client, it correctly generates response headers as I debugged this location:
#Override
public void modifyHandshake(ServerEndpointConfig sec, HandshakeRequest request, HandshakeResponse response) {
super.modifyHandshake(sec, request, response);
}
But in client side it gives following error:
WebSocket connection to 'ws://localhost:7001/websocket' failed: Error during >WebSocket handshake: Invalid status line
In chrome developer tools the response seems as follows:
HTTP/0.9 200 OK
I think somehow http request does not upgrade to websocket connection.
I really appreciate any help regarding this issue.
I encountered exactly this issue today when testing http://showcase.omnifaces.org/push/socket on WebLogic 12.2.1.
Already at the first test attempt of the webapp, WebLogic throws the below exception when making a websocket connection:
java.lang.IllegalStateException: The async-support is disabled on this request: weblogic.servlet.internal.ServletRequest
Impl#6682044b[GET /omnifaces.push/counter?e6845a3a-26ed-4520-9824-63ffd85b24eb HTTP/1.1]
at weblogic.servlet.internal.ServletRequestImpl.startAsync(ServletRequestImpl.java:1949)
at weblogic.servlet.internal.ServletRequestImpl.startAsync(ServletRequestImpl.java:1925)
at javax.servlet.ServletRequestWrapper.startAsync(ServletRequestWrapper.java:432)
at weblogic.websocket.tyrus.TyrusServletFilter.doFilter(TyrusServletFilter.java:234)
...
It turns out that, on contrary to all other servers I tested, WebLogic's own TyrusServletFilter, which is responsible for handling websocket handshake requests, is internally installed after the filters provided via web.xml of the deployed webapp. The webapp shipped with a character encoding filter and a GZIP filter mapped on /*, so they were invoked before the websocket filter. This was strange at first sight, but I thought it is what it is, so I just added <async-supported>true</async-supported> to those webapp-provided filters so that the TyrusServletFilter can do its job.
However, when making a websocket connection, a JavaScript error in the client side occurred when a push message was being sent, exactly the one you faced:
WebSocket connection to 'ws://localhost:7001/omnifaces.push/counter?e6845a3a-26ed-4520-9824-63ffd85b24eb' failed: Error during WebSocket handshake: Invalid status line
It turns out that WebSockets just can't deal with GZIP responses. After disabling the GZIP filter, everything continued to work flawlessly.
The root problem is however that WebLogic should have installed its TyrusServletFilter before all webapp-provided filters. All other Java EE servers I ever have tested do this correctly. Your workaround of immediately dispatching and forwarding all websocket handshake requests to their target URL pattern, as mentioned in your comment on the question, is a good one. The alternative would be to reconfigure the web.xml-provided filters to not match websocket handshake requests anymore, e.g. by using a more specific URL pattern, or mapping to a specific servlet instead.
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...