I am designing a very simple chat application which uses the long polling to communicate between users. I am using a servlet to store the AsyncContexts in a Hashmap. When userA sends a message to userB the servlet gets the corresponding AsyncContext from the HashMap and pushes the data to the client.
The code that I am using is given below-:
protected void doGet(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
response.setContentType("application/json");
if ("getAll".equals(request.getParameter("op"))) {
String friend = request.getParameter("for");
String jsonMessages = getChatMessagesForUser((String)request.getSession()
.getAttribute("id"),friend);
PrintWriter writer = response.getWriter();
writer.println(jsonMessages);
writer.flush();
return;
}
else if ("getFrnd".equals(request.getParameter("op"))) {
String jsonFrndList = getFriends((String)request.getSession()
.getAttribute("id"));
PrintWriter writer = response.getWriter();
writer.println(jsonFrndList);
writer.flush();
return;
}
final AsyncContext asyncContext = request.startAsync(request, response);
asyncContext.setTimeout(30 * 60 * 1000);
asyncContext.addListener(new AsyncListener() {
#Override
public void onTimeout(AsyncEvent event) throws IOException {
AsyncContext ctx = event.getAsyncContext();
PrintWriter writer = ctx.getResponse().getWriter();
ChatMessage directive = new ChatMessage();
directive.setType('D');
directive.setMessage("Keep-Alive");
writer.println(gson.toJson(directive));
writer.flush();
contexts.remove(ctx);
ctx.complete();
}
#Override
public void onStartAsync(AsyncEvent event) throws IOException {
// TODO Auto-generated method stub
}
#Override
public void onError(AsyncEvent event) throws IOException {
AsyncContext ctx = event.getAsyncContext();
PrintWriter writer = ctx.getResponse().getWriter();
ChatMessage directive = new ChatMessage();
directive.setType('D');
directive.setMessage("Keep-Alive");
writer.println(gson.toJson(directive));
writer.flush();
contexts.remove(ctx);
ctx.complete();
}
#Override
public void onComplete(AsyncEvent event) throws IOException {
// TODO Auto-generated method stub
}
});
String id = (String) request.getSession().getAttribute("id");
if(!contexts.containsKey(id)) {
contexts.put(id, asyncContext);
}
}
protected void doPost(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
response.setContentType("application/json");
String receiver = request.getParameter("name");
String sender = (String) request.getSession().getAttribute("id");
String message = request.getParameter("message");
System.out.println(receiver + " " + message);
ChatMessage chatMessage = new ChatMessage();
chatMessage.setType('M');
chatMessage.setReceiver(receiver);
chatMessage.setSender(sender);
chatMessage.setMessage(message);
chatMessage.setImage(false);
if(contexts.containsKey(receiver)) {
AsyncContext recieverContext = (AsyncContext) contexts.remove(receiver);
System.out.println("Connections Open:" + contexts.size());
PrintWriter writer = recieverContext.getResponse().getWriter();
writer.println(gson.toJson(chatMessage));
System.out.println("Message transmitted:" +
gson.toJson(chatMessage));
writer.flush();
recieverContext.complete();
}
try {
System.out.println("Trying to add to database");
ChatDAO.addToChat(chatMessage);
} catch (SQLException e) {
System.err.println(e);
}
}
Let me explain what is happening-:
User A sends the initial GET request to the servlet.
The servlet starts an AsyncContext and adds it to the HashMap.
User B does the same thing.
User A sends a message to User B by posting to the servlet(doPost).
The servlet gets the AsyncContext of user B from the hashmap. Pushes the message from User A and call complete() on it.
User B immediately reconnects to the servlet by doing another GET (doGet).
The obvious problem of this is that when multiple people try to chat with one single person. For example-: If User B and User C try to chat with User A a race condition arises.
It may happen they both post a message to user A at the same time, depending on whose message is processed first the connection to user A will be closed. And hence if a chat message arrives before the recipient reconnects the message will be discarded.
What I need is that if the servlet is unable to find an async context for a particular user then it will wait for sometime and then after waiting if it does not still find it only then will it discard the message.
Can I write something like -:
synchronized(contexts) {
wait(100);
}
You might be better off separating the storage of messages from the async contexts so that the messages can be stored even if there is not a context for the recipient. The next time the recipient sends the get request it would find a message waiting and return it straight away.
If you are concerned about stale undelivered messages then you could have a single thread that checks periodically for messages over a certain age and returns them to the sender. It could also just destroy old return to sender messages
Related
I have a Java web application running under Apache Tomcat (8.0.21.0). Its function is to monitor various external processes and display alerts and periodic updated statuses in a browser. The main HTTP request handler is simple enough.
public class MyApplication extends HttpServlet
{
.
.
.
public void doPost (HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
{
processRequest (request, response);
}
public void doGet (HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
{
processRequest (request, response);
}
private static void processRequest (HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
{
String strOption = request.getParameter ("option");
int nOption = Integer.parseInt (strOption);
response.setContentType ("text/html");
PrintWriter out = response.getWriter ();
outputPage (out, nOption);
}
private void outputPage (PrintWriter out, int nOption)
{
out.println ("<!DOCTYPE html>");
out.println ("<html>");
out.println ("<head>");
switch (nOption)
{
// title, style and <body> content depend on option passed in request
.
.
.
}
out.println ("</body>);
out.println ("</html>");
}
}
However, the application also includes a TCP Listener and socket, to receive IoT (Internet of Things) messages:
public class MyTCPconnection extends Thread
{
public Socket clientSocket; // socket created by listener
private String url = "[local host address and servlet name]";
.
.
.
public void run ()
{
int RC = 400; // default value = "Bad Request"
try
{
// get bytes from remote process
int receiveBufferSize = clientSocket.getReceiveBufferSize ();
byte[] receiveBuffer = new byte[receiveBufferSize];
int bytesRead = TCPreceive (clientSocket, receiveBuffer); // not shown
if (bytesRead != -1 && bytesRead != 0)
{
String strOption = getOption (receiveBuffer); // not shown
}
HttpPost httpPost = new HttpPost (url);
httpPost.setHeader ("Accept", "text/html,application/xhtml+xml,application/xml");
httpPost.setHeader ("Content-Type", "application/x-www-form-urlencoded");
List<NameValuePair> requestParams = new ArrayList<NameValuePair>();
reqestParams.add (new BasicNameValuePair ("option", value));
CloseableHttpClient httpClient = HttpClients.createDefault ();
CloseableHttpResponse response = httpClient.execute (httpPost);
RC = response.getStatusLine().getStatusCode();
String responseBody = EntityUtils.toString (response.getEntity ());
system.out.println (responseBody);
}
catch (UnknownHostException e)
{
RC = 404;
}
catch (IOException e)
{
RC = 400;
}
TCPsend (clientSocket, RC); // reply to remote process, not shown
}
}
Take for granted that MyTCPconnection.run () generates a valid HTTP request body and submits a POST request to the main application. The problem I have encountered is that, where the POST is made from a web browser (IExplorer, Firefox etc), the application outputs a web page in the browser, but on receiving the POST request from the internal MyTCPconnection instance, it outputs nothing to any browser. Instead, it redirects the entire output to the responseBody.
I thought at first that I merely needed to save the HttpServletResponse and PrintWriter variables from a request from the browser, and pass the saved PrintWriter instance to the function outputPage. However, when I logged these, the results were:
Request from browser:
HttpServletResponse response = org.apache.catalina.connector.ResponseFacade#3e1d266b
PrintWriter out = org.apache.catalina.connector.CoyoteWriter#6bc55aa8
Request from MyTCPconnection.run ():
HttpServletResponse response = org.apache.catalina.connector.ResponseFacade#3e1d266b
PrintWriter out = org.apache.catalina.connector.CoyoteWriter#6bc55aa8
Any hints or hlp would be appreciated
Need help in saving request and response of a REST API in below way,
Session ID:
Request:
{
{
request header
}
{
request body
}
}
Response:
{
{
response header
}
{
response body
}
}
This shouldn't depend on the logging level or any other logging related concepts.
Checked many similar questions but no answers for them,
Can any one help me in this please, thank you.
Spring Boot - How to log all requests and responses with exceptions in single place?
You could use the HandlerInterceptorAdapter, and write the informations you need on your file :
Spring provides a mechanism for configuring user-defined interceptors
to perform actions before and after web requests.
Among the Spring request interceptors, one of the noteworthy
interfaces is HandlerInterceptor, which can be used to log the
incoming request by implementing the following methods:
preHandle() – this method is executed before the actual controller
service method afterCompletion() – this method is executed after the
controller is ready to send the response Furthermore, Spring provides
the default implementation of HandlerInterceptor interface in the form
of HandlerInterceptorAdaptor class which can be extended by the user.
Let’s create our own interceptor – by extending
HandlerInterceptorAdaptor as:
#Component public class TaxiFareRequestInterceptor extends
HandlerInterceptorAdapter {
#Override
public boolean preHandle(
HttpServletRequest request,
HttpServletResponse response,
Object handler) {
return true;
}
#Override
public void afterCompletion(
HttpServletRequest request,
HttpServletResponse response,
Object handler,
Exception ex) {
//
} }
http://www.baeldung.com/spring-http-logging
http://www.baeldung.com/spring-mvc-handlerinterceptor
I found answer from Gist https://gist.github.com/int128/e47217bebdb4c402b2ffa7cc199307ba
Logging both request and response. Made some minor changes based on my requirement to write into a file instead of logging using java 7 feature.
Path path = Paths.get("home/midoriya/sample.txt");
String strValue = "Whatever the values want to write in file";
Path path = Paths.get(fileName);
byte[] bytes = strValue.getBytes();
Files.write(path, bytes);
or
FileWriter fw = null;
BufferedWriter writer = null;
// File logFile = null;
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
try {
LocalDate localDate = LocalDate.now();
File logFile = new File("/home/ramesh/logReqRes"+localDate.getDayOfMonth()+localDate.getMonth()+".txt");
boolean flag = logFile.createNewFile();
System.out.println("flag :" + flag);
if( flag || logFile.length() >= (1024*1024*1024))
fw = new FileWriter(logFile, false);
else
fw = new FileWriter(logFile, true);
writer = new BufferedWriter(fw);
if (isAsyncDispatch(request)) {
filterChain.doFilter(request, response);
} else {
doFilterWrapped(wrapRequest(request), wrapResponse(response), filterChain);
}
} catch (IOException io) {
io.printStackTrace();
}
catch (Exception ex) {
ex.printStackTrace();
}
finally {
try {
if (writer != null)
writer.close();
if (fw != null)
fw.close();
} catch (IOException ex) {
ex.printStackTrace();
}
}
In a Java HttpServlet, is it possible to request data from another local service using the original request's header information without necessarily forwarding?
For example, I have FooBar.java:
// Handles the url at /foo/bar and can be accessed at http://localhost/foo/bar
public class FooBar extends HttpServlet
{
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
Object data = ... // 1. Retrieve data at http://localhost/foo/baz utilizing the current request's header
Object newData = doSomething(data); // 2. Process the data
response.getWriter().write(newData.toString); // 3. Return the processed data
}
private Object doSomething(Object data)
{
// Perform some business logic
}
}
Step 1 is the issue here. The purpose of this is that I want to be able to perform some sort of logic on the data before returning it in full, but don't necessarily have access do make the changes on the handler at /foo/baz do to the propriety nature of things.
You can use this answer of me to create a HTTP Request: send get request
In addition, it may be necessary to copy the request header with some care:
private static final Set forbiddenCopyHeaders = new HashSet<>(Arrays.asList(new String[]{
"connection"
, "transfer-encoding"
, "content-length" // POST kann zu Status 500 führen, wenn die content-length kopiert wird
, "via"
, "x-forwarded-for"
, "x-forwarded-host"
, "x-forwarded-server"
}));
private void copyRequestHeaders(HttpServletRequest customerRequest, HttpRequestBase internRequest) throws
HttpException
{
Enumeration<String> headerNames = customerRequest.getHeaderNames();
String connectionHeader = customerRequest.getHeader("connection");
while (headerNames.hasMoreElements())
{
String headerName = headerNames.nextElement();
boolean copyAllowed = !forbiddenCopyHeaders.contains(headerName.toLowerCase()) &&
!StringUtils.containsIgnoreCase(connectionHeader, headerName);
if (copyAllowed)
{
Enumeration<String> values = customerRequest.getHeaders(headerName);
while (values.hasMoreElements())
{
internRequest.addHeader(headerName, values.nextElement());
}
}
}
setProxySpecificRequestHeaders(customerRequest, internRequest);
}
private void setProxySpecificRequestHeaders(HttpServletRequest customerRequest,
HttpRequestBase internRequest) throws HttpException
{
String serverHostName = "doorman";
try
{
serverHostName = InetAddress.getLocalHost().getHostName();
}
catch (UnknownHostException e)
{
logger.error("Couldn't get the hostname needed for headers x-forwarded-server and Via", e);
}
String originalVia = customerRequest.getHeader("via");
StringBuilder via = new StringBuilder("");
if (originalVia != null)
{
if (originalVia.contains(serverHostName))
{
logger.error("This proxy has already handled the Request, will abort.");
throw new HttpException("Request has a cyclic dependency on this proxy.");
}
else
{
via.append(originalVia).append(", ");
}
}
via.append(customerRequest.getProtocol()).append(" ").append(serverHostName);
internRequest.addHeader("via", via.toString());
internRequest.addHeader("x-forwarded-for", customerRequest.getRemoteAddr());
internRequest.addHeader("x-forwarded-host", customerRequest.getServerName());
internRequest.addHeader("x-forwarded-server", serverHostName);
internRequest.addHeader("accept-encoding", "");
}
Using HttpURLConnection and altering the header to include a property from the original request, I was able to get a BufferedReader from the HTTP request:
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
// Step 1
String serverName = request.getLocalName();
String contextPath = request.getContextPath();
URL url = new URL("https://" + serverName + contextPath + "/baz");
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestProperty("Key Header", request.getHeader("Key Header"));
BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
// Step 2
... // Do something with the data from the reader
// Step 3
... // Write the data back using the response
}
I have problem when I submit my form to insert data
the URL can't change and when I refresh it, the data reinsert
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
String _1 = request.getParameter("company_name").toString();
String _2 = request.getParameter("city").toString();
String _3 = request.getParameter("state").toString();
String _4 = request.getParameter("zipcode").toString();
String _5 = request.getParameter("branch").toString();
String _6 = request.getParameter("address").toString();
Database db = (Database) getServletContext().getAttribute("db");
try {
String sql = "insert into company(company_name,city,state,zipcode,branch,company_address) values('"+_1+"','"+_2+"','"+_3+"','"+_4+"','"+_5+"','"+_6+"')";
db.updateSql(sql);
} catch (Exception e2) {
System.out.println(e2);
}
getServletContext().getRequestDispatcher("/company.jsp").forward(request, response);
}
Your problem comes from the understanding of the forward method.
This method transfer the request and the response object to the new URL. It is invisible for the client's browser so the URL is unchanged. By reloading the page, you repeat your resquest so your sending again your data.
This behaviour is completely normal. If you want to redirect to another URL and have another request then you should use the sendRedirect method.
Refer to this post to have a complete description of both methods.
I have a servlet where I need to declare a session which can be acceptable form doGet and doPost both how I should do this?
I have done
#WebServlet(name = "LoginLogout", urlPatterns = {"/LoginLogout.do"})public class LoginLogout extends HttpServlet {//For Session
HttpSession session = request.getSession(true);
protected void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
}
#Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String status = request.getParameter("status");
System.out.println(status);
}
#Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
try {
String loginId = request.getParameter("login_id");
String password = request.getParameter("password");
System.out.println(loginId);
//Inserting value to the Pogo named "newLoginPogo"
loginData newLoginPogo = new loginData();
newLoginPogo.setLoginId(loginId);
newLoginPogo.setPassword(password);
//Creating a obj of ModelLogin to send the loginId and Password via a method which is in ModelLogin class
ModelLogin loginBis = new ModelLogin();
loginData userData = loginBis.checkUser(newLoginPogo);
String userExist = userData.getUserExist();
System.out.println(userExist);
if ("yes".equals(userExist)) {
System.out.println("In while loop of Servlet");
String firstName = userData.getFirstName();
String userId = userData.getUserId();
boolean IsSu = userData.getIsSu();
//conveting boolean to string
String superuser = new Boolean(IsSu).toString();
//Creating a session
session.setAttribute("firstName", firstName);
session.setAttribute(userId, "userId");
session.setAttribute(superuser, "IsSu");
//==============================================================================================================
//If user does exist show the Success Message and forward Dashboard
//==============================================================================================================
//Session for success message
String succmsg = "Login Successful";
session.setAttribute("succmsg", succmsg);
getServletConfig().getServletContext().getRequestDispatcher("/WEB-INF/ViewPages/dashboard/dashboard.jsp").forward(request, response);
} //==============================================================================================================
//If user does not exist show the Error Message
//==============================================================================================================
else if ("no".equals(userExist)) {
//Session for success message
System.out.println("inside NO");
String emsg = "Login Error";
session.setAttribute("errmsg", emsg);
getServletConfig().getServletContext().getRequestDispatcher("/index.jsp").forward(request, response);
} else {
}
/*
//===============================================================================================================
//code for Logout
//===============================================================================================================
String status = request.getParameter("status");
if ("logout".equals(status)) {
//clearing the session
session.invalidate();
//forwarding to index page
getServletConfig().getServletContext().getRequestDispatcher("/index.jsp").forward(request, response);
}
*/
} finally {
}
}}
But it says
Can Not find Symbol
in this line HttpSession session = request.getSession(true);
You don't need to have session variable in servlet as field. In general - this is kind of common mistake. There will be only one onstance of servlet serving lots of requests, and unless you declare it as single-threaded - the requests would be handled concurrently.
HttpSession will be pre-exist for you in doGet and doPost via request object. Servlet container will guarantee this. So simply obtain reference to the session in doGet/doPost and do whatever you want.
What you desire is one of the roles of HTTP session.
You can look at it as a conversation between the client and the server.
As long as the "conversation" (HTTP session) is open and alive, you can set variables on the HTTP session, and access them from different requests that will sent on the same session.
Look at this as some sort of "shared memory" that exists during the "conversation time".
You can find many examples on how to do that over the internet.
Here is an example for session tracking.