How implement generic Server-Sent Events Servlet - java

I'm trying implement server code of Server-Sent Events in a generic way that any Object of my application could send a message to client, so I've decided implement a specific Servlet just for SSE. The initial test codes worked like a charm, but wasn't flexible enought to send messages from different parts of my application. So I've rewrite the code in a way that all objects that has a reference to Servlet object could send a message to the clients:
public class PushServlet extends HttpServlet {
private Thread threadServlet;
private boolean processando=true;
private MensagemSSEBean mensagem;
#Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
threadServlet=Thread.currentThread();
response.setContentType("text/event-stream; charset=utf-8");
while (processando){
if(!pausarThread())
break;
enviarMensagemParaOCliente(response.getWriter());
}
enviarMensagemDeFechamento(response.getWriter());
}
private void enviarMensagemParaOCliente(PrintWriter saida) {
ConversorMensagemSSE conversor = new ConversorMensagemSSE();
saida.print(conversor.converter(mensagem));
saida.flush();
}
private synchronized void enviarMensagemDeFechamento(PrintWriter saida) {
mensagem.setMensagem("#FECHAR_CONEXAO#");
enviarMensagemParaOCliente(saida);
saida.close();
}
public synchronized void enviarMensagem(MensagemSSEBean mensagem) throws IOException {
this.mensagem=mensagem;
threadServlet.notifyAll();
}
public synchronized void finalizar(){
processando=false;
}
private boolean pausarThread() {
try {
threadServlet.wait();
return true;
} catch (InterruptedException e) {
e.printStackTrace();
}
return false;
}
}
As you can see, I pause the Servlet Thread until something call "enviarMensagem". I didn't tested this code, basically cause I don't know how I can get this Servlet object. Could someone explain me how could I get this Servlet object from any Object?? Another important question, is this the ideal approach for this kind of problem??

