I am trying to run a tcp server that I set up in eclipse but I get the following problem in my console when I try to run it. If I need to add any other Java files than the one I included let me know.
[2012-08-07 09:13:16 - com.Proto1] Dx
UNEXPECTED TOP-LEVEL EXCEPTION:
java.lang.IllegalArgumentException: already added: Leneter/messaging/dataprocessing/messagequeueing/MessageQueue$1;
at com.android.dx.dex.file.ClassDefsSection.add(ClassDefsSection.java:123)
at com.android.dx.dex.file.DexFile.add(DexFile.java:163)
at com.android.dx.command.dexer.Main.processClass(Main.java:486)
at com.android.dx.command.dexer.Main.processFileBytes(Main.java:455)
at com.android.dx.command.dexer.Main.access$400(Main.java:67)
at com.android.dx.command.dexer.Main$1.processFileBytes(Main.java:394)
at com.android.dx.cf.direct.ClassPathOpener.processArchive(ClassPathOpener.java:245)
at com.android.dx.cf.direct.ClassPathOpener.processOne(ClassPathOpener.java:131)
at com.android.dx.cf.direct.ClassPathOpener.process(ClassPathOpener.java:109)
at com.android.dx.command.dexer.Main.processOne(Main.java:418)
at com.android.dx.command.dexer.Main.processAllFiles(Main.java:329)
at com.android.dx.command.dexer.Main.run(Main.java:206)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at com.android.ide.eclipse.adt.internal.build.DexWrapper.run(DexWrapper.java:180)
at com.android.ide.eclipse.adt.internal.build.BuildHelper.executeDx(BuildHelper.java:703)
at com.android.ide.eclipse.adt.internal.build.builders.PostCompilerBuilder.build(PostCompilerBuilder.java:577)
at org.eclipse.core.internal.events.BuildManager$2.run(BuildManager.java:728)
at org.eclipse.core.runtime.SafeRunner.run(SafeRunner.java:42)
at org.eclipse.core.internal.events.BuildManager.basicBuild(BuildManager.java:199)
at org.eclipse.core.internal.events.BuildManager.basicBuild(BuildManager.java:321)
at org.eclipse.core.internal.events.BuildManager.build(BuildManager.java:396)
at org.eclipse.core.internal.resources.Project$1.run(Project.java:618)
at org.eclipse.core.internal.resources.Workspace.run(Workspace.java:2344)
at org.eclipse.core.internal.resources.Project.internalBuild(Project.java:597)
at org.eclipse.core.internal.resources.Project.build(Project.java:124)
at com.android.ide.eclipse.adt.internal.project.ProjectHelper.doFullIncrementalDebugBuild(ProjectHelper.java:1000)
at com.android.ide.eclipse.adt.internal.launch.LaunchConfigDelegate.launch(LaunchConfigDelegate.java:147)
at org.eclipse.debug.internal.core.LaunchConfiguration.launch(LaunchConfiguration.java:855)
at org.eclipse.debug.internal.core.LaunchConfiguration.launch(LaunchConfiguration.java:704)
at org.eclipse.debug.internal.ui.DebugUIPlugin.buildAndLaunch(DebugUIPlugin.java:1047)
at org.eclipse.debug.internal.ui.DebugUIPlugin$8.run(DebugUIPlugin.java:1251)
at org.eclipse.core.internal.jobs.Worker.run(Worker.java:54)
[2012-08-07 09:13:16 - com.Proto1] Dx 1 error; aborting
[2012-08-07 09:13:16 - com.Proto1] Conversion to Dalvik format failed with error 1
Java
import eneter.messaging.diagnostic.EneterTrace;
import eneter.messaging.endpoints.typedmessages.*;
import eneter.messaging.messagingsystems.messagingsystembase.*;
import eneter.messaging.messagingsystems.tcpmessagingsystem.TcpMessagingSystemFactory;
import eneter.net.system.EventHandler;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.*;
public class AndroidNetCommunicationClientActivity extends Activity {
// Request message type
// The message must have the same name as declared in the service.
// Also, if the message is the inner class, then it must be static.
public static class MyRequest {
public String Text;
}
// Response message type
// The message must have the same name as declared in the service.
// Also, if the message is the inner class, then it must be static.
public static class MyResponse {
public int Length;
}
// UI controls
private Handler myRefresh = new Handler();
private EditText myMessageTextEditText;
private EditText myResponseEditText;
private Button mySendRequestBtn;
// Sender sending MyRequest and as a response receiving MyResponse.
private IDuplexTypedMessageSender<MyResponse, MyRequest> mySender;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.tcp_server);
// Get UI widgets.
myMessageTextEditText = (EditText) findViewById(R.id.messageTextEditText);
myResponseEditText = (EditText) findViewById(R.id.messageLengthEditText);
mySendRequestBtn = (Button) findViewById(R.id.sendRequestBtn);
// Subscribe to handle the button click.
mySendRequestBtn.setOnClickListener(myOnSendRequestClickHandler);
try {
openConnection();
} catch (Exception err) {
EneterTrace.error("Open connection failed.", err);
}
}
#Override
public void onDestroy() {
// Stop listening to response messages.
mySender.detachDuplexOutputChannel();
}
private void openConnection() throws Exception {
// Create sender sending MyRequest and as a response receiving
// MyResponse
IDuplexTypedMessagesFactory aSenderFactory = new DuplexTypedMessagesFactory();
mySender = aSenderFactory.createDuplexTypedMessageSender(
MyResponse.class, MyRequest.class);
// Subscribe to receive response messages.
mySender.responseReceived().subscribe(myOnResponseHandler);
// Create TCP messaging for the communication.
// Note: 10.0.2.2 is a special alias to the loopback (127.0.0.1)
// on the development machine
IMessagingSystemFactory aMessaging = new TcpMessagingSystemFactory();
IDuplexOutputChannel anOutputChannel = aMessaging
.createDuplexOutputChannel("tcp://10.0.2.2:8060/");
// Attach the output channel to the sender and be able to send
// messages and receive responses.
mySender.attachDuplexOutputChannel(anOutputChannel);
}
private void onSendRequest(View v) {
// Create the request message.
MyRequest aRequestMsg = new MyRequest();
aRequestMsg.Text = myMessageTextEditText.getText().toString();
// Send the request message.
try {
mySender.sendRequestMessage(aRequestMsg);
} catch (Exception err) {
EneterTrace.error("Sending the message failed.", err);
}
}
private void onResponseReceived(Object sender,
final TypedResponseReceivedEventArgs<MyResponse> e) {
// Display the result - returned number of characters.
// Note: Marshal displaying to the correct UI thread.
myRefresh.post(new Runnable() {
public void run() {
myResponseEditText.setText(Integer.toString(e
.getResponseMessage().Length));
}
});
}
private EventHandler<TypedResponseReceivedEventArgs<MyResponse>> myOnResponseHandler
= new EventHandler<TypedResponseReceivedEventArgs<MyResponse>>() {
public void onEvent(Object sender,
TypedResponseReceivedEventArgs<MyResponse> e) {
onResponseReceived(sender, e);
}
};
private OnClickListener myOnSendRequestClickHandler = new OnClickListener() {
public void onClick(View v) {
onSendRequest(v);
}
};
}
I deleted an invalid jar file from my build path and my problem was resolved.
Related
I am developing a client-server application, where I wanted to have a persistent connection between client-server, and I chose the CometD framework for the same.
I successfully created the CometD application.
Client -
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.cometd.bayeux.Channel;
import org.cometd.bayeux.Message;
import org.cometd.bayeux.client.ClientSessionChannel;
import org.cometd.client.BayeuxClient;
import org.cometd.client.transport.LongPollingTransport;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import com.synacor.idm.auth.LdapAuthenticator;
import com.synacor.idm.resources.LdapResource;
public class CometDClient {
private volatile BayeuxClient client;
private final AuthListner authListner = new AuthListner();
private LdapResource ldapResource;
public static void main(String[] args) throws Exception {
org.eclipse.jetty.util.log.Log.getProperties().setProperty("org.eclipse.jetty.LEVEL", "ERROR");
org.eclipse.jetty.util.log.Log.getProperties().setProperty("org.eclipse.jetty.util.log.announce", "false");
org.eclipse.jetty.util.log.Log.getRootLogger().setDebugEnabled(false);
CometDClient client = new CometDClient();
client.run();
}
public void run() {
String url = "http://localhost:1010/cometd";
HttpClient httpClient = new HttpClient();
try {
httpClient.start();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
client = new BayeuxClient(url, new LongPollingTransport(null, httpClient));
client.getChannel(Channel.META_HANDSHAKE).addListener(new InitializerListener());
client.getChannel(Channel.META_CONNECT).addListener(new ConnectionListener());
client.getChannel("/ldapAuth").addListener(new AuthListner());
client.handshake();
boolean success = client.waitFor(1000, BayeuxClient.State.CONNECTED);
if (!success) {
System.err.printf("Could not handshake with server at %s%n", url);
return;
}
}
private void initialize() {
client.batch(() -> {
ClientSessionChannel authChannel = client.getChannel("/ldapAuth");
authChannel.subscribe(authListner);
});
}
private class InitializerListener implements ClientSessionChannel.MessageListener {
#Override
public void onMessage(ClientSessionChannel channel, Message message) {
if (message.isSuccessful()) {
initialize();
}
}
}
private class ConnectionListener implements ClientSessionChannel.MessageListener {
private boolean wasConnected;
private boolean connected;
#Override
public void onMessage(ClientSessionChannel channel, Message message) {
if (client.isDisconnected()) {
connected = false;
connectionClosed();
return;
}
wasConnected = connected;
connected = message.isSuccessful();
if (!wasConnected && connected) {
connectionEstablished();
} else if (wasConnected && !connected) {
connectionBroken();
}
}
}
private void connectionEstablished() {
System.err.printf("system: Connection to Server Opened%n");
}
private void connectionClosed() {
System.err.printf("system: Connection to Server Closed%n");
}
private void connectionBroken() {
System.err.printf("system: Connection to Server Broken%n");
}
private class AuthListner implements ClientSessionChannel.MessageListener{
#Override
public void onMessage(ClientSessionChannel channel, Message message) {
Object data2 = message.getData();
System.err.println("Authentication String " + data2 );
if(data2 != null && data2.toString().indexOf("=")>0) {
String[] split = data2.toString().split(",");
String userString = split[0];
String passString = split[1];
String[] splitUser = userString.split("=");
String[] splitPass = passString.split("=");
LdapAuthenticator authenticator = new LdapAuthenticator(ldapResource);
if(authenticator.authenticateToLdap(splitUser[1], splitPass[1])) {
// client.getChannel("/ldapAuth").publish("200:success from client "+user);
// channel.publish("200:Success "+user);
Map<String, Object> data = new HashMap<>();
// Fill in the structure, for example:
data.put(splitUser[1], "Authenticated");
channel.publish(data, publishReply -> {
if (publishReply.isSuccessful()) {
System.out.print("message sent successfully on server");
}
});
}
}
}
}
}
Server - Service Class
import java.util.List;
import java.util.concurrent.BlockingQueue;
import org.cometd.bayeux.MarkedReference;
import org.cometd.bayeux.Promise;
import org.cometd.bayeux.server.BayeuxServer;
import org.cometd.bayeux.server.ConfigurableServerChannel;
import org.cometd.bayeux.server.ServerChannel;
import org.cometd.bayeux.server.ServerMessage;
import org.cometd.bayeux.server.ServerSession;
import org.cometd.server.AbstractService;
import org.cometd.server.ServerMessageImpl;
import com.synacor.idm.resources.AuthenticationResource;
import com.synacor.idm.resources.AuthenticationResource.AuthC;
public class AuthenticationService extends AbstractService implements AuthenticationResource.Listener {
String authParam;
BayeuxServer bayeux;
BlockingQueue<String> sharedResponseQueue;
public AuthenticationService(BayeuxServer bayeux) {
super(bayeux, "ldapagentauth");
addService("/ldapAuth", "ldapAuthentication");
this.bayeux = bayeux;
}
public void ldapAuthentication(ServerSession session, ServerMessage message) {
System.err.println("********* inside auth service ***********");
Object data = message.getData();
System.err.println("****** got data back from client " +data.toString());
sharedResponseQueue.add(data.toString());
}
#Override
public void onUpdates(List<AuthC> updates) {
System.err.println("********* inside auth service listner ***********");
MarkedReference<ServerChannel> createChannelIfAbsent = bayeux.createChannelIfAbsent("/ldapAuth", new ConfigurableServerChannel.Initializer() {
public void configureChannel(ConfigurableServerChannel channel)
{
channel.setPersistent(true);
channel.setLazy(true);
}
});
ServerChannel reference = createChannelIfAbsent.getReference();
for (AuthC authC : updates) {
authParam = authC.getAuthStr();
this.sharedResponseQueue= authC.getsharedResponseQueue();
ServerChannel channel = bayeux.getChannel("/ldapAuth");
ServerMessageImpl serverMessageImpl = new ServerMessageImpl();
serverMessageImpl.setData(authParam);
reference.setBroadcastToPublisher(false);
reference.publish(getServerSession(), authParam, Promise.noop());
}
}
}
Event trigger class-
public class AuthenticationResource implements Runnable{
private final JerseyClientBuilder clientBuilder;
private final BlockingQueue<String> sharedQueue;
private final BlockingQueue<String> sharedResponseQueue;
private boolean isAuthCall = false;
private String userAuth;
private final List<Listener> listeners = new CopyOnWriteArrayList<Listener>();
Thread runner;
public AuthenticationResource(JerseyClientBuilder clientBuilder,BlockingQueue<String> sharedQueue,BlockingQueue<String> sharedResponseQueue) {
super();
this.clientBuilder = clientBuilder;
this.sharedQueue = sharedQueue;
this.sharedResponseQueue= sharedResponseQueue;
this.runner = new Thread(this);
this.runner.start();
}
public List<Listener> getListeners()
{
return listeners;
}
#Override
public void run() {
List<AuthC> updates = new ArrayList<AuthC>();
// boolean is = true;
while(true){
if(sharedQueue.size()<=0) {
continue;
}
try {
userAuth = sharedQueue.take();
// Notify the listeners
for (Listener listener : listeners)
{
updates.add(new AuthC(userAuth,sharedResponseQueue));
listener.onUpdates(updates);
}
updates.add(new AuthC(userAuth,sharedResponseQueue));
System.out.println("****** Auth consume ******** " + userAuth);
if(userAuth != null) {
isAuthCall = true;
}
} catch (Exception err) {
err.printStackTrace();
break;
}
// if (sharedQueue.size()>0) {
// is = false;
// }
}
}
public static class AuthC
{
private final String authStr;
private final BlockingQueue<String> sharedResponseQueue;
public AuthC(String authStr,BlockingQueue<String> sharedResponseQueue)
{
this.authStr = authStr;
this.sharedResponseQueue=sharedResponseQueue;
}
public String getAuthStr()
{
return authStr;
}
public BlockingQueue<String> getsharedResponseQueue()
{
return sharedResponseQueue;
}
}
public interface Listener extends EventListener
{
void onUpdates(List<AuthC> updates);
}
}
I have successfully established a connection between client and server.
Problems -
1- When I am sending a message from the server to the Client, the same message is sent out multiple times. I only expecting one request-response mechanism.
In my case- server is sending user credentila I am expecting result, whether the user is authenticated or not.
you can see in image how it is flooding with same string at client side -
2- There was other problem looping up of message between client and server, that I can be able to resolve by adding, but still some time looping of message is happening.
serverChannel.setBroadcastToPublisher(false);
3- If I change the auth string on sever, at client side it appears to be old one.
For example -
1 request from server - auth string -> user=foo,pass=bar -> at
client side - user=foo,pass=bar
2 request from server - auth string user=myuser,pass=mypass ->
at client side - user=foo,pass=bar
this are the three problems, please guide me and help me to resolve this.
CometD offer a request/response style of messaging using remote calls, both on the client and on the server (you want to use annotated services on the server).
Channel /ldapAuth has 2 subscribers: the remote client (which subscribes with authChannel.subscribe(...)), and the server-side AuthenticationService (which subscribes with addService("/ldapAuth", "ldapAuthentication")).
Therefore, every time you publish to that channel from AuthenticationService.onUpdates(...), you publish to the remote client, and then back to AuthenticationService, and that is why calling setBroadcastToPublisher(false) helps.
For authentication messages, it's probably best that you stick with remote calls, because they have a natural request/response semantic, rather than a broadcasting semantic.
Please read about how applications should interact with CometD.
About other looping, there are no loops triggered by CometD.
You have loops in your application (in AuthenticationService.onUpdates(...)) and you take from a queue that may have the same information multiple times (in AuthenticationResource.run() -- which by the way it's a spin loop that will likely spin a CPU core to 100% utilization -- you should fix that).
The fact that you see stale data it's likely not a CometD issue, since CometD does not store messages anywhere so it cannot make up user-specific data.
I recommend that you clean up your code using remote calls and annotated services.
Also, clean up your own code from spin loops.
If you still have the problem after the suggestions above, look harder for application mistakes, it's unlikely that this is a CometD issue.
public class MainActivity extends AppCompatActivity {
public static final int TRANSMIT_DATA = 1;
public static String string0;
public String temp;//定义全局变量,想要把string0的值传给它。
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
loadData();
System.out.println("Main output:ID="+Thread.currentThread().getId());
anotherThread();
}
public void anotherThread(){
new Thread(){
public void run() {
System.out.println("anotherThread :ID="+Thread.currentThread().getId());
System.out.println("anotherThread output: Content="+temp);
}
}.start(); //开启一个线程
}
private Handler dataHandler =new Handler(){
#Override
public void handleMessage(Message msg) {
switch (msg.what) {
case TRANSMIT_DATA:
System.out.println("handleMessage output:ID="+Thread.currentThread().getId());
System.out.println("handleMessage output: Content="+msg.obj);
temp=msg.obj.toString();
break;
default:
break;
}
}
};
public void loadData() {
OkHttpClient okHttpClient = new OkHttpClient();
//构造Request,
//builder.get()代表的是get请求,url方法里面放的参数是一个网络地址
Request.Builder builder = new Request.Builder();
final Map params = new LinkedHashMap();// 请求参数
Request request = builder.get()
.url("https://api.avatardata.cn/Jztk/Query?key=15f9ceafeeb94a2492fd84b8c68a554c&subject=4&model=c1&testType=rand")
.build();
//3将Request封装成call
Call call = okHttpClient.newCall(request);
//4,执行call,这个方法是异步请求数据
call.enqueue(new Callback() {
#Override
public void onFailure(Call call, IOException e) {
//失败调用
Log.e("MainActivity", "onFailure: " );
}
#Override
public void onResponse(Call call, final Response response) throws IOException {
//成功调用
Log.e("MainActivity", "onResponse: " );
//获取网络访问返回的字符串
string0 = response.body().string();
System.out.println("Asynchronous Request Output:ID="+Thread.currentThread().getId());
Message message = new Message();
message.obj = string0;
message.what =TRANSMIT_DATA;
dataHandler.sendMessage(message);
}
});
}
}
The picture is about System.out.println
Just like the picture show above: the "anotherThread output: Content=null", I want to Pass information from the main thread to the child thread (in the run method), how can I do it? Try to avoid changing the code of other methods as soon as possible.
Given that you want minimal code changes you could use ThreadLocal , value set in Parent threads ThreadLocal is available for all child threads
I think your "otherthread" starts and ends before the data is available in the temp variable, hence it prints null.
You can do something like:
a. Either you start/run your "otherthread" after you fill the temp variable in the handleMessage function.
b. Or if you insist on starting the "otherthread" before you have the data, have the thread in a synchronized way, check the temp variable for being non null after some interval. Also have some sortof boolean to let the thread know, to exit incase you didnt receive any data.
my 2 cents
I am implementing an upload feature using Grails where basically a user gets to upload a text file and then the system will persist each line of that text file as a database record. While the uploading works fine, larger files take time to process and therefore they ask to have a progress bar so that users can determine if their upload is still processing or an actual error has occurred.
To do this, what I did is to create two URLs:
/upload which is the actual URL that receives the uploaded text file.
/upload/status?uploadToken= which returns the status of a certain upload based on its uploadToken.
What I did is after processing each line, the service will update a session-level counter variable:
// import ...
class UploadService {
Map upload(CommonsMultipartFile record, GrailsParameterMap params) {
Map response = [success: true]
try {
File file = new File(record.getOriginalFilename())
FileUtils.writeByteArrayToFile(file, record.getBytes())
HttpSession session = WebUtils.retrieveGrailsWebRequest().session
List<String> lines = FileUtils.readLines(file, "UTF-8"), errors = []
String uploadToken = params.uploadToken
session.status.put(uploadToken,
[message: "Checking content of the file of errors.",
size: lines.size(),
done: 0])
lines.eachWithIndex { l, li ->
// ... regex checking per line and appending any error to the errors List
session.status.get(uploadToken).done++
}
if(errors.size() == 0) {
session.status.put(uploadToken,
[message: "Persisting record to the database.",
size: lines.size(),
done: 0])
lines.eachWithIndex { l, li ->
// ... Performs GORM manipulation here
session.status.get(uploadToken).done++
}
}
else {
response.success = false
}
}
catch(Exception e) {
response.success = false
}
response << [errors: errors]
return response
}
}
Then create a simple WebSocket implementation that connects to the /upload/status?uploadToken= URL. The problem is that I cannot access the session variable on POGOs. I even change that POGO into a Grails service because I thought that is the cause of the issue, but I still can't access the session variable.
// import ...
#ServerEndpoint("/upload/status")
#WebListener
class UploadEndpointService implements ServletContextListener {
#OnOpen
public void onOpen(Session userSession) { /* ... */ }
#OnClose
public void onClose(Session userSession, CloseReason closeReason) { /* ... */ }
#OnError
public void onError(Throwable t) { /* ... */ }
#OnMessage
public void onMessage(String token, Session userSession) {
// Both of these cause IllegalStateException
def session = WebUtils.retrieveGrailsWebRequest().session
def session = RequestContextHolder.currentRequestAttributes().getSession()
// This returns the session id but I don't know what to do with that information.
String sessionId = userSession.getHttpSessionId()
// Sends the upload status through this line
sendMessage((session.get(token) as JSON).toString(), userSession)
}
private void sendMessage(String message, Session userSession = null) {
Iterator<Session> iterator = users.iterator()
while(iterator.hasNext()) {
iterator.next().basicRemote.sendText(message)
}
}
}
And instead, gives me an error:
Caused by IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet/DispatcherPortlet: In this case,
use RequestContextListener or RequestContextFilter to expose the current request.
I already verified that the web socket is working by making it send a static String content. But what I want is to be able to get that counter and set it as the send message. I'm using Grails 2.4.4 and the Grails Spring Websocket plugin, while looks promising, is only available from Grails 3 onwards. Is there any way to achieve this, or if not, what approach should I use?
Much thanks to the answer to this question that helped me greatly solving my problem.
I just modified my UploadEndpointService the same as the one on that answer and instead of making it as a service class, I reverted it back into a POGO. I also configured it's #Serverendpoint annotation and added a configurator value. I also added a second parameter to the onOpen() method. Here is the edited class:
import grails.converters.JSON
import grails.util.Environment
import javax.servlet.annotation.WebListener
import javax.servlet.http.HttpSession
import javax.servlet.ServletContext
import javax.servlet.ServletContextEvent
import javax.servlet.ServletContextListener
import javax.websocket.CloseReason
import javax.websocket.EndpointConfig
import javax.websocket.OnClose
import javax.websocket.OnError
import javax.websocket.OnMessage
import javax.websocket.OnOpen
import javax.websocket.server.ServerContainer
import javax.websocket.server.ServerEndpoint
import javax.websocket.Session
import org.apache.log4j.Logger
import org.codehaus.groovy.grails.commons.GrailsApplication
import org.codehaus.groovy.grails.web.json.JSONObject
import org.codehaus.groovy.grails.web.servlet.GrailsApplicationAttributes
import org.springframework.context.ApplicationContext
#ServerEndpoint(value="/ep/maintenance/attendance-monitoring/upload/status", configurator=GetHttpSessionConfigurator.class)
#WebListener
class UploadEndpoint implements ServletContextListener {
private static final Logger log = Logger.getLogger(UploadEndpoint.class)
private Session wsSession
private HttpSession httpSession
#Override
void contextInitialized(ServletContextEvent servletContextEvent) {
ServletContext servletContext = servletContextEvent.servletContext
ServerContainer serverContainer = servletContext.getAttribute("javax.websocket.server.ServerContainer")
try {
if (Environment.current == Environment.DEVELOPMENT) {
serverContainer.addEndpoint(UploadEndpoint)
}
ApplicationContext ctx = (ApplicationContext) servletContext.getAttribute(GrailsApplicationAttributes.APPLICATION_CONTEXT)
GrailsApplication grailsApplication = ctx.grailsApplication
serverContainer.defaultMaxSessionIdleTimeout = grailsApplication.config.servlet.defaultMaxSessionIdleTimeout ?: 0
} catch (IOException e) {
log.error(e.message, e)
}
}
#Override
void contextDestroyed(ServletContextEvent servletContextEvent) {
}
#OnOpen
public void onOpen(Session userSession, EndpointConfig config) {
this.wsSession = userSession
this.httpSession = (HttpSession) config.getUserProperties().get(HttpSession.class.getName())
}
#OnMessage
public void onMessage(String message, Session userSession) {
try {
Map params = new JSONObject(message)
if(httpSession.status == null) {
params = [message: "Initializing file upload.",
size: 0,
token: 0]
sendMessage((params as JSON).toString())
}
else {
sendMessage((httpSession.status.get(params.token) as JSON).toString())
}
}
catch(IllegalStateException e) {
}
}
#OnClose
public void onClose(Session userSession, CloseReason closeReason) {
try {
userSession.close()
}
catch(IllegalStateException e) {
}
}
#OnError
public void onError(Throwable t) {
log.error(t.message, t)
}
private void sendMessage(String message, Session userSession=null) {
wsSession.basicRemote.sendText(message)
}
}
The real magic happens within the onOpen() method. There is where the accessing of the session variable takes place.
I already read these topics:
how to use SignalR in Android
Android Client doesn't get data but .net client getting data from SignalR server
I write a simple chat system with Android that works with SignalR.
It is supposed to the clients send messages (by calling SendMessage method on the server) and the server should call the NewMessage method on the clients.
Here is my ChatHub class (simplified) written in C#.
public class ChatHub : Hub
{
// Store the clients connections Id
static readonly List<string> _connectedClients;
public override Task OnConnected()
{
// Keep connections id
// This section works fine and when the android device connects to the server,
// Its connection id will stored.
_connectedClients.Add(Context.ConnectionId)
//... other codes
}
public void SendMessage(string message)
{
foreach (var connectionId in _connectedClients)
{
// according to the logs
// android device connection id exists here
// and it works fine.
Clients.Client(connectionId).NewMessage(message);
}
}
}
When the android client connects to the server, On the OnConnected method, the connection id will be stored in the _connectedClients and it works fine.
In the SendMessage method of the ChatHub class, We have the android device connection id, and I'm sure that the android device is within the list
And here is my Andoird codes:
public class ChatActivity extends AppCompatActivity
{
// private fields
HubConnection connection;
HubProxy hub;
ClientTransport transport;
protected void onCreate(Bundle savedInstanceState) {
Logger logger = new Logger() {
#Override
public void log(String message, LogLevel logLevel) {
Log.e("SignalR", message);
}
};
Platform.loadPlatformComponent(new AndroidPlatformComponent());
connection = new HubConnection("192.168.1.100");
hub = connection.createHubProxy("chatHub"); // case insensitivity
transport = new LongPollingTransport(connection.getLogger());
// no difference when using this:
//transport = new ServerSentEventsTransport(connection.getLogger());
// this event never fired!
hub.subscribe(new Object() {
public void NewMessage(String message)
{
Log.d("<Debug", "new message received in subscribe"); // won't work!
}
}
// this event never fired!
hub.on("NewMessage", new SubscriptionHandler() {
#Override
public void run() {
Log.d("<Debug", "new message received in `on`"); // won't work!
}
});
// connect to the server that works fine.
SignalRFuture<Void> awaitConnection = connection.start(transport);
try {
awaitConnection.get(); // seems useless when using this or not!
}
catch (Exception ex) {
}
// this method works fine.
hub.invoke("sendMessage", "this is a test message to the server")
.done(new Action<Void>() {
#Override
public void run(Void aVoid) throws Exception {
Log.d("<Debug", "message sent."); // Works fine
}
});
}
}
In the above java code, invoking the sendMessage on the server works fine and the server get the messages.
But the only problem is that the hub.on(...) or hub.subscribe(...) events are never be called by the server.
In a simple description, My app can send message, but can not receive message from the others.
Any suggestion will be appreciated.
For the futures this is the way I finally achieved the answer (please first read the question android codes):
public class ChatActivity extends AppCompatActivity
{
// private fields
HubConnection connection;
HubProxy hub;
ClientTransport transport;
protected void onCreate(Bundle savedInstanceState) {
Logger logger = new Logger() {
#Override
public void log(String message, LogLevel logLevel) {
Log.e("SignalR", message);
}
};
Platform.loadPlatformComponent(new AndroidPlatformComponent());
connection = new HubConnection("192.168.1.100");
hub = connection.createHubProxy("chatHub"); // case insensitivity
/* ****new codes here**** */
hub.subscribe(this);
transport = new LongPollingTransport(connection.getLogger());
/* ****new codes here**** */
connection.start(transport);
/* ****new codes here**** */
/* ****seems useless but should be here!**** */
hub.subscribe(new Object() {
#SuppressWarnings("unused")
public void newMessage(final String message, final String messageId, final String chatId,
final String senderUserId, final String fileUrl, final String replyToMessageId) {
}
});
/* ********************** */
/* ****new codes here**** */
/* **** the main method that I fetch data from server**** */
connection.received(new MessageReceivedHandler() {
#Override
public void onMessageReceived(final JsonElement json) {
runOnUiThread(new Runnable() {
public void run() {
JsonObject jsonObject = json.getAsJsonObject();
Log.e("<Debug>", "response = " + jsonObject.toString());
}
});
}
});
/* ********************** */
}
}
!important note:
The priority of the codes is important. this is how I fix my problem in this topic.
You does not provider parameters in your client-side which should be same as your side-site. The code should be below:
hub.on("NewMessage", new SubscriptionHandler1<String>() {
#Override
public void run(String message) {
Log.d("<Debug", "new message received in `on`");
}
},String.class); //do not forget say the String class in the end
I am trying to create a chat app using Ionic2 (Angular2). I have a Java Server and Ionic 2 Client.
I get the following error:
ORIGINAL EXCEPTION: ReferenceError: io is not defined
Any suggestions please?
Client
import { Component, NgZone } from '#angular/core';
import { Http } from "#angular/http";
declare var io;
//require ('io');
#Component({
templateUrl: 'build/pages/chat/chat.html',
})
export class ChatPage {
private socketHost: string = "http://localhost:3700";
private messages: string[] = [];
private zone: NgZone = null;
private chatBox: string = null;
private socket: any = null;
constructor(http: Http) {
this.messages = [];
this.zone = new NgZone({ enableLongStackTrace: false });
//let url = this.socketHost + "/fetch";
let url = this.socketHost;
http.get(url).subscribe((success) => {
var data = success.json();
for (var i = 0; i < data.length; i++) {
this.messages.push(data[i].message);
}
}, (error) => {
console.log(JSON.stringify(error));
});
this.chatBox = "";
this.socket = io(this.socketHost);
this.socket.on("chat_message", (msg) => {
this.zone.run(() => {
this.messages.push(msg);
});
});
}
send(message) {
if (message && message != "") {
this.socket.emit("chat_message", message);
}
this.chatBox = "";
}
}
HTML
<ion-navbar *navbar>
<ion-title>
Chat
</ion-title>
</ion-navbar>
<ion-content class="home">
<ion-list>
<ion-item *ngFor="let message of messages">{{message}}</ion-item>
</ion-list>
</ion-content>
<ion-footer-bar>
<ion-input>
<input type="text" [(ngModel)]="chatBox" placeholder="Message..." />
<button (click)="send(chatBox)">Send</button>
</ion-input>
</ion-footer-bar>
index.html
<script src="/socket.io/socket.io.js"></script>
Server
import com.corundumstudio.socketio.AckRequest;
import com.corundumstudio.socketio.Configuration;
import com.corundumstudio.socketio.SocketIOClient;
import com.corundumstudio.socketio.SocketIOServer;
import com.corundumstudio.socketio.listener.ConnectListener;
import com.corundumstudio.socketio.listener.DataListener;
import com.corundumstudio.socketio.listener.DisconnectListener;
public class Server {
public static void main(String[] args) {
Configuration config = new Configuration();
config.setHostname("localhost");
config.setPort(3700);
final SocketIOServer server = new SocketIOServer(config);
server.addConnectListener(new ConnectListener() {
#Override
public void onConnect(SocketIOClient client) {
System.out.println("onConnected");
client.sendEvent("message", new Message("", "Welcome to the chat!"));
}
});
server.addDisconnectListener(new DisconnectListener() {
#Override
public void onDisconnect(SocketIOClient client) {
System.out.println("onDisconnected");
}
});
server.addEventListener("send", Message.class, new DataListener<Message>() {
#Override
public void onData(SocketIOClient client, Message data, AckRequest ackSender) throws Exception {
System.out.println("onSend: " + data.toString());
server.getBroadcastOperations().sendEvent("message", data);
}
});
System.out.println("Starting server...");
server.start();
System.out.println("Server started");
}
}
UPDATE
I add the following to index.html, and I don't get any errors any more:
<script src="https://cdn.socket.io/socket.io-1.4.5.js"></script>
But it just hangs. And in Firebug, I can see that the following request is just hanging:
GET http://localhost:3700/
The following is printed in the server console:
onConnected
When the server is not running the following request times out as expected, but when the server is running, the request does return, but with a null response:
GET http://localhost:3700/socket.io/?EIO=3&transport=...LRQn9sx&sid=53081e79-81f3-4fc0-8fb7-17c8673938ca
200 OK
27ms
So it suggests that my server code or the communication between client and server is wrong I think.
Any ideas?
In your Angular app you're listening for and emitting chat_message.
In your Java server you're listening for send and emitting message.
This doesn't add up, does it?