Servlet for receive and share streaming data - java

I have three programs (a-b-c): a starts b that handles c, when c ends a restarts b. I can only change b, but it is unstable because it is restarted. I was thinking of solving my problems with a servlet service that receives b data and errors and displays them on a web page immediately. I tried two ways: 1. Receive data in a Thread parallel to the servlet and share it with it (I did not succeed); 2. Receive data from a client and share it with ServletContext and refresh the page with sent event but skip lines, even with retry: 100, and repeat the last one when there are no new data. Is there a way to send data to all servlet clients only when they are new? Is there a better solution considering that b is restarted and that I have to limit the ports used?
--UPDATE--
Servlet.java
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 java.io.*;
#WebServlet(name = "Servlet", urlPatterns = {"/Servlet", "/test"})
public class Servlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setHeader("Connection", "keep-alive");
response.setContentType("text/event-stream");
response.setCharacterEncoding("UTF-8");
response.setHeader("Cache-Control", "no-cache");
String datain = request.getParameter("StringIn");
String dataold = request.getParameter("StringOld");
PrintWriter writer = response.getWriter();
if (datain == null) {
boolean test = getServletContext().getAttribute("actualData").equals(request.getParameter("StringOld"));
if (!test) {
writer.write("data: " + getServletContext().getAttribute("actualData") +"\n\n");
writer.write("retry: 500");
}
else {
writer.write("data: \n\n");
writer.write("retry: 1000");
}
}
else {
getServletContext().setAttribute("actualData", datain);
writer.write("data receive!\n\n");
}
writer.flush();
writer.close();
}
}
Script index.jsp
var eventSource = null;
var oldmessage = "";
var path = "http://localhost:8080/Servlet?StringaOld=";
function start() {
eventSource = new EventSource(path + oldmessage);
eventSource.onopen = function () {
};
eventSource.onmessage = function (message) {
var mex = message.data;
message.data = null;
if (mex != null) {
oldmex = mex;
testo.value += mex + "\n";
}
};
eventSource.onerror = function (error) {
eventSource = new EventSource(path + encodeURI(oldmessage));
};
Start.disabled = true;
}
function stop() {
eventSource.close();
Start.disabled = false;
}

Related

Servlet failed to addCookie

I'm a new bird learning Servlet. When I use cookie in Servlet, I found cookie can't be added after visiting web page.
Here's my code:
import jakarta.servlet.ServletException;
import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.Date;
public class LastAccessServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
#Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html;charset=utf-8");
String lastAccessTime = null;
Cookie[] cookies = req.getCookies();
PrintWriter writer = resp.getWriter();
for (int i = 0; cookies != null && i < cookies.length; ++i){
if ("lastAccess".equals(cookies[i].getName())) {
lastAccessTime = cookies[i].getValue();
break;
}
}
if (lastAccessTime == null){
writer.println("Your first visit.");
} else {
writer.println("Last time" + lastAccessTime);
}
String currentTime = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(new Date());
Cookie cookie = new Cookie("lastAccess", currentTime);
cookie.setMaxAge(999999999);
cookie.setPath("/");
resp.addCookie(cookie);
}
#Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doGet(req, resp);
}
}
Here's my WebPage:
No matter how many times I refresh, reload or change browsers, the result is the same.
I check the cookies in my edge browser.
The cookie hasn't the value name lastAccess I added, so I think the problem is in resp.addCookie(cookie);.But don't know how to solve it.
Your problem is not actually with the code/logic but data here. You are trying to set a cookie value which is more like a Date string looking like 2022-10-01 03:01:22.
The cookie value here is illegal. As per the RFC6265, you are not allowed to have some of the special characters in the cookie. A more detailed description is available with this answer
You can solve it by changing may be the way you form the cookie value. For eg use the timestamp instead of date string. Like below.
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html;charset=utf-8");
String lastAccessTime = null;
Cookie[] cookies = req.getCookies();
PrintWriter writer = resp.getWriter();
for (int i = 0; cookies != null && i < cookies.length; ++i){
if ("lastAccess".equals(cookies[i].getName())) {
lastAccessTime = cookies[i].getValue();
break;
}
}
if (lastAccessTime == null){
writer.println("Your first visit.");
} else {
writer.println("Last time" + new Date(Long.parseLong(lastAccessTime)));
}
String currentTime = String.valueOf( new Date().getTime());
Cookie cookie = new Cookie("lastAccess", currentTime);
cookie.setMaxAge(999999999);
cookie.setPath("/");
resp.addCookie(cookie);
}

How to verify if a servlet processing is really doing non-blocking io?

