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'
}
Related
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 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 = "";
}
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...
I want to use SignalR to recognize Jquery client on WCF service and board him messages even without a request. (Ie after the first request the client sends to the service, the service can to know him and send him messages).
I do not know if this is the best way to do this, but it's all I could find. (Except WebSocket that are supported only in VS 2012).
I added in the Global file on the Service the following function:
protected void Application_Start(object sender, EventArgs e)
{
RouteTable.Routes.MapHubs();
}
I created Chat.cs:
public class Chat : Hub
{
public void Send(string message)
{
// Call the addMessage method on all clients
Clients.All.addMessage(message);
}
}
In the JS project I added the JS files of SignalR:
<script src="../JavaScript/jquery.signalR-1.1.2.min.js" type="text/javascript"></script>
<script src="/signalr/hubs" type="text/javascript"></script>
Function that uses it:
function Chat() {
$(function () {
// Proxy created on the fly
var chat = $.connection.chat;
// Declare a function on the chat hub so the server can invoke it
chat.client.addMessage = function (message) {
alert(message);
};
// Start the connection
$.connection.hub.start().done(function () {
// Call the chat method on the server
chat.server.send('aa');
});
});
}
This is something new to me so sorry if the question stupid, but how the SignalR of JS should know the Service, where I should define him? (This calls for a Cross-Domain)
(The variable $.connection.chat is Undefined)
I'm sure I missed a few things, especially the main thing that he how to link the service and JS via SignalR?
What I was missing is the use SignalR with cross-domain.
On Global file I changed the code:
protected void Application_Start(object sender, EventArgs e)
{
RouteTable.Routes.MapHubs(new HubConfiguration() { EnableCrossDomain = true });
}
It is important to note here that since I use cross-domain I have code in Application_BeginRequest function that should cancel it when done SignalR request otherwise it does not work. So I canceled it this way:
protected void Application_BeginRequest(object sender, EventArgs e)
{
//Here is testing whether this request SignalR, if not I do the following code
if (HttpContext.Current.Request.Path.IndexOf("signalr") == -1)
{
HttpContext.Current.Response.Cache.SetCacheability(HttpCacheability.NoCache);
HttpContext.Current.Response.Cache.SetNoStore();
HttpContext.Current.Response.AddHeader("Access-Control-Allow-Origin", "*");
if (HttpContext.Current.Request.HttpMethod == "OPTIONS")
{
HttpContext.Current.Response.AddHeader("Access-Control-Allow-Methods", "GET, POST");
HttpContext.Current.Response.AddHeader("Access-Control-Allow-Headers", "Content-Type, Authorization, Accept, x-requested-with");
HttpContext.Current.Response.AddHeader("Access-Control-Max-Age", "1728000");
HttpContext.Current.Response.End();
}
}
}
On the client:
I added the scripts of SignalR and Jquery, and I deleted this script:
<script src="/signalr/hubs" type="text/javascript"></script>
Because there's cross-domain calls it is not needed.
The connection is in the following function:
var connection;
var contosoChatHubProxy;
function RegisterToServiceMessege() {
connection = $.hubConnection();
connection.url = 'http://localhost:xxx/signalr';
contosoChatHubProxy = connection.createHubProxy('ChatHub');
//This part happens when function broadcastMessage is activated(from the server)
contosoChatHubProxy.on('broadcastMessage', function (userName, message) {
alert('You have a messege:\n' + userName + ' ' + message);
});
connection.start()
.done(function () {
console.log('Now connected, connection ID=' + connection.id);
})
.fail(function () { console.log('Could not connect'); });
}
Chat.cs on the server:
[HubName("ChatHub")]
public class Chat : Hub
{
[HubMethodName("Send")]
public void Send(string name, string message)
{
// Call the broadcastMessage method to update clients.
Clients.All.broadcastMessage(name, message);
}
}
Call to a function Send from one of customers is as follows:
function SendMessege() {
contosoChatHubProxy.invoke('Send', 'aaa', 'bbb').done(function () {
console.log('Invocation of NewContosoChatMessage succeeded');
}).fail(function (error) {
console.log('Invocation of NewContosoChatMessage failed. Error: ' + error);
});
}
All clients receive the sent message.
(You can check this by running several browsers simultaneously.)
Since version 2.0 of SignalR you can no longer enable Cors using this code:
protected void Application_Start(object sender, EventArgs e)
{
RouteTable.Routes.MapHubs(new HubConfiguration() { EnableCrossDomain = true });
}
Instead, you must add those two lines in your Startup class initializing method:
public void Configuration(IAppBuilder app)
{
app.UseCors(CorsOptions.AllowAll);
app.MapSignalR();
}
And, what may not be obvious for everyone, if you don't want to host SignalR using its self-hosting library, then remember to change your project to WebApplication. In my case I was trying to append Signalr to WCFProject. And runtime wouldn't even go to this Configuration method of Startup, thus causing constant errors forbidding access to signalr resources