Push Notification in Web Server - java

I just have a quick question.
Is it possible to send a push notification upon receiving a request in the web server?
For example, user A sends a message to user B
user A's request goes to the web server (Tomcat in this case)
Upon receiving request from user A, the server sends request to APNs to send notification to user B (user B's token has been retrieved earlier).
I am trying to use jdk9.incubator.http to send a push notification to the user B's device in doPost() method when user A sends a request to the web server.
My class extends HttpServlet
public class AuthenticationController extends HttpServlet
Trying to send push notification upon authenticating the user
#SuppressWarnings("exports")
public void doPost(HttpServletRequest req, HttpServletResponse resp) {
try {
LOGGER.debug("POST requested from a client");
Authenticate(req, resp);
sendNotification();
} catch (Exception e) {
LOGGER.error("Oops",e);
}
}
Please give me advice to do it correctly.
EDIT:
I learned that the socket closes after the response is flushed in HttpServlet, so I tried to send another request to send a notification before flushing the response.
private void sendResponse(
HttpServletResponse resp, Gson gson ,Object obj) throws IOException, NoSuchAlgorithmException {
sendTestRequest()
resp.getOutputStream().print(gson.toJson(obj));
resp.getOutputStream().flush();
}//sendResponse()
I tested out JDK9.incubator in the main thread of another project that I worked on, which worked beautifully; however, I cannot somehow have it work in my tomcat server environment.
When I tried to send a simple request to just test out jdk9.incubator.request, nothing happens. sendTestRequest() method simply sends out a http request. Example can be shown here.
Below code snippet is an example of what my http/2 request looks like
final HttpRequest request = setHttpRequest(notification);
NotificationResponse resp = new NotificationResponse(null);
try {
HttpResponse<String> response =
getHttpClient().send(request, BodyHandler.asString());
resp.setResult(response.statusCode(), response.body());
if (resp.isAccepted()) {
System.out.println("Push Notification was accepted by APNs");
} else {
System.out.println("Request rejected reason: " + resp.getFailureReason());
}
} catch (IOException | InterruptedException e) {
System.out.println("Exception was thrown; check the log");
LOGGER.fatal("Failed to send request synchronously", e);
}

Related

Does a HttpServlet have to respond to a request?

I have several servlets that do things server side. On a few I just encode some unnecessary data and send it back, which seems pointless. Do you have to respond ? What happens when you just say return ? I've done that before and nothing seems to go wrong but I am relatively new to servlets. Are there consequences for simply returning that go above my head ? And what exactly happens when you return;
if(request.getParameter("name").equals("saveusedcards")) {
String sessId = request.getSession().getId();
//encode request with confirmation that cards were successfully updated
if(usersUpdatedCards.get(sessId).isEmpty()){
//no cards were seen
}
boolean success = DataDAO.updateCards(usersUpdatedCards.get(sessId));
if(success){
System.out.println("Data base update successfull!");
String responseMessage = new Gson().toJson("card successfully udpated");
response.setContentType("application/json");
response.setCharacterEncoding("UTF-8");
System.out.println("updated cards response message: "+responseMessage);
response.getWriter().write(responseMessage);
return;
} else {
System.out.println("Data base update failed...");
String responseMessage = new Gson().toJson("card was not successfully updated");
response.setContentType("application/json");
response.setCharacterEncoding("UTF-8");
System.out.println("updated cards response message: "+responseMessage);
response.getWriter().write(responseMessage);
return;
}
}
The servlet must produce an HTTP response for the client, however it is perfectly acceptable to return no content in the response body. When doing so your servlet should make this clear to the client by sending a response code of 204 (no content). Reference: https://httpstatuses.com/204
Here is an example of how you would set the response code from the doGet method. You could do the same from doPost or service methods.
#Override
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException {
// Do whatever work you need to do here...
res.setStatus(HttpServletResponse. SC_NO_CONTENT); // This returns a 204
}

How to run a method after sending a servlet response while also accepting further requests?

The scenario I am trying to complete is the following:
The client submits a HTTP POST request to the servlet.
The servlet sends a response to the client confirming the request has been received.
The servlet then sends an email notification to the system administrator.
So far I am able to complete the following steps in the order described above, however I run into an issue at the end. If the client makes another HTTP request to the same or another servlet while the email notification method EmailUtility.sendNotificationEmail() is still running, the servlet will not run any further code until this email method is completed (I am using javax.mail to send emails if that matters).
I have tried to use AsyncContext to get around this (which I may be using incorrectly), but unfortunately the issue still remains.
How can I make the EmailUtility.sendNotificationEmail() run in a different thread/asynchronously so that the servlets do not have to wait for this method to complete?
This is my code so far:
//Step 1: Client submits POST request to servlet.
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
request.setAttribute("org.apache.catalina.ASYNC_SUPPORTED", true);
//Step 2: Servlet sends response to client.
response.getWriter().write("Your request has been received");
response.getOutputStream().flush();
response.getOutputStream().close();
//Step 3: Servlet send email notification.
final AsyncContext acontext = request.startAsync();
acontext.start(new Runnable() {
public void run() {
EmailUtility.sendNotificationEmail();
acontext.complete();
}
});
}
Try something simple, like a thread:
new Thread(new Runnable()
{
#Override
public void run()
{
EmailUtility.sendNotificationEmail();
}
}, "Send E-mail").start();
So I resolved the issue by using ExecutorService as follows:
ExecutorService executorService = Executors.newFixedThreadPool(10);
executorService.execute(new Runnable() {
public void run() {
EmailUtility.sendNotificationEmail();
}
});
executorService.shutdown();