I am trying to receive an http request using non-blocking io, then make another http request to another server using non-blocking io, and return some response, here is the code for my servlet:
package learn;
import java.io.IOException;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import javax.servlet.AsyncContext;
import javax.servlet.ReadListener;
import javax.servlet.ServletException;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletOutputStream;
import javax.servlet.WriteListener;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.InvocationCallback;
import javax.ws.rs.core.Response;
import org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder;
#WebServlet(urlPatterns = {"/async"}, asyncSupported = true)
public class AsyncProcessing extends HttpServlet {
private static final long serialVersionUID = -535924906221872329L;
public CompletableFuture<String> readRequestAsync(final HttpServletRequest req) {
final CompletableFuture<String> request = new CompletableFuture<>();
final StringBuilder httpRequestData = new StringBuilder();
try (ServletInputStream inputStream = req.getInputStream()){
inputStream.setReadListener(new ReadListener() {
final int BUFFER_SIZE = 4*1024;
final byte buffer[] = new byte[BUFFER_SIZE];
#Override
public void onError(Throwable t) {
request.completeExceptionally(t);
}
#Override
public void onDataAvailable() {
if(inputStream.isFinished()) return;
System.out.println("----------------------------------------");
System.out.println("onDataAvailable: " + Thread.currentThread().getName());
try {
while(inputStream.isReady()) {
int length = inputStream.read(buffer);
httpRequestData.append(new String(buffer, 0, length));
}
} catch (IOException ex) {
request.completeExceptionally(ex);
}
}
#Override
public void onAllDataRead() throws IOException {
try {
request.complete(httpRequestData.toString());
}
catch(Exception e) {
request.completeExceptionally(e);
}
}
});
} catch (IOException e) {
request.completeExceptionally(e);
}
return request;
}
private Client createAsyncHttpClient() {
ResteasyClientBuilder restEasyClientBuilder = (ResteasyClientBuilder)ClientBuilder.newBuilder();
return restEasyClientBuilder.useAsyncHttpEngine().connectTimeout(640, TimeUnit.SECONDS).build();
}
public CompletableFuture<Response> process(String httpRequest){
System.out.println("----------------------------------------");
System.out.println("process: " + Thread.currentThread());
CompletableFuture<Response> futureResponse = new CompletableFuture<>();
Client client = createAsyncHttpClient();
client.target("http://localhost:3000").request().async().get(new InvocationCallback<Response>() {
#Override
public void completed(Response response) {
System.out.println("----------------------------------------");
System.out.println("completed: " + Thread.currentThread());
futureResponse.complete(response);
}
#Override
public void failed(Throwable throwable) {
System.out.println(throwable);
futureResponse.completeExceptionally(throwable);
}
});
return futureResponse;
}
public CompletableFuture<Integer> outputResponseAsync(Response httpResponseData, HttpServletResponse resp){
System.out.println("----------------------------------------");
System.out.println("outputResponseAsync: " + Thread.currentThread().getName());
CompletableFuture<Integer> total = new CompletableFuture<>();
try (ServletOutputStream outputStream = resp.getOutputStream()){
outputStream.setWriteListener(new WriteListener() {
#Override
public void onWritePossible() throws IOException {
System.out.println("----------------------------------------");
System.out.println("onWritePossible: " + Thread.currentThread().getName());
outputStream.print(httpResponseData.getStatus());
total.complete(httpResponseData.getLength());
}
#Override
public void onError(Throwable t) {
System.out.println(t);
total.completeExceptionally(t);
}
});
} catch (IOException e) {
System.out.println(e);
total.completeExceptionally(e);
}
return total;
}
#Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("----------------------------------------");
System.out.println("doGet: " + Thread.currentThread().getName());
final AsyncContext asyncContext = req.startAsync();
readRequestAsync(req)
.thenCompose(this::process)
.thenCompose(httpResponseData -> outputResponseAsync(httpResponseData, resp))
.thenAccept(a -> asyncContext.complete());
}
}
The server at http://localhost:3000 is an http server written in node which just returns a response after 27 seconds, i would like to make a request to the node server and while this request is being processed i would want to make another http request to the servlet to see if the same thread is being used. Currently i'm trying to use payara 5.194 to do this but even if i set the two thread pools to have one thread the app server seems to create another threads. So, i would like to know from your knowledge if this servlet is really doing non-blocking io and not blocking at any time, also it would be amazing if i could do some experiment to ensure this. I think it's important to point out that the class ServletInputStream is a subclass of InputStream, so i really don't know if this is non-blocking io. Thank you.

