Java SNMP4J Trap application is freezing the GUI - java

I have an SNMP trap application in Java that aims at listening to an SNMP agent and printing the received SNMP messages on a JTextArea in a JFrame window.
Part I below is my source code showing the content of class TrapReceiver. In this class, the listen method is the place that makes the most of the job. The class is intantiated within a JFrame class on which I intend to show the messages on so mentioned JTeaxtArea. I send the reference of the JTextArea object, the SNMP agent URL and the port into the constructor of the class TrapReceiver and then call the run method of TrapReceiver object to start execution in a seperate thread other than the JFrame instance. Part II below shows how I instantiate the class TrapReeceiver within the JFrame instance.
When I run the application I noticed that the JFrame instance (i.e. GUI) is freezing and no message is being printed on so mentioned JTeaxtArea within the JFrame instance which instantiates the class TrapReeceiver shown in Part I below.
My question is why the JFrame instance (i.e. GUI) is freezing although the TRapReceiver itself is executed as a separate thread? Also, I wonder what could be the possible solution to this freezing issue. Thanks in advance.
P.S.: I have verified that TrapReceiver works fine and can print messages to standard output when running as a stand alone application without GUI but it is this GUI that is freezing somehow due to some possible thread synch issue. I tried to run the TrapReceiver without putting onto a thread and even in this case the GUI was still freezing.
PART I
package com.[Intenionally removed].snmp;
import java.io.IOException;
import javax.swing.JTextArea;
import org.snmp4j.*;
import org.snmp4j.mp.MPv1;
import org.snmp4j.mp.MPv2c;
import org.snmp4j.security.Priv3DES;
import org.snmp4j.security.SecurityProtocols;
import org.snmp4j.smi.OctetString;
import org.snmp4j.smi.TcpAddress;
import org.snmp4j.smi.TransportIpAddress;
import org.snmp4j.smi.UdpAddress;
import org.snmp4j.transport.AbstractTransportMapping;
import org.snmp4j.transport.DefaultTcpTransportMapping;
import org.snmp4j.transport.DefaultUdpTransportMapping;
import org.snmp4j.util.MultiThreadedMessageDispatcher;
import org.snmp4j.util.ThreadPool;
public class TrapReceiver implements CommandResponder, Runnable {
private String targetSnmpAgentURL;
private int targetSnmpAgentPort;
private JTextArea outConsole;
public TrapReceiver() {
}
public TrapReceiver(JTextArea outConsole) {
this.outConsole = outConsole;
}
public TrapReceiver(JTextArea outConsole, String targetSnmpAgentURL, int targetSnmpAgentPort) {
this.targetSnmpAgentURL = targetSnmpAgentURL;
this.targetSnmpAgentPort = targetSnmpAgentPort;
this.outConsole = outConsole;
try {
listen(new UdpAddress(targetSnmpAgentURL + "/" + targetSnmpAgentPort));
} catch (IOException e) {
e.printStackTrace();
}
}
public final synchronized void listen(TransportIpAddress address) throws IOException {
AbstractTransportMapping transport;
if (address instanceof TcpAddress) {
transport = new DefaultTcpTransportMapping((TcpAddress) address);
} else {
transport = new DefaultUdpTransportMapping((UdpAddress) address);
}
ThreadPool threadPool = ThreadPool.create("DispatcherPool", 10);
MessageDispatcher mDispathcher = new MultiThreadedMessageDispatcher(
threadPool, new MessageDispatcherImpl());
// add message processing models
mDispathcher.addMessageProcessingModel(new MPv1());
mDispathcher.addMessageProcessingModel(new MPv2c());
// add all security protocols
SecurityProtocols.getInstance().addDefaultProtocols();
SecurityProtocols.getInstance().addPrivacyProtocol(new Priv3DES());
// Create Target
CommunityTarget target = new CommunityTarget();
target.setCommunity(new OctetString("public"));
Snmp snmp = new Snmp(mDispathcher, transport);
snmp.addCommandResponder(this);
transport.listen();
System.out.println("Listening on " + address);
try {
this.wait();
} catch (InterruptedException ex) {
Thread.currentThread().interrupt();
}
}
/**
* This method will be called whenever a pdu is received on the given port
* specified in the listen() method
*/
#Override
public synchronized void processPdu(CommandResponderEvent cmdRespEvent) {
//System.out.println("Received PDU...");
outConsole.append("Received PDU...\n");
PDU pdu = cmdRespEvent.getPDU();
if (pdu != null) {
outConsole.append("Trap Type = " + pdu.getType() + "\n");
outConsole.append("Alarm Type: " + pdu.getVariableBindings().get(4) + "\n");
outConsole.append("Alarm Message: " + pdu.getVariableBindings().get(9) + "\n\n");
}
}
#Override
public void run() {
try {
listen(new UdpAddress(targetSnmpAgentURL + "/" + targetSnmpAgentPort));
} catch (IOException e) {
outConsole.append("\nError occured while listening to SNMP messages: \n" + e.getMessage() + "\n\n");
}
}
} //end of class TrapReceiver
PART II
In the following, I run an instance of class TrapReceiver within a thread.
private void jButtonStartListeningSNMPActionPerformed(java.awt.event.ActionEvent evt) {
Thread snmpThread =
new Thread(new TrapReceiver(jTextAreaSNMPAlarmOutput, jTextFieldSnmpAgentUrl.getText().trim(), Integer.parseInt(jTextFieldSnmpAgentPort.getText().trim())));
snmpThread.start()
}

the problem is that you are calling listen() in the TrapReceiver constructor, which happens on the gui thread. you only want to call listen() in the run() method, as that's the part that happens in the new thread.

Related

Matlab Java Interoperability