404 Response from server without request reaching the "doGet"-method

In my Netbeans java web application I have an ajax GET request to the controller.
suddenly, the server started sending 404 Response Messages to every Ajax Request.
What's even weirder is this:
In the controller, the following code is supposed to handle GET requests:
#Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
try {
processRequest1(request, response);
} catch (JsonException ex) {
Logger.getLogger(controller.class.getName()).log(Level.SEVERE, null, ex);
}
}
So I set a breakpoint next to this "doGet"method. Then I initiated the GET-request. (Purpose: being able to step through the code step by step in debugging mode).
The thing is: The ajax-request doesn't even arrive at this method. This also means that the server doesn't actually send a response.
However, google chrome is telling me that there was a 404 response:
What the hell is going on here ??!!
UPDATE:
Here's how I mapped the url used by the ajax-GET-eequest to the controller:
#WebServlet(
name = "controller",
urlPatterns = {"/controller"}
)
public class controller extends HttpServlet {
//
(i.e. I used annotations)
Here's the beginning of the ajax-request:
var url = "http://localhost:8081/CourseProject/controller";
$.ajax({
url: url,
type: "get",
//etc.

Wait for Response on REST Request JAVA

I created a java project with glassfish and posted a simple REST GET service like this:
#Path("/get")
public class Rest {
#Path("test/{user}/")
#GET
public String getTest(#PathParam("user") String id) throws IOException {
//send message to websocket client and wait for response
//return "websocket client response";
}
}
this works fine.
I also have a websocket server implementation in the same project. This implementation allows me to send data to the connected clients.
This is my WebSocket implementation:
#ServerEndpoint("/websocket")
public class WebSocketServer {
#OnOpen
public void onOpen(Session userSession){
System.out.println("Se conecto un nuevo cliente");
Modelo.getInstance().users.add(userSession);
}
#OnMessage
public void onMessage(String message,Session userSession) throws IOException{
String username=(String) userSession.getUserProperties().get("username");
if(username==null){
userSession.getUserProperties().put("username", message);
userSession.getBasicRemote().sendText(Modelo.getInstance().buildJsonData("Servidor","nuevo cliente conectado como: "+message));
}else{
Iterator<Session> iterator=Modelo.getInstance().users.iterator();
while(iterator.hasNext()){
iterator.next().getBasicRemote().sendText(Modelo.getInstance().buildJsonData(username,message));
}
}
}
#OnClose
public void onClose(Session userSession){
Modelo.getInstance().users.remove(userSession);
}
#OnError
public void onError(Throwable t){
t.printStackTrace();
}
}
this works fine too.
When the REST method is called i can send successfully a message to one of my websockets clients.
The thing is that i want to return as the REST response, the data that the WebSocket client sends me.
So...
1)Receive REST GET request in Java Server
2)Send via websocket to the client i want to get the info from
3)Respond the REST GET request with the message the websocket client send me.
How can i accomplish this?
[SOLVED]?
I found a way to do this, please i would like to know what do you think.
I found this article: here about async rest reponses.
So i implemented, its the first thing come to my mind, i save the websocket client message in an array, and the REST request is responded when the array has a message.
#Path("/resource")
#GET
public void asyncGet(#Suspended final AsyncResponse asyncResponse) throws IOException {
Modelo.getInstance().enviarMensaje("5", "escenas");
new Thread(new Runnable() {
#Override
public void run() {
String result = veryExpensiveOperation();
asyncResponse.resume(result);
}
private String veryExpensiveOperation() {
while(Modelo.getInstance().responses.size()==0){
}
String result=Modelo.getInstance().responses.get(0);
Modelo.getInstance().responses.clear();
return result;
// ... very expensive operation
}
}).start();
}
I know there a more things to validate this reponses, but at first it works.
I also edit the websockerserver.java to save in the array the response.
Thank you very much
REST works over HTTP which is a request/response model of communication. Which means you need to send a request in order to get a response. Web Sockets is a full duplex socket model. This means the client or the server can send a message as long as the connection is up. The challenge is you're trying to send a response with REST without a request. You could queue the response from the web socket and then send it back with the next REST response. This would however require the REST client to poll the server periodically since you would not have an indication of when the Web Socket client responded.