cookies giving unusual values along with set values in servlet

I have set cookies in a servlet class and read those cookies values in another servlet class. In another servlet class along with the set cookies values, I am getting some unusual values.
My Home.java servlet class results set ::
first result
Hello JSESSIONID, Hello A502A7144AE035ED9B1A2549F5C7B74B
Hello first_name, Hello RACHEL
Hello last_name, Hello KIM
second result
Hello JSESSIONID, Hello A502A7144AE035ED9B1A2549F5C7B74B
Hello first_name, Hello CAIRO
Hello last_name, Hello SENGAL
in both the results I am getting the set cookies values and names but along with them I am getting JSESSIONID and A502A7144AE035ED9B1A2549F5C7B74B. I can't understand from where do these cookies values are appearing? How can I remove this? Why are these values appearing?
My code :
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Enumeration;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* Servlet implementation class Authenticate
*/
#WebServlet("/Authenticate")
public class Authenticate extends HttpServlet {
private static final long serialVersionUID = 1L;
public Authenticate() {
super();
// TODO Auto-generated constructor stub
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
response.getWriter().append("Served at: ").append(request.getContextPath());
}
#Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
try
{
// Set response content type
response.setContentType("text/html");
PrintWriter out = response.getWriter();
String firstname = request.getParameter("firstname");
String lastname = request.getParameter("lastname");
out.print("Welcome "+ firstname);
// Create cookies for first and last names.
Cookie f_name = new Cookie("first_name", firstname);
Cookie l_name = new Cookie("last_name", lastname);
// Add both the cookies in the response header.
response.addCookie( f_name );
response.addCookie( l_name );
//creating submit button
out.print("<form action='Home' method='post' >");
out.print("<input type='submit' value='cookie click' />");
out.print("</form>");
out.close();
}
catch(Exception ex)
{
System.out.println("exception occured");
System.out.println(ex.toString());
}
}
}
Code for Home servlet
#WebServlet("/Home")
public class Home extends HttpServlet {
private static final long serialVersionUID = 1L;
/**
* #see HttpServlet#HttpServlet()
*/
public Home() {
super();
// TODO Auto-generated constructor stub
}
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
}
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
try {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
Cookie ck[] = request.getCookies();
if (ck != null) {
for (int i = 0; i < ck.length; i++) {
out.print("Hello " + ck[i].getName() + ", ");
out.print("Hello " + ck[i].getValue());
out.print("<br />");
}
}
out.close();
} catch (Exception e) {
System.out.println(e);
}
}
}

No response from HttpServlet with AsyncContext