Our web app acts as an integration layer which allows the users to run Matlab code (Matlab is a scientific programming language) which was compiled to Java, packaged up as jar files via browser (selected ones as in above image, except for remote_proxy-1.0.0.jar which is not, it is used for RMI).
The problem is that, Matlab Java runtime, contained inside the javabuilder-1.0.0.jar file, has a process-wide blocking mechanism which means if the first user sends an HTTP request to execute the cdf_read-1.0.0.jar or any Matlab-compiled-to-Java jars at all, then subsequent requests will block until the first one finishes and it will take no less than 5 seconds since JNI is used to invoke the native Matlab code and because the app server just spawns new threads to serve each request, but once again, due to the process-wide locking mechanism of Matlab Java runtime, these newly spawned threads will just block waiting for the first request to be fulfilled, thus our app can technically serve one user at a time.
So to work around this problem, for each such request, we start a new JVM process, send the request to this new process to run the job using RMI, then return the result back to the app server's process, then destroy the spawned process. So we've solved the blocking issue, but this is not very good at all in terms of memory used, this is a niche app, so number of users is in the range of thoudsands. Below is the code used to start a new process to run the BootStrap class which starts a new RMI registry, and binds a remote object to run the job.
package rmi;
import java.io.*;
import java.nio.file.*;
import static java.util.stream.Collectors.joining;
import java.util.stream.Stream;
import javax.enterprise.concurrent.ManagedExecutorService;
import org.slf4j.LoggerFactory;
//TODO: Remove sout
public class ProcessInit {
public static Process startRMIServer(ManagedExecutorService pool, String WEBINF, int port, String jar) {
ProcessBuilder pb = new ProcessBuilder();
Path wd = Paths.get(WEBINF);
pb.directory(wd.resolve("classes").toFile());
Path lib = wd.resolve("lib");
String cp = Stream.of("javabuilder-1.0.0.jar", "remote_proxy-1.0.0.jar", jar)
.map(e -> lib.resolve(e).toString())
.collect(joining(File.pathSeparator));
pb.command("java", "-cp", "." + File.pathSeparator + cp, "rmi.BootStrap", String.valueOf(port));
while (true) {
try {
Process p = pb.start();
pool.execute(() -> flushIStream(p.getInputStream()));
pool.execute(() -> flushIStream(p.getErrorStream()));
return p;
} catch (Exception ex) {
ex.printStackTrace();
System.out.println("Retrying....");
}
}
}
private static void flushIStream(InputStream is) {
try (BufferedReader br = new BufferedReader(new InputStreamReader(is))) {
br.lines().forEach(System.out::println);
} catch (IOException ex) {
LoggerFactory.getLogger(ProcessInit.class.getName()).error(ex.getMessage());
}
}
}
This class is used to start a new RMI registry so each HTTP request to execute Matlab code can be run in a separate process, the reason we do this is because each RMI registry is bound to a process, so we need a separate registry for each JVM process.
package rmi;
import java.rmi.RemoteException;
import java.rmi.registry.*;
import java.rmi.server.UnicastRemoteObject;
import java.util.logging.*;
import remote_proxy.*;
//TODO: Remove sout
public class BootStrap {
public static void main(String[] args) {
int port = Integer.parseInt(args[0]);
System.out.println("Instantiating a task runner implementation on port: " + port );
try {
System.setProperty("java.rmi.server.hostname", "localhost");
TaskRunner runner = new TaskRunnerRemoteObject();
TaskRunner stub = (TaskRunner)UnicastRemoteObject.exportObject(runner, 0);
Registry reg = LocateRegistry.createRegistry(port);
reg.rebind("runner" + port, stub);
} catch (RemoteException ex) {
Logger.getLogger(BootStrap.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
This class allows to submit the request to execute the Matlab code, returns the result, and kill the newly spawned process.
package rmi.tasks;
import java.rmi.*;
import java.rmi.registry.*;
import java.util.Random;
import java.util.concurrent.*;
import java.util.logging.*;
import javax.enterprise.concurrent.ManagedExecutorService;
import remote_proxy.*;
import rmi.ProcessInit;
public final class Tasks {
/**
* #param pool This instance should be injected using #Resource(name = "java:comp/DefaultManagedExecutorService")
* #param task This is an implementation of the Task interface, this
* implementation should extend from MATLAB class and accept any necessary
* arguments, e.g Class1 and it must implement Serializable interface
* #param WEBINF WEB-INF directory
* #param jar Name of the jar that contains this MATLAB function
* #throws RemoteException
* #throws NotBoundException
*/
public static final <T> T runTask(ManagedExecutorService pool, Task<T> task, String WEBINF, String jar) throws RemoteException, NotBoundException {
int port = new Random().nextInt(1000) + 2000;
Future<Process> process = pool.submit(() -> ProcessInit.startRMIServer(pool, WEBINF, port, jar));
Registry reg = LocateRegistry.getRegistry(port);
TaskRunner generator = (TaskRunner) reg.lookup("runner" + port);
T result = generator.runTask(task);
destroyProcess(process);
return result;
}
private static void destroyProcess(Future<Process> process) {
try {
System.out.println("KILLING THIS PROCESS");
process.get().destroy();
System.out.println("DONE KILLING THIS PROCESS");
} catch (InterruptedException | ExecutionException ex) {
Logger.getLogger(Tasks.class.getName()).log(Level.SEVERE, null, ex);
System.out.println("DONE KILLING THIS PROCESS");
}
}
}
The questions:
Do I have to start a new separate RMI registry and bind a remote to it for each new process?
Is there a better way to achieve the same result?
You don't want JVM startup time to be part of the perceived transaction time. I would start a large number of RMI JVMs ahead of time, dependending on the expected number of concurrent requests, which could be in the hundreds or even thousands.
You only need one Registry: rmiregistry.exe. Start it on its default port and with an appropriate CLASSPATH so it can find all your stubs and application classes they depend on.
Bind each remote object into that Registry with sequentially-increasing names of the general form runner%d.
Have your RMI client pick a 'runner' at random from the known range 1-N where N is the number of runners. You may need a more sophisticated mechanism than mere randomness to ensure that the runner is free at the time.
You don't need multiple Registry ports or even multiple Registries.

Communication management in client/server application

Let me explain the purpose of my application so you can guide me about the best possible approach.
The idea is building a web application to remotely manage some particular equipments that my company manufactures. These equipments will periodically connect to the remote server to send/receive certain data (through simple socket communication but they don't use Java); this data will be stored in the corresponding data base and will be available through the web application for the different users.
In the same way, when you access through the web interface, each client will be able to see their equipments and perform different changes in the configuration. At this point there are 2 possible options and this is the reason of this post:
The easiest but not the best option: the user performs some changes and I save those changes in the data base. When the equipment later establish communication to the server, then it'll read those changes and update its configuration.
The ideal solution: as soon as the user save the changes through the web interface and push the "send" button, those changes are sent to the corresponding equipment.
As mentioned above, these equipments will periodically open a socket communication (let's say every 5 minutes) to the server to send their configuration. At this moment, in order to implement the "ideal solution", the only option I can think of is not to close that socket so I can use it to immediately send information back to the equipment when a certain user makes any changes.
If this application grows along the time, I'm afraid that too many open sockets/threads can crash my application.
Let me illustrate with some code I was playing around. I know this is far from the final solution, it's just to help you understand what I'm looking for.
First of all, I register the socket server during the start-up of the web server (Tomcat in this case):
package org.listeners;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import org.sockets.KKMultiServer;
public class ApplicationListener implements ServletContextListener {
public void contextInitialized(ServletContextEvent event) {
KKMultiServer kKMultiServer = new KKMultiServer();
Thread serverThread = new Thread(kKMultiServer);
serverThread.start();
event.getServletContext().setAttribute("PlainKKMultiServer", kKMultiServer);
}
public void contextDestroyed(ServletContextEvent event) { }
}
This is the main socket server class that listens for new connections:
public class KKMultiServer implements Runnable {
private Map<Long, KKMultiServerThread_v2> createdThreads = new HashMap<Long, KKMultiServerThread_v2>();
#Override
public void run() {
boolean listening = true;
try (ServerSocket serverSocket = new ServerSocket(5000)) {
while (listening) {
KKMultiServerThread_v2 newServerThread = new KKMultiServerThread_v2(serverSocket.accept(), this);
Thread myThread = new Thread(newServerThread);
myThread.start();
Long threadId = myThread.getId();
System.out.println("THREAD ID: " + threadId);
}
} catch (IOException e) {
System.err.println("Could not listen on port " + 5000);
System.exit(-1);
}
}
public Map<Long, KKMultiServerThread_v2> getCreatedThreads() {
return createdThreads;
}
}
And the thread class created with every single petition from each of the equipments (dispensers) to handle the socket communication:
public class KKMultiServerThread_v2 implements Runnable {
private Socket socket = null;
PrintWriter out = null;
BufferedReader in = null;
private long dispenserCode;
private KKMultiServer kKMultiServer;
public KKMultiServerThread_v2(Socket socket, KKMultiServer kKMultiServer) {
this.socket = socket;
this.kKMultiServer = kKMultiServer;
}
public void run() {
try {
out = new PrintWriter(socket.getOutputStream(), true);
in = new BufferedReader(
new InputStreamReader(
socket.getInputStream()));
} catch (IOException e) {
e.printStackTrace();
}
readDataFromDispenser();
}
private void readDataFromDispenser() {
String inputLine;
try {
while ((inputLine = in.readLine()) != null) {
if (inputLine.equals("Bye")) {
break;
}
if (dispenserCode == 0) {
dispenserCode = 1111; // this code will be unique per equipment
this.kKMultiServer.getCreatedThreads().put(dispenserCode, this);
}
}
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
public void sendDataToDispenser(String dataToSend) {
if (!socket.isClosed() && socket.isConnected()) {
out.println(dataToSend);
} else {
this.kKMultiServer.getCreatedThreads().remove(this);
}
}
}
Now that the socket is created and alive I can use it directly from the web application to send messages back to the equipment (Struts Action in this case)
public class HelloWorldAction extends ActionSupport {
private static final long serialVersionUID = 1L;
public String sendMessageToDispenser() throws Exception {
ServletContext context = ServletActionContext.getServletContext();
KKMultiServer kKMultiServer = (KKMultiServer) context.getAttribute("PlainKKMultiServer");
Map<Long, KKMultiServerThread_v2> currentThreads = kKMultiServer.getCreatedThreads();
Iterator<Long> it = currentThreads.keySet().iterator();
while (it.hasNext()) {
Long key = (Long) it.next();
KKMultiServerThread_v2 currentThread = currentThreads.get(key);
currentThread.sendDataToDispenser("DATA TO YOU!");
}
return SUCCESS;
}
}
Do you think it's possible to perform this solution? I mean, keeping these connections open so I can access my equipments whenever necessary (without waiting for the periodically connections). What's the best approach? If you have any other suggestions please let me know.
Thank you very much.
To my mind it clearly depends on how many equipment will be connected to your system. Sockets are not always sending data so it can have low effect on the overall performance. Though, Socket are know to be a little slow, if you have a lot of data to send to/from your equipments, you should consider this.
If you want to have send data from your server to your client you have few solutions
Your server knows all your equipment after registering for example. When starting you equipment connect to the server. (be careful about local network redirection)
Your equipment and server use sockets to communicate
I don't think there is another solution but I can be wrong. If your equipment request your server every X seconds, it will never be exactly perfeclty on time.

Servlet-3 Async Context, how to do asynchronous writes?

Problem Description
Servlet-3.0 API allows to detach a request/response context and answer to it later.
However if I try to write a big amount of data, something like:
AsyncContext ac = getWaitingContext() ;
ServletOutputStream out = ac.getResponse().getOutputStream();
out.print(some_big_data);
out.flush()
It may actually block - and it does block in trivial test cases - for both Tomcat 7 and Jetty 8. The tutorials recommend to create a thread pool that would
handle such a setup - witch is generally the counter-positive to a traditional 10K architecture.
However if I have 10,000 open connections and a thread pool of let's say 10 threads,
it is enough for even 1% of clients that have low speed connections or just blocked
connection to block the thread pool and completely block the comet response or
slow it down significantly.
The expected practice is to get "write-ready" notification or I/O completion notification
and than continue to push the data.
How can this be done using Servlet-3.0 API, i.e. how do I get either:
Asynchronous Completion notification on I/O operation.
Get non-blocking I/O with write ready notification.
If this is not supported by the Servlet-3.0 API, are there any Web Server specific APIs (like Jetty Continuation or Tomcat CometEvent) that allow to handle such events truly asynchronously without faking asynchronous I/O using thread pool.
Does anybody know?
And if this is not possible can you confirm it with a reference to documentation?
Problem demonstration in a sample code
I had attached the code below that emulates event-stream.
Notes:
it uses ServletOutputStream that throws IOException to detect disconnected clients
it sends keep-alive messages to make sure clients are still there
I created a thread pool to "emulate" asynchronous operations.
In such an example I explicitly defined thread pool of size 1 to show the problem:
Start an application
Run from two terminals curl http://localhost:8080/path/to/app (twice)
Now send the data with curd -d m=message http://localhost:8080/path/to/app
Both clients received the data
Now suspend one of the clients (Ctrl+Z) and send the message once again curd -d m=message http://localhost:8080/path/to/app
Observe that another non-suspended client either received nothing or after the message was transfered stopped receiving keep-alive requests because other thread is blocked.
I want to solve such a problem without using thread pool, because with 1000-5000 open
connections I can exhaust the thread pool very fast.
The sample code below.
import java.io.IOException;
import java.util.HashSet;
import java.util.Iterator;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.LinkedBlockingQueue;
import javax.servlet.AsyncContext;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.ServletOutputStream;
#WebServlet(urlPatterns = "", asyncSupported = true)
public class HugeStreamWithThreads extends HttpServlet {
private long id = 0;
private String message = "";
private final ThreadPoolExecutor pool =
new ThreadPoolExecutor(1, 1, 50000L,TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>());
// it is explicitly small for demonstration purpose
private final Thread timer = new Thread(new Runnable() {
public void run()
{
try {
while(true) {
Thread.sleep(1000);
sendKeepAlive();
}
}
catch(InterruptedException e) {
// exit
}
}
});
class RunJob implements Runnable {
volatile long lastUpdate = System.nanoTime();
long id = 0;
AsyncContext ac;
RunJob(AsyncContext ac)
{
this.ac = ac;
}
public void keepAlive()
{
if(System.nanoTime() - lastUpdate > 1000000000L)
pool.submit(this);
}
String formatMessage(String msg)
{
StringBuilder sb = new StringBuilder();
sb.append("id");
sb.append(id);
for(int i=0;i<100000;i++) {
sb.append("data:");
sb.append(msg);
sb.append("\n");
}
sb.append("\n");
return sb.toString();
}
public void run()
{
String message = null;
synchronized(HugeStreamWithThreads.this) {
if(this.id != HugeStreamWithThreads.this.id) {
this.id = HugeStreamWithThreads.this.id;
message = HugeStreamWithThreads.this.message;
}
}
if(message == null)
message = ":keep-alive\n\n";
else
message = formatMessage(message);
if(!sendMessage(message))
return;
boolean once_again = false;
synchronized(HugeStreamWithThreads.this) {
if(this.id != HugeStreamWithThreads.this.id)
once_again = true;
}
if(once_again)
pool.submit(this);
}
boolean sendMessage(String message)
{
try {
ServletOutputStream out = ac.getResponse().getOutputStream();
out.print(message);
out.flush();
lastUpdate = System.nanoTime();
return true;
}
catch(IOException e) {
ac.complete();
removeContext(this);
return false;
}
}
};
private HashSet<RunJob> asyncContexts = new HashSet<RunJob>();
#Override
public void init(ServletConfig config) throws ServletException
{
super.init(config);
timer.start();
}
#Override
public void destroy()
{
for(;;){
try {
timer.interrupt();
timer.join();
break;
}
catch(InterruptedException e) {
continue;
}
}
pool.shutdown();
super.destroy();
}
protected synchronized void removeContext(RunJob ac)
{
asyncContexts.remove(ac);
}
// GET method is used to establish a stream connection
#Override
protected synchronized void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// Content-Type header
response.setContentType("text/event-stream");
response.setCharacterEncoding("utf-8");
// Access-Control-Allow-Origin header
response.setHeader("Access-Control-Allow-Origin", "*");
final AsyncContext ac = request.startAsync();
ac.setTimeout(0);
RunJob job = new RunJob(ac);
asyncContexts.add(job);
if(id!=0) {
pool.submit(job);
}
}
private synchronized void sendKeepAlive()
{
for(RunJob job : asyncContexts) {
job.keepAlive();
}
}
// POST method is used to communicate with the server
#Override
protected synchronized void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
{
request.setCharacterEncoding("utf-8");
id++;
message = request.getParameter("m");
for(RunJob job : asyncContexts) {
pool.submit(job);
}
}
}
The sample above uses threads to prevent blocking... However if the number of blocking clients is bigger than the size of the thread pool it would block.
How could it be implemented without blocking?
I've found the Servlet 3.0 Asynchronous API tricky to implement correctly and helpful documentation to be sparse. After a lot of trial and error and trying many different approaches, I was able to find a robust solution that I've been very happy with. When I look at my code and compare it to yours, I notice one major difference that may help you with your particular problem. I use a ServletResponse to write the data and not a ServletOutputStream.
Here my go-to Asynchronous Servlet class adapted slightly for your some_big_data case:
import java.io.IOException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import javax.servlet.AsyncContext;
import javax.servlet.AsyncEvent;
import javax.servlet.AsyncListener;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebInitParam;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.apache.log4j.Logger;
#javax.servlet.annotation.WebServlet(urlPatterns = { "/async" }, asyncSupported = true, initParams = { #WebInitParam(name = "threadpoolsize", value = "100") })
public class AsyncServlet extends HttpServlet {
private static final Logger logger = Logger.getLogger(AsyncServlet.class);
public static final int CALLBACK_TIMEOUT = 10000; // ms
/** executor service */
private ExecutorService exec;
#Override
public void init(ServletConfig config) throws ServletException {
super.init(config);
int size = Integer.parseInt(getInitParameter("threadpoolsize"));
exec = Executors.newFixedThreadPool(size);
}
#Override
public void service(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
final AsyncContext ctx = req.startAsync();
final HttpSession session = req.getSession();
// set the timeout
ctx.setTimeout(CALLBACK_TIMEOUT);
// attach listener to respond to lifecycle events of this AsyncContext
ctx.addListener(new AsyncListener() {
#Override
public void onComplete(AsyncEvent event) throws IOException {
logger.info("onComplete called");
}
#Override
public void onTimeout(AsyncEvent event) throws IOException {
logger.info("onTimeout called");
}
#Override
public void onError(AsyncEvent event) throws IOException {
logger.info("onError called: " + event.toString());
}
#Override
public void onStartAsync(AsyncEvent event) throws IOException {
logger.info("onStartAsync called");
}
});
enqueLongRunningTask(ctx, session);
}
/**
* if something goes wrong in the task, it simply causes timeout condition that causes the async context listener to be invoked (after the fact)
* <p/>
* if the {#link AsyncContext#getResponse()} is null, that means this context has already timed out (and context listener has been invoked).
*/
private void enqueLongRunningTask(final AsyncContext ctx, final HttpSession session) {
exec.execute(new Runnable() {
#Override
public void run() {
String some_big_data = getSomeBigData();
try {
ServletResponse response = ctx.getResponse();
if (response != null) {
response.getWriter().write(some_big_data);
ctx.complete();
} else {
throw new IllegalStateException(); // this is caught below
}
} catch (IllegalStateException ex) {
logger.error("Request object from context is null! (nothing to worry about.)"); // just means the context was already timeout, timeout listener already called.
} catch (Exception e) {
logger.error("ERROR IN AsyncServlet", e);
}
}
});
}
/** destroy the executor */
#Override
public void destroy() {
exec.shutdown();
}
}
During my research on this topic, this thread kept popping up, so figured I mention it here:
Servlet 3.1 introduced async operations on ServletInputStream and ServletOutputStream. See ServletOutputStream.setWriteListener.
An example can be found at http://docs.oracle.com/javaee/7/tutorial/servlets013.htm
this might be helpful
http://www.oracle.com/webfolder/technetwork/tutorials/obe/java/async-servlet/async-servlets.html
We can't quite cause the writes to be asynchronous. We realistically have to live with the limitation that when we do write something out to a client, we expect to be able to do so promptly and are able to treat it as an error if we don't. That is, if our goal is to stream data to the client as fast as possible and use the blocking/non-blocking status of the channel as a way to control the flow, we're out of luck. But, if we're sending data at a low rate that a client should be able to handle, we are able at least to promptly disconnect clients that don't read quickly enough.
For example, in your application, we send the keepalives at a slow-ish rate (every few seconds) and expect clients to be able to keep up with all the events they're being sent. We splurge the data to the client, and if it can't keep up, we can disconnect it promptly and cleanly. That's a bit more limited than true asynchronous I/O, but it should meet your need (and incidentally, mine).
The trick is that all of the methods for writing out output which just throw IOExceptions actually do a bit more than that: in the implementation, all the calls to things that can be interrupt()ed will be wrapped with something like this (taken from Jetty 9):
catch (InterruptedException x)
throw (IOException)new InterruptedIOException().initCause(x);
(I also note that this doesn't happen in Jetty 8, where an InterruptedException is logged and the blocking loop is immediately retried. Presumably you make to make sure your servlet container is well-behaved to use this trick.)
That is, when a slow client causes a writing thread to block, we simply force the write to be thrown up as an IOException by calling interrupt() on the thread. Think about it: the non-blocking code would consume a unit of time on one of our processing threads to execute anyway, so using blocking writes that are just aborted (after say one millisecond) is really identical in principle. We're still just chewing up a short amount of time on the thread, only marginally less efficiently.
I've modified your code so that the main timer thread runs a job to bound the time in each write just before we start the write, and the job is cancelled if the write completes quickly, which it should.
A final note: in a well-implemented servlet container, causing the I/O to throw out ought to be safe. It would be nice if we could catch the InterruptedIOException and try the write again later. Perhaps we'd like to give slow clients a subset of the events if they can't keep up with the full stream. As far as I can tell, in Jetty this isn't entirely safe. If a write throws, the internal state of the HttpResponse object might not be consistent enough to handle re-entering the write safely later. I expect it's not wise to try to push a servlet container in this way unless there are specific docs I've missed offering this guarantee. I think the idea is that a connection is designed to be shut down if an IOException happens.
Here's the code, with a modified version of RunJob::run() using a grotty simple illustration (in reality, we'd want to use the main timer thread here rather than spin up one per-write which is silly).
public void run()
{
String message = null;
synchronized(HugeStreamWithThreads.this) {
if(this.id != HugeStreamWithThreads.this.id) {
this.id = HugeStreamWithThreads.this.id;
message = HugeStreamWithThreads.this.message;
}
}
if(message == null)
message = ":keep-alive\n\n";
else
message = formatMessage(message);
final Thread curr = Thread.currentThread();
Thread canceller = new Thread(new Runnable() {
public void run()
{
try {
Thread.sleep(2000);
curr.interrupt();
}
catch(InterruptedException e) {
// exit
}
}
});
canceller.start();
try {
if(!sendMessage(message))
return;
} finally {
canceller.interrupt();
while (true) {
try { canceller.join(); break; }
catch (InterruptedException e) { }
}
}
boolean once_again = false;
synchronized(HugeStreamWithThreads.this) {
if(this.id != HugeStreamWithThreads.this.id)
once_again = true;
}
if(once_again)
pool.submit(this);
}
Is Spring an option for you? Spring-MVC 3.2 has a class called DeferredResult, which will gracefully handle your "10,000 open connections/10 server pool threads" scenario.
Example: http://blog.springsource.org/2012/05/06/spring-mvc-3-2-preview-introducing-servlet-3-async-support/
I've had a quick look at your listing, so I may have missed some points.
The advantage of a pool thread is to share thread resources between several tasks over time. Maybe you can solve your problem by spacing keepAlive responses of different http connections, instead of grouping all of them at the same time.

Trying to send GPS coordinates to the android emulator

I am trying to write a program that will send GPS coordinates using telnet.
I keep getting the following exception:
Exception in thread "Timer-0" java.lang.NullPointerException
at org.apache.commons.net.telnet.Telnet._sendByte(Telnet.java:1060)
at org.apache.commons.net.telnet.TelnetOutputStream.write(TelnetOutputStream.java:87)
at org.apache.commons.net.io.ToNetASCIIOutputStream.write(ToNetASCIIOutputStream.java:77)
at org.apache.commons.net.io.ToNetASCIIOutputStream.write(ToNetASCIIOutputStream.java:111)
at java.io.PrintStream.write(PrintStream.java:430)
at sun.nio.cs.StreamEncoder.writeBytes(StreamEncoder.java:202)
at sun.nio.cs.StreamEncoder.implFlushBuffer(StreamEncoder.java:272)
at sun.nio.cs.StreamEncoder.flushBuffer(StreamEncoder.java:85)
at java.io.OutputStreamWriter.flushBuffer(OutputStreamWriter.java:168)
at java.io.PrintStream.write(PrintStream.java:477)
at java.io.PrintStream.print(PrintStream.java:619)
at java.io.PrintStream.println(PrintStream.java:756)
at com.example.myandroid.gpsSender$1.run(gpsSender.java:34)
at java.util.TimerThread.mainLoop(Timer.java:512)
at java.util.TimerThread.run(Timer.java:462)
I don't know why I am getting this. Can you please tell me? thanks
Here is my code:
package com.example.myandroid;
import org.apache.commons.net.telnet.TelnetClient;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import java.net.SocketException;
import java.util.Timer;
import java.util.TimerTask;
public class gpsSender {
private TelnetClient telnet = new TelnetClient();
public static void main(String[] args) throws Exception {
gpsSender client = new gpsSender();
client.start();
}
public String start() throws Exception {
// Connect to the specified server
telnet.connect("localhost", 5554);
Timer timer = new Timer();
timer.schedule(new TimerTask() {
float longitude = 1;
float latitude = 1;
int count = 0;
PrintStream out = new PrintStream(telnet.getOutputStream());
public void run() {
out.println("geo fix " + String.valueOf(longitude) + " "
+ String.valueOf(latitude));
out.flush();
System.out.println("geo fix " + String.valueOf(longitude) + " "
+ String.valueOf(latitude));
longitude++;
latitude++;
count++;
if (count > 1000) {
cancel();
}
}
}, 0, 1000);
try {
telnet.disconnect();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return ("Done");
}
public void write(String value) {
try {
} catch (Exception e) {
e.printStackTrace();
}
}
}
The line
telnet.disconnect();
is going to execute, so you won't have an output stream to write to, hence the NPE. You should remove that line.
Instead of using localhost, try 10.0.2.2 - that's the IP address the emulator is usually on but I'm not sure if you can establish telnet comms with the emulator.
Edit: Here's a link to telnet the emulator but it's from a command window - perhaps you could write a small batch script for your tests to send gps coordinates but if you have to syncronise this somehow with your running test, you would have to do it from the Android app. It looks like the emulator is indeed on localhost and your 'pc' is on 10.0.2.2
Of course Use the 10.0.2.2 iP and also make sure you're giving the right permissions in your manifest .. but still, if you need to input the long. and lat. to use them in another app .. you don't really need telnet and for the emulator there's a little window called "Emulator Control" where you input manually the long. and lat. but using telnet communication with the emulator, that probably doesn't happen. and on a real phone you can use the "NetwProvider.getLocation()" but of course you can't test that on an emulator as well..
Thanks for publishing this question. This approach lets me easily manipulate the hardware and the sensors of the emulator from within my Robotium tests. Here is a code snippet to set the battery charge of the emulator to 100%:
TelnetClient telnet = new TelnetClient();
telnet.connect("10.0.2.2",5554);
PrintStream out = new PrintStream(telnet.getOutputStream());
out.println("power capacity 100");
out.flush();
telnet.disconnect();

Jini/JavaSpaces discovery error

On this article: http://java.sun.com/developer/technicalArticles/tools/JavaSpaces/ is a tutorial how to run JavaSpaces client. I wrote these classes in Eclipse, started Launch-All script and Run example. It works.
After that I exported these classes into executable jar (JavaSpaceClient.jar) and tried that jar with following command:
java -jar JavaSpaceClient.jar
It works fine, gives me result:
Searching for a JavaSpace...
A JavaSpace has been discovered.
Writing a message into the space...
Reading a message from the space...
The message read is: Здраво JavaSpace свете!
My problem is when I move this jar file on my other LAN computer, it shows me error when I type same command. Here is error:
cica#cica-System-Name:~/Desktop$ java -jar JavaSpaceClient.jar
Searching for a JavaSpace...
Jul 27, 2011 11:20:54 PM net.jini.discovery.LookupDiscovery$UnicastDiscoveryTask run
INFO: exception occurred during unicast discovery to biske-Inspiron-1525:4160 with constraints InvocationConstraints[reqs: {}, prefs: {}]
java.net.UnknownHostException: biske-Inspiron-1525
at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:175)
at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:384)
at java.net.Socket.connect(Socket.java:546)
at java.net.Socket.connect(Socket.java:495)
at com.sun.jini.discovery.internal.MultiIPDiscovery.getSingleResponse(MultiIPDiscovery.java:134)
at com.sun.jini.discovery.internal.MultiIPDiscovery.getResponse(MultiIPDiscovery.java:75)
at net.jini.discovery.LookupDiscovery$UnicastDiscoveryTask.run(LookupDiscovery.java:1756)
at net.jini.discovery.LookupDiscovery$DecodeAnnouncementTask.run(LookupDiscovery.java:1599)
at com.sun.jini.thread.TaskManager$TaskThread.run(TaskManager.java:331)
I just writes "Searching for JavaSpace..." and after a while prints these error messages.
Can someone help me with this error?
EDIT:
For discovery I am using LookupDiscovery class I've found on Internet:
import java.io.IOException;
import java.rmi.RemoteException;
import net.jini.core.lookup.ServiceRegistrar;
import net.jini.core.lookup.ServiceTemplate;
import net.jini.discovery.LookupDiscovery;
import net.jini.discovery.DiscoveryListener;
import net.jini.discovery.DiscoveryEvent;
/**
A class which supports a simple JINI multicast lookup. It doesn't register
with any ServiceRegistrars it simply interrogates each one that's
discovered for a ServiceItem associated with the passed interface class.
i.e. The service needs to already have registered because we won't notice
new arrivals. [ServiceRegistrar is the interface implemented by JINI
lookup services].
#todo Be more dynamic in our lookups - see above
#author Dan Creswell (dan#dancres.org)
#version 1.00, 7/9/2003
*/
public class Lookup implements DiscoveryListener {
private ServiceTemplate theTemplate;
private LookupDiscovery theDiscoverer;
private Object theProxy;
/**
#param aServiceInterface the class of the type of service you are
looking for. Class is usually an interface class.
*/
public Lookup(Class aServiceInterface) {
Class[] myServiceTypes = new Class[] {aServiceInterface};
theTemplate = new ServiceTemplate(null, myServiceTypes, null);
}
/**
Having created a Lookup (which means it now knows what type of service
you require), invoke this method to attempt to locate a service
of that type. The result should be cast to the interface of the
service you originally specified to the constructor.
#return proxy for the service type you requested - could be an rmi
stub or an intelligent proxy.
*/
Object getService() {
synchronized(this) {
if (theDiscoverer == null) {
try {
theDiscoverer =
new LookupDiscovery(LookupDiscovery.ALL_GROUPS);
theDiscoverer.addDiscoveryListener(this);
} catch (IOException anIOE) {
System.err.println("Failed to init lookup");
anIOE.printStackTrace(System.err);
}
}
}
return waitForProxy();
}
/**
Location of a service causes the creation of some threads. Call this
method to shut those threads down either before exiting or after a
proxy has been returned from getService().
*/
void terminate() {
synchronized(this) {
if (theDiscoverer != null)
theDiscoverer.terminate();
}
}
/**
Caller of getService ends up here, blocked until we find a proxy.
#return the newly downloaded proxy
*/
private Object waitForProxy() {
synchronized(this) {
while (theProxy == null) {
try {
wait();
} catch (InterruptedException anIE) {
}
}
return theProxy;
}
}
/**
Invoked to inform a blocked client waiting in waitForProxy that
one is now available.
#param aProxy the newly downloaded proxy
*/
private void signalGotProxy(Object aProxy) {
synchronized(this) {
if (theProxy == null) {
theProxy = aProxy;
notify();
}
}
}
/**
Everytime a new ServiceRegistrar is found, we will be called back on
this interface with a reference to it. We then ask it for a service
instance of the type specified in our constructor.
*/
public void discovered(DiscoveryEvent anEvent) {
synchronized(this) {
if (theProxy != null)
return;
}
ServiceRegistrar[] myRegs = anEvent.getRegistrars();
for (int i = 0; i < myRegs.length; i++) {
ServiceRegistrar myReg = myRegs[i];
Object myProxy = null;
try {
myProxy = myReg.lookup(theTemplate);
if (myProxy != null) {
signalGotProxy(myProxy);
break;
}
} catch (RemoteException anRE) {
System.err.println("ServiceRegistrar barfed");
anRE.printStackTrace(System.err);
}
}
}
/**
When a ServiceRegistrar "disappears" due to network partition etc.
we will be advised via a call to this method - as we only care about
new ServiceRegistrars, we do nothing here.
*/
public void discarded(DiscoveryEvent anEvent) {
}
}
My client program tries simply to search for JavaSpaces service write MessageEntry into and then retrieves message and prints it out. Here is client program:
import net.jini.space.JavaSpace;
public class SpaceClient {
public static void main(String argv[]) {
try {
MessageEntry msg = new MessageEntry();
msg.content = "Hello JavaSpaces wordls!";
System.out.println("Searching for JavaSpaces...");
Lookup finder = new Lookup(JavaSpace.class);
JavaSpace space = (JavaSpace) finder.getService();
System.out.println("JavaSpaces discovered.");
System.out.println("Writing into JavaSpaces...");
space.write(msg, null, 60*60*1000);
MessageEntry template = new MessageEntry();
System.out.println("Reading message from JavaSpaces...");
MessageEntry result = (MessageEntry) space.read(template, null, Long.MAX_VALUE);
System.out.println("Message: "+result.content);
} catch(Exception e) {
e.printStackTrace();
}
}
}
And of course this is MessageEntry class:
import net.jini.core.entry.*;
public class MessageEntry implements Entry {
public String content;
public MessageEntry() {
}
public MessageEntry(String content) {
this.content = content;
}
public String toString() {
return "MessageContent: " + content;
}
}
EDIT2:
I did discovery on two Windows computers.
After that I tried Windows - Ubuntu combiantion and it doesn't work. Maybe there are some network problems? When I ping each another everything is ok. Maybe there are some DNS issues on Ubuntu..
EDIT3:
Windows - Ubuntu combination works if JavaSpaces service is started up on Windows and client program is on Ubuntu. When I try to do reverse, to run JavaSpaces service on Ubuntu and run client on Windows error occurs.
Obviously there is some problem with Ubuntu. Ubuntu has installed OpenJDK installed by default. I installed Oracle JDK, and set JAVA_HOME and put JAVA_HOME/bin into PATH variable. I wonder maybe there is some problem with different versions of Java, maybe I am not using right one.
It is possible that the service registrar that you are running (on host biske-Inspiron-1525 at port 4160), is discovering it's hostname incorrectly (without domain name) and is therefore sending out the announcements with a short hostname. Therefore, after discovering the service registrar, it is possible that subsequently the client is trying to make a connection to the service registrar it cannot resolve the hostname if it is on a different domain.
To ensure that the service registrar is running with the correct hostname, try starting it with the following command line attribute:
-Dcom.sun.jini.reggie.unicastDiscoveryHost="biske-Inspiron-1525.and.its.domain"
It appears that you are doing unicast discovery to a specific host and port and that you can't look up that host.
Assuming you can resolve the name biske-Inspiron-1525 with DNS try removing the ":4160" part and see if the unicast lookup succeeds then.
Here is an example of the code I use to look up a service. It's a bit more complicated because I implement ServiceDiscoveryListener and handle service discovery that way. I actually keep a list of services and dynamically switch between then when one fails but I stripped that part out of the example. I am also using the Configuration part of Jini which I'll explain afterwards. The service interface I am using here is called "TheService":
public class JiniClient implements ServiceDiscoveryListener {
private TheService service = null;
private Class[] serviceClasses;
private ServiceTemplate serviceTemplate;
public JiniClient(String[] configFiles) throws ConfigurationException {
Configuration config = ConfigurationProvider.getInstance(configFiles,
getClass().getClassLoader());
// Set the security manager
System.setSecurityManager(new RMISecurityManager());
// Define the service we are interested in.
serviceClasses = new Class[] {TheService.class};
serviceTemplate = new ServiceTemplate(null, serviceClasses, null);
// Build a cache of all discovered services and monitor changes
ServiceDiscoveryManager serviceMgr = null;
DiscoveryManagement mgr = null;
try {
mgr = (DiscoveryManagement)config.getEntry(
getClass().getName(), // component
"discoveryManager", // name
DiscoveryManagement.class); // type
if (null == mgr) {
throw new ConfigurationException("entry for component " +
getClass().getName() + " name " +
"discoveryManager must be non-null");
}
} catch (Exception e) {
/* This will catch both NoSuchEntryException and
* ConfigurationException. Putting them both
* below just to make that clear.
*/
if( (e instanceof NoSuchEntryException) ||
(e instanceof ConfigurationException)) {
// default value
try {
System.err.println("Warning, using default multicast discover.");
mgr = new LookupDiscoveryManager(LookupDiscovery.ALL_GROUPS,
null, // unicast locators
null); // DiscoveryListener
} catch(IOException ioe) {
e.printStackTrace();
throw new RuntimeException("Unable to create lookup discovery manager: " + e.toString());
}
}
}
try {
serviceMgr = new ServiceDiscoveryManager(mgr, new LeaseRenewalManager());
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException("Unable to create service discovery manager: " + e.toString());
}
try {
serviceMgr.createLookupCache(serviceTemplate,
null, // no filter
this); // listener
} catch(Exception e) {
e.printStackTrace();
throw new RuntimeException("Unable to create serviceCache: " + e.getMessage());
}
}
public void serviceAdded(ServiceDiscoveryEvent evt) {
/* Called when a service is discovered */
ServiceItem postItem = evt.getPostEventServiceItem();
//System.out.println("Service appeared: " +
// postItem.service.getClass().toString());
if(postItem.service instanceof TheService) {
/* You may be looking for multiple services.
* The serviceAdded method will be called for each
* so you can use instanceof to figure out if
* this is the one you want.
*/
service = (TheService)postItem.service;
}
}
public void serviceRemoved(ServiceDiscoveryEvent evt) {
/* This notifies you of when a service goes away.
* You could keep a list of services and then remove this
* service from the list.
*/
}
public void serviceChanged(ServiceDiscoveryEvent evt) {
/* Likewise, this is called when a service changes in some way. */
}
The Configuration system allows you to dynamically configure the discovery method so you can switch to discover specific unicast systems or multicast without changing the app. Here is an example of a unicast discovery configuration file that you could pass to the above objects constructor:
import net.jini.core.discovery.LookupLocator;
import net.jini.discovery.LookupDiscoveryManager;
import net.jini.discovery.LookupDiscovery;
com.company.JiniClient {
discoveryManager = new LookupDiscoveryManager(
LookupDiscovery.ALL_GROUPS,
new LookupLocator[] { new LookupLocator("jini://biske-Inspiron-1525.mycompany.com")},
null,
this); // the current config
}
I found solution! That was dns issue. On Ubuntu my /etc/hosts file was:
192.168.1.3 biske-Inspiron-1525 # Added by NetworkManager
127.0.0.1 localhost.localdomain localhost
::1 biske-Inspiron-1525 localhost6.localdomain6 localhost6
127.0.1.1 biske-Inspiron-1525
# The following lines are desirable for IPv6 capable hosts
::1 localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
ff02::3 ip6-allhosts
I've just removed line 127.0.1.1 biske-Inspiron-1525 and now it works fine.
Little thing was destroyed million of my nerves :)

Categories