Netty, how to implement an HTTP connection limiter which sends a response (503) prior to closing channel

Currently in my pipeline I have a simple handler to reject connections when my server gets overloaded:
public class RequestFilter extends SimpleChannelHandler {
#Override
public void channelConnected(final ChannelHandlerContext ctx, final ChannelStateEvent e) throws Exception {
requestLimiter(ctx, e);
super.channelConnected(ctx, e);
}
}
private void requestLimiter(final ChannelHandlerContext ctx, final ChannelStateEvent e) {
if(threshold < counter) {
ctx.getChannel().close();
}
}
When the counter exceeds the threshold, the channel is closed, that all seems to work fine.
Now I'd like to enhance this by first sending an HTTP 503 response prior to closing the channel. What i've tried so far is this method below, instead of closing the channel immediatly I try to write a response to the channel and then handle closing it with a channelfuture listener so it's closed when the write is complete. However whats happening is I get a ton of exceptions about "already sent a response, can't send more than 1" followed by stack overflow.
protected void sendResponse(Channel channel, HttpResponse response) {
if (channel.isConnected()) {
channel.write(response).addListener(ChannelFutureListener.CLOSE);
log.trace("response sent");
} else if (!channel.isConnected()) {
log.trace("attempted to send response, but the channel was closed");
} else {
log.trace("Not sure why this would happen");
}
}
Any thoughts or examples I could look at? thanks
Edit: stacktrace
java.lang.IllegalStateException: cannot send more responses than requests
at org.jboss.netty.handler.codec.http.HttpContentEncoder.writeRequested(HttpContentEncoder.java:104)
at org.jboss.netty.handler.timeout.WriteTimeoutHandler.writeRequested(WriteTimeoutHandler.java:152)
at org.jboss.netty.handler.stream.ChunkedWriteHandler.flush(ChunkedWriteHandler.java:262)
at org.jboss.netty.handler.stream.ChunkedWriteHandler.handleDownstream(ChunkedWriteHandler.java:119)
at org.jboss.netty.handler.execution.ExecutionHandler.handleDownstream(ExecutionHandler.java:165)
at org.jboss.netty.channel.Channels.write(Channels.java:605)
at org.jboss.netty.channel.Channels.write(Channels.java:572)
at org.jboss.netty.channel.AbstractChannel.write(AbstractChannel.java:245)
at com.test.RequestFilter.sendResponse(RequestFilter.java:98)
I don't think it is sending multiple responses. I think that it's [trying] to send one, which is one more than the number of requests which is zero because the the event is being triggered by the connect channel event and the pipeline has yet to see any http requests.
I would change your code to not do this on connect, but rather, trigger the 503 response on the first request. If the channel is then closed, adios client, but if the client's first request sneaks in under the threshold, then remove the bouncer from the pipeline (assuming that once a client is in, they're in for good).
Make sense ?

Categories

Resources