I'm trying to implement async servlet on Tomcat, that will send update to client every time an HttpSessionAttributeListener.attributeReplaced() is triggered. Client side is configured to receive Server Sent Events.
Although the listener receives the updates, the browser does not receive any response. Browser's developer pane shows, that the request is pending and it ends with error 500 after the timeout set by the AsyncContext.setTimeout(). I run out of ideas, why this is happening.
JS
var source = new EventSource('/acount/sse');
source.onmessage = function (event) {
console.log(event.data);
document.querySelector('#messageArea p').innerHTML += event.data;
};
And this is my servlet code:
Servlet
public class SSE extends HttpServlet implements HttpSessionAttributeListener {
public static final String ATTR_ENTRY_PROCESSOR_PROGRESS = "entryProcessorProgress";
private AsyncContext aCtx;
#Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setAttribute("org.apache.catalina.ASYNC_SUPPORTED", true);
resp.setContentType("text/event-stream");
resp.setHeader("Cache-Control", "no-cache");
resp.setHeader("Connection", "keep-alive");
resp.setCharacterEncoding("UTF-8");
aCtx = req.startAsync(req, resp);
aCtx.setTimeout(80000);
}
#Override
public void attributeAdded(HttpSessionBindingEvent httpSessionBindingEvent) {
write(httpSessionBindingEvent);
}
#Override
public void attributeRemoved(HttpSessionBindingEvent httpSessionBindingEvent) {
}
#Override
public void attributeReplaced(HttpSessionBindingEvent httpSessionBindingEvent) {
write(httpSessionBindingEvent);
}
private void write(HttpSessionBindingEvent httpSessionBindingEvent) {
if (httpSessionBindingEvent.getName().equals(ATTR_ENTRY_PROCESSOR_PROGRESS)) {
try {
String message = "data: " + httpSessionBindingEvent.getValue() + "\n\n";
aCtx.getResponse().getWriter().write(message);
aCtx.getResponse().getWriter().flush();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
The problem:
I tried your code and there was a java.lang.NullPointerException in:
aCtx.getResponse().getWriter().write(message);
Because the aCtx is null.
You have mixed the Servlet and Listener, but when the Listeners methods are called, the AsyncContext initialization is not. Therefore nothings go to the browser.
I splitted your code in Servlet an Listener and smuggled the AsychContext object through a session attribute. So it was accessible in the listener.
And it works.
The Complete Code:
The HTML:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Server Event Listener</title>
</head>
<body>
<div id="messageArea">
<p></p>
</div>
<script>
var source = new EventSource('/yourPath/ServerSentEvent');
source.onmessage = function (event) {
console.log(event.data);
document.querySelector('#messageArea p').innerHTML += event.data;
};
</script>
</body>
</html>
The Servlet:
package testingThings.ServerSentEvent;
import java.io.IOException;
import javax.servlet.AsyncContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
#WebServlet(asyncSupported = true, value = {"/ServerSentEvent"})
public class ServerSentEvent extends HttpServlet {
private static final long serialVersionUID = 1L;
#Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// add #WebServlet(asyncSupported = true) instead
// http://stackoverflow.com/questions/7855712/how-to-avoid-request-set-async-supported-true-to-enable-async-servlet-3-0-proces
// req.setAttribute("org.apache.catalina.ASYNC_SUPPORTED", true);
resp.setContentType("text/event-stream");
resp.setHeader("Cache-Control", "no-cache");
resp.setHeader("Connection", "keep-alive");
resp.setCharacterEncoding("UTF-8");
AsyncContext aCtx = req.startAsync(req, resp);
aCtx.setTimeout(80000);
// add a asyncContext a session Attribute
req.getSession().setAttribute("asyncContext", aCtx);
//start logging in listener
req.getSession().setAttribute("entryProcessorProgress", "trigger output");
}
}
The HttpSessionAttributeListener:
package testingThings.ServerSentEvent;
import java.io.IOException;
import javax.servlet.AsyncContext;
import javax.servlet.annotation.WebListener;
import javax.servlet.http.HttpSessionAttributeListener;
import javax.servlet.http.HttpSessionBindingEvent;
#WebListener
public class SessionAttributeListener implements HttpSessionAttributeListener {
public static final String ATTR_ENTRY_PROCESSOR_PROGRESS = "entryProcessorProgress";
#Override
public void attributeAdded(HttpSessionBindingEvent httpSessionBindingEvent) {
write(httpSessionBindingEvent);
}
#Override
public void attributeRemoved(HttpSessionBindingEvent httpSessionBindingEvent) {
}
#Override
public void attributeReplaced(HttpSessionBindingEvent httpSessionBindingEvent) {
write(httpSessionBindingEvent);
}
private void write(HttpSessionBindingEvent httpSessionBindingEvent) {
if (httpSessionBindingEvent.getName().equals(ATTR_ENTRY_PROCESSOR_PROGRESS)) {
try {
// get the AsyncContext from the session
AsyncContext aCtx = (AsyncContext) httpSessionBindingEvent.getSession().getAttribute("asyncContext");
String message = "data: " + httpSessionBindingEvent.getValue() + "<br>\n\n";
aCtx.getResponse().getWriter().write(message);
aCtx.getResponse().getWriter().flush();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}

Health check servlet doesn't establish connection?

I'm trying to write a bare-bones health check servlet for my app, just to see if my Tomcat server is running. Essentially, it's a servlet with a "/health" endpoint that is an empty page. Below is my code:
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
public class HealthCheckServlet extends HttpServlet
{
private static final String SUCCESS = "Success";
private static final String FAILURE = "Failure";
#Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
isConnectionOK(request);
}
private static String isConnectionOK(HttpServletRequest request) throws IOException
{
String status = "";
try
{
URL connectionURL = new URL(request.getRequestURL().toString() + "/health");
HttpURLConnection connection = (HttpURLConnection) connectionURL.openConnection();
connection.setRequestMethod("GET");
connection.connect();
int httpResponseCode = connection.getResponseCode();
if (httpResponseCode == 200)
{
status = SUCCESS;
}
}
catch (IOException e)
{
status = FAILURE;
}
return status;
}
}
When I navigate to localhost:8080/health and debug it, it reaches my breakpoint at URL connectionURL = new URL(request.getRequestURL().toString() + "/health"); but won't progress to any subsequent breakpoints. The page itself returns a status code of 200, so I'm just trying to get the code to pick up on that.
Am I doing something wrong with trying to establish this connection within a servlet, or is there another problem here?

Categories

Resources