Most recent applications rely on push notifications to alert users of events that they are interested in. Just like how Facebook alerts users of new friend requests or messages.
This can be implemented in several ways… the simplest is to display the alert when page is loaded or refreshed but this is not very user friendly as user will only be notified when they refresh the page. Alternatively, users can be alerted in a more real-time fashion by polling the server for updates. This works, but when there are thousands of users, this can be a resource hog because each user will have to several poll request every minute.
The ideal solution is to use non-blocking IO technology or websockets to keep the connection open and avoid constant polling. Below is a sample implementation on how to achieve this using Atmosphere and Spring.
STEP 1: Include Atmosphere libraries in your Spring project. In your pom.xml, add Maven to atmosphere library.
org.atmosphere atmosphere-runtime ${org.atmosphere-version}
STEP 2: Update web.xml to use the Atmosphere servlet. Add the atmosphere servlet that will handle the NIO requests. This servlet will receive the NIO calls and uses retains your Spring configuration. The Atmosphere servlet should have different URL pattern so that only NIO calls are forwarded to Atmosphere.
notification org.atmosphere.cpr.MeteorServlet org.atmosphere.servlet org.springframework.web.servlet.DispatcherServlet contextClass org.springframework.web.context.support.AnnotationConfigWebApplicationContext 2 true notification /n/*
STEP 3: Create Spring controller. Below is a sample code snippet to handle atmosphere request. This will be the URL to be invoked to retrieve the NIO request. Notice that the request is suspended by Atmosphere.
@Controller public class NotifyHandler { @Autowired private NotificationService notificationService; @RequestMapping(value = "/notify/{userId}") @ResponseStatus(HttpStatus.OK) @ResponseBody public List watch(@PathVariable("userId") String userId, HttpServletRequest request) throws Exception { //Atmosphere framework puts filter/servlet that adds ATMOSPHERE_RESOURCE to all requests AtmosphereResource resource = (AtmosphereResource) request.getAttribute(ApplicationConfig.ATMOSPHERE_RESOURCE); //suspending resource to keep connection resource.suspend(); //find broadcaster, second parameter says to create broadcaster if it doesn't exist Broadcaster broadcaster = BroadcasterFactory.getDefault().lookup(userId,true); //saving resource for notifications broadcaster.addAtmosphereResource(resource); long nCount = notificationService.countNewPopup(new Long(userId)); List notifications = notificationService.findMostRecentPopup(new Long(userId)); List response = new ArrayList(); response.add(""+nCount); for (Notification n:notifications) { response.add(n.getMessage()); } return response; } }
STEP 4: Broadcast message to appropriate users when needed. In your code, whenever a notification is triggered you may broadcast the message to recipient using the sample code below.
Broadcaster b = BroadcasterFactory.getDefault().lookup(userId); if (b!=null) { long nCount = notificationDao.countNewPopup(new Long(userId)); List notifications = notificationDao.findMostRecentPopup(new Long(userId)); StringBuffer response = new StringBuffer("["); response.append(nCount); for (Notification n:notifications) { response.append(",\"") .append(n.getMessage()) .append("\""); } response.append("]"); b.broadcast(response.toString()); }
STEP 5: Finally, include javascript to invoke Atmosphere library. The code below shows the snippet to invoke the URL.
$.atmosphere.subscribe( "/n/notify/1", // URL containing the atmosphere servlet callback, // javascript callback to handle the response $.atmosphere.request = { transport:"websocket", requestCount: 0 });
That’s it. The code snippets above is not complete and cannot not run by itself. The purpose of this post is just to provide directions on the steps needed to get push notification to work. Hope this gives you some direction on how to go about exploring push notification for your application.
7 thoughts on “Push Notifications using Atmosphere and Spring”
Can you please share entire code. I am trying to do a prototype.
Thanks and Regards,
Akash
It would be nice if you post the sample source code with detailed configuration.!
Please give us a working sample of this code at my email id,
Nice tuto, but can you upload all files of this project, I have some problems to get the controller working.
Hi,
I am trying to run above code in FireFox but I don’t see any notifications displaying in FireFox 47.0.1.
I tried with sample chat application using MeteorService where I am able to display the messages in the browser and notifications are not working in FirFox 47.0.1, but same sample was working in Chrome.
So, had a doubt whether Push Notification with Atmosphere & Websocket would work in ForFox 47.0.1 or not.
PFB sample query and let me you ideas for same.
Code:
——-
@MeteorService(path = “/*”, interceptors = {AtmosphereResourceLifecycleInterceptor.class})
public class MeteorChat extends HttpServlet {
@Inject
@Named(“/*”)
private Broadcaster broadcaster;
/**
* Create a {@link Meteor} and use it to suspend the response.
*
* @param req An {@link HttpServletRequest}
* @param res An {@link HttpServletResponse}
*/
@Override
public void doGet(HttpServletRequest req, HttpServletResponse res) throws IOException {
// Set the logger level to TRACE to see what’s happening.
Meteor m=Meteor.build(req).addListener(new AtmosphereResourceEventListenerAdapter());
m.resumeOnBroadcast(m.transport() == LONG_POLLING ? true : false).suspend(-1);
Broadcaster broadcaster= m.getAtmosphereResource().getAtmosphereConfig().getBroadcasterFactory().lookup(“venu”, true);
broadcaster.addAtmosphereResource(m.getAtmosphereResource());
}
/**
* Re-use the {@link Meteor} created on the first GET for broadcasting message.
*
* @param req An {@link HttpServletRequest}
* @param res An {@link HttpServletResponse}
*/
@Override
public void doPost(HttpServletRequest req, HttpServletResponse res) throws IOException {
String body = req.getReader().readLine().trim();
Meteor m=Meteor.build(req);
// Simple JSON — Use Jackson for more complex structure
// Message looks like { “author” : “foo”, “message” : “bar” }
String author = body.substring(body.indexOf(“:”) + 2, body.indexOf(“,”) – 1);
String message = body.substring(body.lastIndexOf(“:”) + 2, body.length() – 2);
System.out.println(“–author—-“+author);
System.out.println(“–message—-“+message);
Broadcaster b = m.getAtmosphereResource().getAtmosphereConfig().getBroadcasterFactory().lookup(“venu”);
if (b!=null) {
b.broadcast(new Data(author, message).toString());
}
}
private final static class Data {
private final String text;
private final String author;
public Data(String author, String text) {
this.author = author;
this.text = text;
}
public String toString() {
return “{ \”text\” : \”” + text + “\”, \”author\” : \”” + author + “\” , \”time\” : ” + new Date().getTime() + “}”;
}
}
}
Does any one get success with this source?
seems web.xml has wrong values
If anyone can share working source it would be brilliant?!
Hola, estaría bien que pusiera en código completo