Finally I implemented it in a generic way. The servlet class now send keep-alive every ten seconds or the messages in a shared queue:
public class PushServlet extends HttpServlet {
private boolean processing = true;
private HttpServletResponse response;
#Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.response = response;
configureAndStart();
while (processing) {
try {
sendMessages();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private void configureAndStart() throws IOException {
processing = true;
response.setContentType("text/event-stream; charset=utf-8");
sendMessage(new SSEMessageBean(SSEEventType.START));
}
private void sendMessages() throws IOException, InterruptedException {
SSEMessageBean message = MessageQueueController.getInstance().getNextMessage();
while (message != null) {
sendMessage(message);
message = MessageQueueController.getInstance().getNextMessage();
if (message.getEventType() != SSEEventType.END)
return;
}
Thread.sleep(10000);
sendMessage(new SSEMessageBean(SSEEventType.KEEP_ALIVE));
}
public void sendMessage(SSEMessageBean message) throws IOException {
SSEMessageConverter converter = new SSEMessageConverter();
PrintWriter out = response.getWriter();
out.print(converter.convert(message));
out.flush();
if (message.getEventType() == SSEEventType.END) {
processing = false;
out.close();
}
}
}
The objects that want send events to clients simply write in shared queue.

Related

How to get the XML from POST request and modify it in Servlet Filter?

I am currently working on a requirement where I need to get the XML (from POST request) in the servlet filter before the request reaches to the Spring controller and then I need to process the XML (cut off some empty nodes/elements) in the filter and then the call should proceed further.
I tried the below code (attached only snippet) and I was able to get the request body (XML) and able to set the modified response.
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
if (httpRequest.getMethod().equalsIgnoreCase("POST")) {
extractDataFromRequest(httpRequest);
httpResponse.getWriter().write("<root><root>");
}
chain.doFilter(request, wrappedResponse);
public static String extractDataFromRequest(HttpServletRequest request) throws IOException {
String line;
StringBuilder builder = new StringBuilder();
BufferedReader reader = request.getReader();
while ((line = reader.readLine()) != null) {
builder.append(line);
}
return builder.toString();
}
However, spring failed with the following exception.
Severe: java.lang.IllegalStateException: PWC3997: getReader() has already been called for this request
at org.apache.catalina.connector.Request.getInputStream(Request.java:1178)
at org.apache.catalina.connector.RequestFacade.getInputStream(RequestFacade.java:407)
at org.springframework.http.server.ServletServerHttpRequest.getBody(ServletServerHttpRequest.java:165)
at org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.readWithMessageConverters(RequestResponseBodyMethodProcessor.java:120)
at org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.resolveArgument(RequestResponseBodyMethodProcessor.java:100)
I am looking for a concrete implementation for this requirement from experts.
You can't use the InputStream twice, you need to create a wrapper class which keeps a repeatable copy of the InputStream.
public class ReadTwiceHttpServletRequestWrapper extends HttpServletRequestWrapper {
private ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
public ReadTwiceHttpServletRequestWrapper(HttpServletRequest request) {
super(request);
try {
IOUtils.copy(request.getInputStream(), outputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
#Override
public BufferedReader getReader() throws IOException {
return new BufferedReader(new InputStreamReader(new ByteArrayInputStream(outputStream.toByteArray())));
}
#Override
public ServletInputStream getInputStream() throws IOException {
final ByteArrayInputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray());
return new ServletInputStream() {
#Override
public int readLine(byte[] b, int off, int len) throws IOException {
return inputStream.read(b, off, len);
}
#Override
public boolean isFinished() {
return inputStream.available() > 0;
}
#Override
public boolean isReady() {
return true;
}
#Override
public void setReadListener(ReadListener arg0) {
// TODO Auto-generated method stub
}
#Override
public int read() throws IOException {
return inputStream.read();
}
};
}
public void setBody(String body) {
outputStream = new ByteArrayOutputStream();
try {
outputStream.write(body.getBytes());
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public String getBody() {
return new String(outputStream.toByteArray());
}
}
Then you need to initialise that with a Filter which is first in the chain.
public class ReadTwiceFilter implements Filter {
#Override
public void destroy() {
}
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
ReadTwiceHttpServletRequestWrapper readTwiceHttpServletRequestWrapper = new ReadTwiceHttpServletRequestWrapper(
(HttpServletRequest) request);
String newBody = readTwiceHttpServletRequestWrapper.getBody().replace("<soap:studentId>1</soap:studentId>", "<soap:studentId>2</soap:studentId>");
readTwiceHttpServletRequestWrapper.setBody(newBody);
chain.doFilter(readTwiceHttpServletRequestWrapper, response);
}
#Override
public void init(FilterConfig arg0) throws ServletException {
}
}
Change your implementation in the filter to use getInputStream instead of getReader method. This issue arises when the overall implementation invokes both getReader and getInputStream method on ServletRequest.
As mentioned in javadoc, only one of them can be called. Looking at the stacktrace; the controller(spring mvc) is invoking getInputStream on it and hence failing with a message getReader() has already been called...

Asynchronous property in Servlet 3.0 testing query

I have implemented Asynchronous property in Servlet 3.0 using below tutorial.
http://hmkcode.com/java-servlet-3-0-asynchronous-support/
After implementing a runnable class at back-end, I have observed that the 2 threads are created and one ends up in asynchronous manner and other does back-end processing. I was able to implement successfully the mentioned Asynchronous property.
In the runnable class, I have kept a sleep of 25 seconds and I have tried using outStream.println(Calendar.getInstance().getTimeInMillis()) in the servlet class, I have observed a deviation.The time value which is printed in println is the time when the request started but the outStream is printed on the URL hit page after 25 seconds.
I just want to understand when print was framed within servlet (based on time-stamp I came to this analysis), why is it printed in servlet URL hit page after worker class sleep time.
#WebServlet(name="asyncServlet",value = {"/async"},asyncSupported = true)
public class AsyncServlet extends HttpServlet
{
private static final long serialVersionUID = 1L;
#Override
protected void handleRequest(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
servletoutputstream out = response.getoutputstream();
final AsyncContext ctx = req.startAsync();
ctx.addListener(new AsyncListener() {
#Override
public void onTimeout(AsyncEvent arg0) throws IOException {
System.out.println("onTimeout...");
}
#Override
public void onStartAsync(AsyncEvent arg0) throws IOException {
System.out.println("onStartAsync...");
}
#Override
public void onError(AsyncEvent arg0) throws IOException {
System.out.println("onError...");
}
#Override
public void onComplete(AsyncEvent arg0) throws IOException {
System.out.println("onComplete...");
}
});
ctx.start(new Runnable() {
#Override
public void run() {
try {
Thread.currentThread.sleep(1000);
} catch InterruptedException e) {
e.printStackTrace();
}
ctx.complete();
}
});
out.write("Got the request"+calendar.getinstance().gettimeinmillis());
out.close();
}
}
Here I am printing out, the time captured in out string is before the sleep time yet it is printed in after sleep time.
I have tried with PrintWriter, yet same output is observed.
Is there any way to print the response before sleep time using above code.
Below is the code that works as you want.
#WebServlet(name = "asyncServlet", value = { "/async" }, asyncSupported = true)
public class AsyncServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
#Override
protected void doGet(HttpServletRequest req, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html");
final PrintWriter out = response.getWriter();
final AsyncContext ctx = req.startAsync();
ctx.addListener(new AsyncListener() {
#Override
public void onTimeout(AsyncEvent arg0) throws IOException {
System.out.println("onTimeout...");
}
#Override
public void onStartAsync(AsyncEvent arg0) throws IOException {
System.out.println("onStartAsync...");
}
#Override
public void onError(AsyncEvent arg0) throws IOException {
System.out.println("onError...");
}
#Override
public void onComplete(AsyncEvent arg0) throws IOException {
System.out.println("onComplete...");
out.close();
}
});
ctx.start(new Runnable() {
#Override
public void run() {
try {
Thread.sleep(3000);
}catch(InterruptedException e) {
e.printStackTrace();
}
try {
out.print("<br> Got the request : "+Calendar.getInstance().getTimeInMillis()+" For Async thread :"+Thread.currentThread().getName());
out.flush();
} catch (Exception e) {
e.printStackTrace();
}
ctx.complete();
}
});
try {
long time=Calendar.getInstance().getTimeInMillis();
out.print("<br>Got the request :"+time+" For Original thread completed :"+Thread.currentThread().getName());
out.flush();
} catch (Exception e) {
e.printStackTrace();
}
}
}
output :
Got the request :1426238802101 For Original thread completed :http-bio-8080-exec-14
After 3 seconds:
Got the request : 1426238805101 For Async thread :http-bio-8080-exec-9
Click here to see a demo project which contains details code of Asynchronous Servlet.

Need to wait for asynchronous api callback before I return from method in Java

import java.util.concurrent.CountDownLatch;
import quickfix.Initiator;
public class UserSession {
private final CountDownLatch latch = new CountDownLatch(1);
public String await() {
try {
System.out.println("waiting...");
if (latch.await(5, TimeUnit.SECONDS))
System.out.println("released!");
else
System.out.println("timed out");
return secret;
} catch (InterruptedException e) {
// TODO Auto-generated catch block
System.out.println(e.getMessage());
e.printStackTrace();
}
return null;
}
public void countdown(String s) {
System.out.println("In countdown: "+s+ ". Latch count: "+latch.getCount());
secret = s;
latch.countDown();
System.out.println("Latch count: "+latch.getCount());
}
}
public class LogonHandler extends AbstractHandler {
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException
{
Map<String,String[]> query = request.getParameterMap();
if (query.containsKey("method")) {
if (query.get("method")[0].compareTo(method) == 0) {
baseRequest.setHandled(true);
response.getWriter().println(logon(query));
}
}
else
baseRequest.setHandled(false);
}
private String logon(Map<String,String[]> query) {
if (query.containsKey("username") && query.containsKey("password") && query.containsKey("sendercompid")) {
app.mapUser(query.get("sendercompid")[0], new UserSession(query.get("username")[0], query.get("password")[0]));
SessionID session = new SessionID(new BeginString("FIX.4.4"), new SenderCompID(query.get("sendercompid")[0]), new TargetCompID("PARFX"));
try {
ThreadedSocketInitiator tsi = new ThreadedSocketInitiator(app, app.getFileStoreFactory(), settings, app.getLogFactory(), app.getMessageFactory());
UserSession userSession = new UserSession(query.get("username")[0], query.get("password")[0]);
userSession.setInitiator(tsi);
tsi.start();
return userSession.await();
} catch (ConfigError e) {
// TODO Auto-generated catch block
e.printStackTrace();
return e.toString();
}
}
return "fail";
}
}
public class QuickfixjApplication implements Application {
private Map<String,UserSession> users = new HashMap<String,UserSession>();
public void mapUser(String s, UserSession u) {
users.put(s, u);
}
public void toAdmin(Message message, SessionID sessionId) {
try {
if (message.getHeader().getField(new StringField(MsgType.FIELD)).valueEquals(Logon.MSGTYPE)) {
UserSession user = users.get(sessionId.getSenderCompID());
message.setField(new Username(user.getUsername()));
message.setField(new Password(user.getPassword()));
}
} catch (FieldNotFound e) {
e.printStackTrace();
}
}
public void fromAdmin(Message message, SessionID sessionId)
throws FieldNotFound, IncorrectDataFormat, IncorrectTagValue, RejectLogon {
if (message.getHeader().getField(new StringField(MsgType.FIELD)).valueEquals(Logon.MSGTYPE)) {
System.out.println(message.toString());
UserSession user = users.get(sessionId.getSenderCompID());
user.countdown(message.toString());
}
}
}
Ok, I've tried to only include the minimum amount of code here. There are three interesting classes, UserSession is the internal glue between the Jetty handler and the QuickFix/j application.
The LogonHandler receives an HTTP logon request and tries to log a user onto a QuickFix/j application session.
QuickFix/j is sending a logon message to a FIX server, this logon request / response is asynchronous. The HTTP logon request is of course synchronous. So we have to wait for the reply from the FIX server before we return from the HTTP request. I do this using CountDownLatch and this UserSession object.
When I create the QuickFix/j session object I also create a UserSession object and add it to a map (that happens in the LogonHandler logon method).
There are two callbacks in the QuickFix/j application object, toAdmin() and fromAdmin(). In fromAdmin() I check if the message is a logon response and if it is I call a method of UserSession to countdown the latch. In debugging the code I see that the fromAdmin() method is hit, the UserSession object is found in the map and the countdown() method is called and the latch.getCount() goes from 1 to 0, but the latch.await() method in UserSession await() never returns. It always times out.
You could use CountDownLatch like this:
public class LogonHandler implements Handler {
private final CountDownLatch loginLatch = new CountDownLatch (1);
private boolean callbackResults;
public void serverResponseCallback(boolean result) {
callbackResults = result;
loginLatch.countDown ();
}
public boolean tryLogon(Credentials creds) throws InterruptedException {
SomeServer server = new SomeServer(address);
server.tryLogon (creds.getName (), creds.getPass ());
loginLatch.await ();
return callbackResults;
}
}
If you want to limit waiting time by, for example, 5 seconds, then instead of loginLatch.await () use the following:
if (loginLatch.await (5L, TimeUnit.SECONDS))
return callbackResults;
else
return false; // Timeout exceeded

write to File from Servlet

here is the piece of code that i wrote:
public class ServletCounter extends HttpServlet {
private final Object lock = new Object();
private int serviceCounter = 0;
private FileOutputStream out;
private boolean shuttingDown;
#Override
public void init(ServletConfig servletConfig) throws ServletException {
super.init(servletConfig);
}
#Override
protected void service(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws ServletException, IOException {
enteringServiceMethod();
try {
super.service(httpServletRequest, httpServletResponse);
out = new FileOutputStream("C:\\xampp\\tomcat\\webapps\\myapp\\WEB-INF\\lib\\counter.txt");
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
#Override
protected void doGet(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws ServletException, IOException {
if (!shuttingDown) {
writeToFile("number of servlet access = " + serviceCounter );
}
}
#Override
public void destroy() {
...
}
private void enteringServiceMethod() {
synchronized (lock) {
serviceCounter++;
writeToFile("method enteringServiceMethod serviceCounter = " + serviceCounter);
}
}
private int getNumServices() {
synchronized (lock) {
return serviceCounter;
}
}
private void writeToFile(String text) {
System.out.println(text);
text += "\r\n";
try {
out.write(text.getBytes());
} catch (IOException e) {
e.printStackTrace();
}
}
}
What i need is every time someone opens my Servlet, it should open "counter.txt" file and store a number of how many times the Servlet was opened. for example if the file hold number 8 then after someone accesses the servlet it should store number 9 and delete number 8. does it make sense? can anyone help me override writeToFile method. the code that i wrote is incomplete, but i'm stuck, tried several things and nothing seems to work.
If you are trying to count page hit, then Filter would be the nice approach
Intercept each request and take a synchronized variable in application scope and increment it

Application-to-Application communication through web sockets

I have some trouble getting application-to-application communication via web sockets (that is without a browser to work). Since this does not seem to be the most usual application of web sockets, I wonder if anybody has any experience doing this.
Why do I want to use web sockets?
Because of firewall issues I need to go through port 80/8080 (and I need to continue to handle some other HTTP communication, so I can't just use plain TCP/IP socket communication).
How did I try to make this work?
I'm using Jetty 8.0 both for the server and for the client. My server code:
public class WebSocketTestServlet extends WebSocketServlet {
public WebSocket doWebSocketConnect(HttpServletRequest arg0, String arg1) {
return new TestWebSocket();
}
class TestWebSocket implements WebSocket, WebSocket.OnTextMessage
{
public void onClose(int arg0, String arg1) {
}
public void onOpen(Connection arg0) {
}
public void onMessage(String messageText) {
}
}
}
My client code:
public class MyWebSocketClient{
MyWebSocketClient() throws IOException
{
WebSocketClientFactory factory = new WebSocketClientFactory();
try {
factory.start();
} catch (Exception e1) {
e1.printStackTrace();
}
WebSocketClient client = factory.newWebSocketClient();
WebSocket.Connection connection = client.open(new URI("ws://myserver:8080/testws"), new WebSocket.OnTextMessage()
{
public void onOpen(Connection connection)
{
}
public void onClose(int closeCode, String message)
{
}
public void onMessage(String data)
{
}
}).get(50, TimeUnit.SECONDS)
}
What problem do I see?
A ProtocolException
Caused by: java.net.ProtocolException: Bad response status 302 Found
at org.eclipse.jetty.websocket.WebSocketClientFactory$HandshakeConnection.closed(WebSocketClientFactory.java:423)
at org.eclipse.jetty.websocket.WebSocketClientFactory$WebSocketClientSelector.endPointClosed(WebSocketClientFactory.java:235)
at org.eclipse.jetty.io.nio.SelectorManager$SelectSet.destroyEndPoint(SelectorManager.java:948)
at org.eclipse.jetty.io.nio.SelectChannelEndPoint.doUpdateKey(SelectChannelEndPoint.java:523)
at org.eclipse.jetty.io.nio.SelectorManager$SelectSet.doSelect(SelectorManager.java:469)
at org.eclipse.jetty.io.nio.SelectorManager$1.run(SelectorManager.java:283)
at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:598)
at org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:533)
at java.lang.Thread.run(Thread.java:619)
Any idea why this is not working?
Try to add "/" at the end of your address in the client's code:
"ws://myserver:8080/testws/"
It just fixed the issue for me.
Trying to do something similar to allow WS calls to an embedded Jetty REST API...here's my echo test code (Jetty 7), HTH
public class myApiSocketServlet extends WebSocketServlet
{
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException ,IOException
{
OutputStream responseBody = response.getOutputStream();
responseBody.write("Socket API".getBytes());
responseBody.close();
}
public WebSocket doWebSocketConnect(HttpServletRequest request, String protocol)
{
return new APIWebSocket();
}
class APIWebSocket implements WebSocket, WebSocket.OnTextMessage
{
Connection connection;
#Override
public void onClose(int arg0, String arg1)
{
}
#Override
public void onOpen(Connection c)
{
connection = c;
}
#Override
public void onMessage(String msg)
{
try
{
this.connection.sendMessage("I received: " + msg);
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
}

Categories

Resources