The Source for Java Technology Collaboration
User: Password:
Register | Login help    

Search

Online Books:
java.net on MarkMail:


jfarcand's Blog

@ MUST \!A = 4\pi r^2 : Atmosphere 0.5 is released

Posted by jfarcand on January 21, 2010 at 10:29 AM PST

Atmosphere 0.5 is released. This release includes many new features like Guice Support, asynchronous request processing, JMS support, JQuery support, Events listeners, etc..

IMG_0159.JPG

The community around Atmosphere is shaping and we have received many good feedback, which is reflected by the new features added:

Guice Support: Now quite simple to integrate with Google Guice using the new AtmosphereGuiceServlet.

As simple a:

     @Override
protected void configureServlets(){
Map params = new HashMap();
params.put(PackagesResourceConfig.PROPERTY_PACKAGES, "org.atmosphere.samples.guice");
serve("/chat*").with(AtmosphereGuiceServlet.class, params);
}

New Broadcaster: Easy Asynchronous HTTP Request Processing

The new set of Broadcaster allow you to do full asynchronous http processing, similar to what RESTEasy offer:

@Path("/")
public class SimpleResource
{
@GET
@Path("basic")
@Produces("text/plain")
@Suspend(period=1)
public Broadcastable getBasic(final @PathParam("topic") JerseySimpleBroadcaster broadcaster) throws Exception
{
Thread t = new Thread()
{
@Override
public void run()
{
try
{
Response jaxrs = Response.ok("basic").type(MediaType.TEXT_PLAIN).build();
broadcaster.broadcast(jaxrs);
}
catch (Exception e)
{
e.printStackTrace();
}
}
};
t.start();
return new Broadcastable(broadcaster);
}
}

New AtmosphereResourceEventListener

You can now listen to events like resume, client disconnections and broadcast.

      @GET
@Suspend(listeners={EventsLogger.class})
public Broadcastable subscribe() {
return broadcast("OK");
}

where

public class EventsLogger implements AtmosphereResourceEventListener {

public EventsLogger() {
}

public void onResume(AtmosphereResourceEvent event) {
}

public void onDisconnect(AtmosphereResourceEvent event) {
}

public void onBroadcast(AtmosphereResourceEventevent) {
}

The Meteor now supports scheduled and delayed events broadcasting

If you want to use Atmosphere without re-writing your Servlet based application (or don't want to use the Servlet 3.0 Async API), you can use a Meteor instead and do all the same operation as when you use the annotations:

    @Override
public void doGet(HttpServletRequest req, HttpServletResponse res) throws IOException {
Meteor m = Meteor.build(req, list, null);

// Log all events on the concole.
m.addListener(new EventsLogger());
....
m.suspend(-1);

// Periodic events broadcast
m.schedule("Atmosphere is cool", 10, TimeUnit.SECONDS);
}

Build in Framework support for different browser behaviors and proxy

The Framework is now adding the proper headers required to make it work with any proxy. The framework also make sure all browsers work out of the box by writing some default comments when a response get suspended so browser like Chome and Webkit works properly. The framework is also taking care of errant connections when hot deployment happens.

Improved algorithm for WebServer detection like App Engine, Tomcat, Jetty and GlassFish

The Framework is auto detecting which WebServer is running on in a more efficient way. The behaviour is also configurable by adding, in web.xml:

<init-param>                 
<param-name>org.atmosphere.cpr.cometSupport</param-name>
<param-value>AN EXISTING CometSupport implementation, or a customized one</param-value>
</init-param>

JMS support

You can now cluster your application using JMS, Shoal or JGroups

    @POST
@Broadcast
@Cluster(
name="chat",
value=JMSFilter.class
)
public Broadcastable publish(@FormParam("message") String message){
return broadcast(message);
}

Annotation improvements

All annotations has been reviewed and some cool functionality added like resumeOnBroadcast, event listeners, etc. See the API for more information.

JQuery Support

See how simple it can be here.

Better Control of BroadcastFilter state, allowing easier agreggation/filtering/pruning of events

If your application extensively broadcast events, it is important to pick up the right strategy to make sure all events reach your clients as fast as possible, even under high load. BroadcastAction has been added to make the writing BroadcastFilter's aggreagation/filtering/pruning of events really simple.  Simple as

    public BroadcastAction filter(Object message) {
if (message instanceof String){
bufferedMessage.append(message);
if (bufferedMessage.length() < maxBufferedString){
return new BroadcastAction(ACTION.ABORT,message);
} else {
message = bufferedMessage.toString();
bufferedMessage = new StringBuilder();
return new BroadcastAction(ACTION.CONTINUE,message);
}
} else {
return new BroadcastAction(message);
}
}

Powerful PubSub sample

Take a look at our PubSub sample that demonstrate all the power of Atmosphere!!! We also improved our white paper (PDF).

Woooooo!

As usual, Thanks to Matthias (ADF Faces), Paul (Jersey), Viktor (Akka), Hubert (Grizzly) and Catagay (PrimeFaces) and all the users for their contributions to Atmosphere! Special Thanks to Ning for allowing me to work on this project (and Thanks to Sun before Ning!).

For any questions or to download Atmosphere, go to our main site and use our Nabble forum (no subscription needed) or follow us on Twitter and tweet your questions there!

technorati:

Related Topics >> Blogs      
Comments
Comments are listed in date ascending order (oldest first)

htp://atmosphere.dev.java.net

htp://atmosphere.dev.java.net/ or http://atmosphere.dev.java.net/

Using Google Guice with Atmosphere

Posted by jfarcand on December 15, 2009 at 8:15 AM PST

Starting with Atmosphere 0.5, you can now use Google Guice to configure Atmosphere

IMG_0159.JPG

Google Guice support is enabled by referencing the Guice filter GuiceFilter and an application specific ServletContextListener that extends from GuiceServletContextListener in the web.xml. For example, the web.xml may be as follows:


   <web-app>
     <listener>
       <listener-class>org.atmosphere.samples.guice.GuiceChatModule</listener-class>
     </listener>
     <filter>

       <filter-name>Guice Filter</filter-name>
       <filter-class>com.google.inject.servlet.GuiceFilter</filter-class>
     </filter>
     <filter-mapping>

       <filter-name>Guice Filter</filter-name>

       <url-pattern>/*</url-pattern>
     </filter-mapping>
   </web-app>
and the application specific servlet context listener may be as follows:
public class GuiceChatConfig extends GuiceServletContextListener {

    @Override
    protected Injector getInjector() {
        return Guice.createInjector(new GuiceChatModule());
    }
} 
with
public class GuiceChatModule  extends ServletModule{

    @Override
    protected void configureServlets(){
        Map params = new HashMap();
        params.put(PackagesResourceConfig.PROPERTY_PACKAGES, "org.atmosphere.samples.guice");
        serve("/chat*").with(AtmosphereGuiceServlet.class, params);
    }
} 

That's it. You can download the previously Atmosphere-Jersey sample, this time Guice enabled from here. For any questions or to download Atmosphere, go to our main site and use our Nabble forum (no subscription needed) or follow us on Twitter and tweet your questions there!

technorati:

Related Topics >> Blogs      Java Enterprise      Web Applications      
Comments
Comments are listed in date ascending order (oldest first)

Google Guice is popularly

Google Guice is popularly known as the Google Juice is a light weight version for the Java 5 application. Guice helps you with the use of factories and new lines in the codes. You will be needed to write the code but still that should be much easier than the way you have done it before. The attraction that I got while working with the guice is that it can be easily debugged with a much faster response than the way we do it normally. You would be able to understand the code more easily with and without the tool. That is also another advantage with the Guice. GuiceFilter and an application specific ServletContextListener is good and the code would help in the implementation of the projects. Super Bowl Ticket |Structural Foam | Pool Tables| Tube Notcher

"Google Guice is popularly

"Google Guice is popularly known as the Google Juice is a light weight version for the Java 5 application. Guice helps you with the use of factories and new lines in the codes. You will be needed to write the code but still that should be much easier than the way you have done it before. The attraction that I got while working with the guice is that it can be easily debugged with a much faster response than the way we do it normally. You would be able to understand the code more easily with and without the tool. That is also another advantage with the Guice. GuiceFilter and an application specific ServletContextListener is good and the code would help in the implementation of the projects." - I absolutely agree on this point. Great post! steam press iron | ways to make money online

org.atmosphere.samples.guice.

org.atmosphere.samples.guice.GuiceChatModule Guice Filter com.google.inject.servlet.GuiceFilter Guice Filter /* I am happy to find it. Thanks for sharing it here. Birthday SMS Funny Text Good Morning SMS

Putting GlassFish v3 in Production: Essential Surviving Guide

Posted by jfarcand on November 27, 2009 at 7:56 AM PST

On December 10, GlassFish v3 GA will spread the world. As you are aware, the marketing vehicle for this release will be Java EE 6 and the fact that GlassFish is now a full OSGi runtime/container!!! Both are innovative technology, but they will not save your life once you put GlassFish in production hence this survival guide :-). At the end, once your OSGi/EE 6 application is ready, you still want to have the same great performance you've got with GlassFish v2. This blog will gives some hints about how to configure and prepare GlassFish v3 for production use.

v3runtime.png

New Architecture

With v3, the Grizzly Web Framework role has significantly increased if you compare with v2. In v2, its role was to serve HTTP requests in front of the Tomcat based WebContainer. In v3, Grizzly is used as an extensible micro-kernel which handle almost all real time operations including dispatching HTTP requests to the Grizzly's Web based extensions (Static Resource Container, Servlet Container, JRuby Container, Python Container, Grails Container), Administrative extensions (Java WebStart support, Admin CLI), WebService extension (EJB) and Monitoring/Management REST extensions.

v3-diagram.pgn

At runtime, Grizzly will do the following

v3runtime.png

If you are familiar with Grizzly's internals

v3runtime.png

As you can see, it is critical to properly configure GlassFish in order to get the expected performance for your application and GlassFish in general.

Debugging GlassFish

Before jumping into the details, I recommend you always run GlassFish using the following property, which display in the server log the Grizzly internal configuration for both the NIO and Web Framework

-Dcom.sun.grizzly.displayConfiguration=true or
network-config>network-listeners>network-listener>transport#display-configuration
If you need to see what Grizzly is doing under the hood like the request header received, the response written etc. you may want to turn on snoop so you don't need to use Wireshark or ngrep
-Dcom.sun.grizzly.enableSnoop=true or 
network-config>network-listeners>network-listener>transport#enable-snoop 
Note that if you enable that mechanism, the performance will drop significantly so use it only for debugging purpose.

Configuring the VM

Makes sure you remove in domain.xml the following jvm-options:

-Xmx512 -client

and replace it with

-server -XX:+AggressiveHeap -Xmx3500m -Xms3500m -Xss128k
-XX:+DisableExplicitGC

For anything other than Solaris/SPARC, 3500m needs to be 1400m. On a multi-CPU machine, add:

-XX:ParallelGCThreads=N -XX:+UseParallelOldGC
where N is the number of CPUs if < 8 (so really, you can leave it out altogether in that case) and N = number of CPUs / 2 otherwise. On a Niagara, add:

-XX:LargePageSizeInBytes=256m
You can also install the 64-bit JVM and use
-XX:+UseCompressedOops
with JDK 6u16 and later. A 64-bit JVM with
-XX:+UseCompressedOops
will allow you to specify larger Java heaps, especially useful on Windows x64, where you are limited to about
-Xmx1400m
of max Java heap. Note that a 64-bit JVM will mean you'll need to be running a 64-bit operating system. That's not an issue with Solaris. Many people who run Linux only run the 32-bit version of Linux. And, for Windows users, they'll need a 64-bit Windows in order to use a 64-bit Windows JVM. A 64-bit JVM with -XX:+UseCompressedOops will give you larger Java heaps with 32-bit performance. 64-bit JVMs also provides additional CPU registers to be available on Intel/AMD platforms.

Configuring the Thread Pool

Make sure you take a look at "what changed" since v2 and how you can properly configure Grizzly in v3. The one you should care are acceptors-thread

network-config>transports>transport>tcp#acceptor-threads
and the number of worker threads
network-config>thread-pools>http-threadpool
The recommended value for acceptors-thread should be the number of core/processor available on the machine you deploy on. I recommend you always run sanity performance test using the default value (1) and with the number of core just to make sure. Next is to decide the number of threads required per HTTP port. With GlassFish v2, the thread pool configuration was shared amongst all HTTP port, which was problematic, as some port/listener didn't needed to have that many threads as port 8080. We fixed that in v3 so you can configure the thread pool per listener. Now the ideal value for GlassFish v3 should always be between 20 and 500 maximum as Grizzly use an non blocking I/O strategy under the hood, and you don't need as many threads as if you were using a blocking I/O server like Tomcat. Here I can't recommend a specific number, it is always based on what your application is doing. For example, if you do a lot of database query, you may want to have a higher number of threads just in case the connection pool/jdbc locks on a database, and "waste" threads until they unlock. In GlassFish v2, we did see a lot of applications that were hanging because all the worker threads were locked by the connection-pool/jdbc. The good things in v3 is those "wasted" threads will eventually times out, something that wasn't available with v2. The default value is 5 minutes, and this is configurable
configs.config.server-config.thread-pools.thread-pool.http-thread-pool.idle-thread-timeout-seconds

I/O strategy and buffer configuration

In terms of buffers used by Grizzly to read and write I/O operations, the default (8192) should be the right value but you can always play with the number

network-config>protocols>protocol>http#header-buffer-length-byte
network-config>protocols>protocol>http#send-buffer-size
If your application is doing a lot of I/O operations like write, you can also tell Grizzly to use an asynchronous strategy
-Dcom.sun.grizzly.http.asyncwrite.enabled=true
When this strategy is used, all I/O write will be executed using a dedicated thread, freeing the worker thread that executed the operation. Again, it could make a big differences. An alternative that could be considered also is if you are noticing that some write operations seems to takes more time than expected. You may try to increase the pool of "write processor" by increasing the number of NIO Selector:
-Dcom.sun.grizzly.maxSelectors=XXX
Make sure this number is never smaller than the number of worker thread as it will gives disastrous performance result. You should increase that number if you application use the new Servlet 3.0 Async API, the Grizzly Comet Framework or Atmosphere (recommended). When asynchronous API are used, GlassFish will needs more "write processor" than without

Let Grizzly magically configure itself

Now Grizzly supports two "unsupported" properties in GlassFish that can always be used to auto configure GlassFish by itself. Those properties may or may not make a difference, but you can always try them with and without your configuration. The first one will configure for you the buffers, acceptor-threads and worker threads:

-Dcom.sun.grizzly.autoConfigure=true
The second one will tell Grizzly to change its threading strategy to leader/follower
-Dcom.sun.grizzly.useLeaderFollower=true
It may or may not make a difference, but worth the try. You can also force Grizzly to terminates all its I/O operations using a dedicated thread
com.sun.grizzly.finishIOUsingCurrentThread=false
It may makes a difference if you application do a small amount of I/O operations under load.

Cache your static resources!

Now by default, the Grizzly HTTP File Caching is turned off. To get decent static resources performance, I strongly recommend you turn it on (it makes a gigantic difference)
network-config>protocols>protocol>http>file-cache#enabled

Only for JDK 7

Now, if you are planning to use JDK 7, I recommend you switch Grizzly ByteBuffer strategy and allocate memory outside the VM heap by using direct byte buffer
-Dcom.sun.grizzly.useDirectByteBuffer=true
Only on JDK 7 as with JDK 6, using allocating heap byte buffer gives better performance than native. Now if you realize GlassFish is allocate too much native memory, just add
-Dcom.sun.grizzly.useByteBufferView=false
That should reduce the native memory usage.

WAP and Slow Network

If your application will be used by Phone using the WP protocol or if from slow network, you may configure extends the default time out when Grizzly is executing I/O operations:

-Dcom.sun.grizzly.readTimeout or
network-config>network-listeners>network-listener>transport#read-timeout
for read, and
com.sun.grizzly.writeTimeout or
network-config>network-listeners>network-listener>transport#write-timeout
for write. The default for both is 30 seconds. That means Grizzly will wait 30 seconds for incoming bytes to comes from the client when processing a request, and 30 seconds when writing bytes back to the client before closing the connection. On slow network, 30 seconds for executing the read operations may not be enough and some client may not be able to execute requests. But be extra careful when changing the default value as if the value is too high, a worker thread will be blocked waiting for bytes and you may end up running out of worker threads. Note to say that a malicious client may produce a denial-of-service by sending bytes very slowly. It may takes as 5 minutes (see the thread times out config above) before Grizzly will reclaims the worker threads. If you experience write times out, e.g the remote client is not reading the bytes the server is sending, you may also increase the value but instead I would recommend you enable the async I/O strategy described above to avoid locking worker thread.

Configuring the keep alive mechanism a-la-Tomcat

The strategy Grizzly is using for keeping a remote connection open is by pooling the file descriptor associated with the connection, and when an I/O operations occurs, get the file description from the pool and spaw a thread to execute the I/O operation. As you can see, Grizzly isn't blocking a Thread who waits for I/O operation (the next client's request). Tomcat strategy is different, e.g when Tomcat process requests, a dedicated thread will block between the client requests for maximum 30 seconds. This gives really good throughput performance but it doesn't scale very well, as you need one thread per connection. But if your OS have tons of Threads available, you can always configure Grizzly to use a similar strategy:

-Dcom.sun.grizzly.keepAliveLockingThread=true
Tomcat also have an algorithm that will reduce the waiting time a Thread can block waiting for the new I/O operations, so under load threads don't get blocked too long, giving the change to other requests to execute. You can enable a similar algorithm with Grizzly:
-Dcom.sun.grizzly.useKeepAliveAlgorithm=true
Depending on what your application is doing, you may get nice performance improvement by enabling those properties.

Ask questions!

As I described here, I will no longer work on GlassFish by the time you read this blog, so make sure you ask your questions using the GlassFish mailing list (users@glassfish.dev.java.net) or you can always follow me on Twitter! and ask the question there!

Related Topics >> Blogs      Glassfish      Performance      
Comments
Comments are listed in date ascending order (oldest first)

Leaving Sun Microsystems

Posted by jfarcand on November 24, 2009 at 9:20 AM PST

It is always hard to write these type of posts. As of December 4th, I will no longer be with Sun Microsystems.

IMG_0159.JPG

It all started from working on Java EE1.3 and a server called Tomcat. I was on Tomcat for a couple of years and then came with the idea of a NIO based HTTP Connector for Tomcat called ... Grizzly :-) Funny it never ended into Tomcat! Grizzly started with SJS AS 8.0 and slowly replaced the old Netscape C Runtime and Tomcat inside Sun's products (they are many many now :-)). I've then moved to a Project called Minnow, a components based server running on top of Grizzly and Maven 2. You start Grizzly and Grizzly was, at runtime, taking care of downloading/installing the artifacts needed to serve the request: Containers installed on the fly! The project got canned as soon as I've presented it internally ...I've always had trouble inside Sun with my projects :-) ... but it has opened the door to GlassFish v3 as the code got re-used to create the Grizzly based micro-kernel of the current GlassFish v3. Didn't wasted my time finally :-) During that time GlassFish moved from being a Zero to a Hero, and now it is just amazing to see where GlassFish is and the perception the community have of it. My fingers hurt when I look at the emails traffics we have generated on users@glassfish! Finally the "Comet things" surrounded me and at the end I've created the Atmosphere Framework, which is positively invading the planet those days :-).

I will really miss my team I've been working for the last 7 years.....Now the sad news: I will stop working on both GlassFish and Grizzly on December 4, letting something I've created growing by itself. But the Grizzly community is quite mature and I'm fully confident to see amazing release in the future! BTW, since I am privately getting up to 30 emails per weeks from early adopter or existing GlassFish users, please make sure you either ping Sun's support directly or use the Grizzly/GlassFish public mailing list to get the appropriate response starting now :-)

What about Atmosphere? This project is way too innovative to leave it and I will continue working on it or on something similar, depending on what Sun is up to :-).

Where do I go? I'm going to Ning.com. Don't worries I will continue polluting this blog and worse you can always follow me on Twitter!

Comments
Comments are listed in date ascending order (oldest first)

Bonne chance, et je te

Bonne chance, et je te remercie de m'avoir fait découvrir Grizzly et fait rejoindre l'équipe. Ce fut un très bonne expérience pour le développement professionnel et je vais continuer de suivre tes blogs. Faut bien suivre la technologie et si possible d'en faire parti.

That is sad news for the rest

That is sad news for the rest of us indeed, but incredibly exciting for you. Without all you've done, Glassfish would still be an also-ran. Best of luck in the future!

Thanks for everything

Good luck with your new endeavors. Thanks for all the help getting us get started with GlassFish at IQSS at Harvard

Sorry to see you go

It's sad to see so many of Sun's greats leaving the company because of the extremely long merger process and all of the uncertainty. It's understandable though. The GlassFish community will miss you. Good luck at your new job.

thanks, jean francois

Thanks, Jean Francois for your excellent work on GlassFish, Grizzly and Atmosphere. Of course this is sad news for all of us. Your conference talks also will be missed (so sad that I couldn't go to this year's Devoxx) - unless ning.com will allow you to keep up with this work. I wish you much pleasure at your new workplace, a good team to work with and many interesting projects. Good luck!

Thanks for the fish, JFA!

I'm sure the move will be good for you, JFA! Thanks for all your contributions; we will take good care of Grizzly, and will work with you on Atmosphere! - eduard/o

Vous venez à Palo Alto?

Vous venez à Palo Alto?

It's a shame to see you go,

It's a shame to see you go, but thanks for all the good work you've done that I get to benefit from daily!

La vie est courte, il faut savoir saisir les opportunités !

Et tant pis pour Sun :) OOps, Oracle... BTW, on démarre juste MINA 3.0, tu es le bienvenu ! Bonne chance chez Ning, et salut nos amis les apaches qui y bossent (Henning, Brian...)

Merci !

Merci d'avoir fait progresser le moteur de servlet qui en avait bien besoin, merci d'avoir pris le temps de passer voir le Paris JUG, merci d'avoir bousculé le monde java avec Comet ... Merci pour tout et bonne continuation chez Ning.com. Un chat communautaire de millions d'utilisateurs avec Atmosphere ? :-) Cyrille

Servlet 3.0 Asynchronous API or Atmosphere? Easy decision!

Posted by jfarcand on November 6, 2009 at 9:38 AM PST

One the comment I'm getting about Atmosphere is why should I use the framework instead of waiting for Servlet 3.0 Async API. Well, it simple: much simpler, works with any existing Java WebServer (including Google App Engine!), and will auto-detect the Servlet 3.0 Async API if you deploy your application on a WebServer that support it.

IMG_0159.JPG

To make a fair comparison, let's write the hello world of Comet, a Chat application and compare the server side code. Without technical details, let's just drop the entire server code. First, the Servlet 3.0 version (can probably be optimized a little):

  1 package web.servlet.async_request_war;
  2 
  3 import java.io.IOException;
  4 import java.io.PrintWriter;
  5 import java.util.Queue;
  6 import java.util.concurrent.ConcurrentLinkedQueue;
  7 import java.util.concurrent.BlockingQueue;
  8 import java.util.concurrent.LinkedBlockingQueue;
  9 
 10 import javax.servlet.AsyncContext;
 11 import javax.servlet.AsyncEvent;
 12 import javax.servlet.AsyncListener;
 13 import javax.servlet.ServletConfig;
 14 import javax.servlet.ServletException;
 15 import javax.servlet.annotation.WebServlet;
 16 import javax.servlet.http.HttpServlet;
 17 import javax.servlet.http.HttpServletRequest;
 18 import javax.servlet.http.HttpServletResponse;
 19 
 20 @WebServlet(urlPatterns = {"/chat"}, asyncSupported = true)
 21 public class AjaxCometServlet extends HttpServlet {
 22 
 23     private static final Queue<AsyncContext> queue = new ConcurrentLinkedQueue<AsyncContext>();
 24     private static final BlockingQueue<String> messageQueue = new LinkedBlockingQueue<String>();
 25     private static final String BEGIN_SCRIPT_TAG = "<script type='text/javascript'>\n";
 26     private static final String END_SCRIPT_TAG = "</script>\n";
 27     private static final long serialVersionUID = -2919167206889576860L;
 28 
 29     private Thread notifierThread = null;
 30 
 31     @Override
 32     public void init(ServletConfig config) throws ServletException {
 33         Runnable notifierRunnable = new Runnable() {
 34             public void run() {
 35                 boolean done = false;
 36                 while (!done) {
 37                     String cMessage = null;
 38                     try {
 39                         cMessage = messageQueue.take();
 40                         for (AsyncContext ac : queue) {
 41                             try {
 42                                 PrintWriter acWriter = ac.getResponse().getWriter();
 43                                 acWriter.println(cMessage);
 44                                 acWriter.flush();
 45                             } catch(IOException ex) {
 46                                 System.out.println(ex);
 47                                 queue.remove(ac);
 48                             }
 49                         }
 50                     } catch(InterruptedException iex) {
 51                         done = true;
 52                         System.out.println(iex);
 53                     }
 54                 }
 55             }
 56         };
 57         notifierThread = new Thread(notifierRunnable);
 58         notifierThread.start();
 59     }
 60 
 61     @Override
 62     protected void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
 63         res.setContentType("text/html");
 64         res.setHeader("Cache-Control", "private");
 65         res.setHeader("Pragma", "no-cache");
 66         
 67         PrintWriter writer = res.getWriter();
 68         // for IE
 69         writer.println("<!-- Comet is a programming technique that enables web servers to send data to the client without having any need for the client to request it. -->\
    n");
 70         writer.flush();
 71 
 72         req.setAsyncTimeout(10 * 60 * 1000);
 73         final AsyncContext ac = req.startAsync();
 74         queue.add(ac);
 75         req.addAsyncListener(new AsyncListener() {
 76             public void onComplete(AsyncEvent event) throws IOException {
 77                 queue.remove(ac);
 78             }
 79 
 80             public void onTimeout(AsyncEvent event) throws IOException {
 81                 queue.remove(ac);
 82             }
 83         });
 84     }
 85 
 86     @Override
 87     @SuppressWarnings("unchecked")
 88     protected void doPost(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
 89         res.setContentType("text/plain");
 90         res.setHeader("Cache-Control", "private");
 91         res.setHeader("Pragma", "no-cache");
 92
 93         req.setCharacterEncoding("UTF-8");
 94         String action = req.getParameter("action");
 95         String name = req.getParameter("name");
 96 
 97         if ("login".equals(action)) {
 98             String cMessage = BEGIN_SCRIPT_TAG + toJsonp("System Message", name + " has joined.") + END_SCRIPT_TAG;
 99             notify(cMessage);
100 
101             res.getWriter().println("success");
102         } else if ("post".equals(action)) {
103             String message = req.getParameter("message");
104             String cMessage = BEGIN_SCRIPT_TAG + toJsonp(name, message) + END_SCRIPT_TAG;
105             notify(cMessage);
106 
107             res.getWriter().println("success");
108         } else {
109             res.sendError(422, "Unprocessable Entity");
110         }
111     }
112 
113     @Override
114     public void destroy() {
115         queue.clear();
116         notifierThread.interrupt();
117     }
118 
119     private void notify(String cMessage) throws IOException {
120         try {
121             messageQueue.put(cMessage);
122         } catch(Exception ex) {
123             throw new IOException(ex);
124         }
125     }
126 
127     private String escape(String orig) {
128         StringBuffer buffer = new StringBuffer(orig.length());
129 
130         for (int i = 0; i < orig.length(); i++) {
131             char c = orig.charAt(i);
132             switch (c) {
133             case '\b':
134                 buffer.append("\\b");
135                 break;
136             case '\f':
137                 buffer.append("\\f");
138                 break;
139             case '\n':
140                 buffer.append("<br />");
141                 break;
142             case '\r':
143                 // ignore
144                 break;
145             case '\t':
146                 buffer.append("\\t");
147                 break;
148             case '\'':
149                 buffer.append("\\'");
150                 break;
151             case '\"':
152                 buffer.append("\\\"");
153                 break;
154             case '\\':
155                 buffer.append("\\\\");
156                 break;
157             case '<':
158                 buffer.append("<");
159                 break;
160             case '>':
161                 buffer.append(">");
162                 break;
163             case '&':
164                 buffer.append("&");
165                 break;
166             default:
167                 buffer.append(c);
168             }
169         }
170 
171         return buffer.toString();
172     }
173 
174     private String toJsonp(String name, String message) {
175         return "window.parent.app.update({ name: \"" + escape(name) + "\", message: \"" + escape(message) + "\" });\n";
176     }
177 }

OK now with Atmosphere , the same code consist of:

  1 package org.atmosphere.samples.chat.resources;
  2 
  3 import javax.ws.rs.Consumes;
  4 import javax.ws.rs.GET;
  5 import javax.ws.rs.POST;
  6 import javax.ws.rs.Path;
  7 import javax.ws.rs.Produces;
  8 import javax.ws.rs.WebApplicationException;
  9 import javax.ws.rs.core.MultivaluedMap;
 10 import org.atmosphere.annotation.Broadcast;
 11 import org.atmosphere.annotation.Schedule;
 12 import org.atmosphere.annotation.Suspend;
 13 import org.atmosphere.util.XSSHtmlFilter;
 14 
 15 @Path("/")
 16 public class ResourceChat {
 17 
 18     @Suspend
 19     @GET
 20     @Produces("text/html;charset=ISO-8859-1")
 21     public String suspend() {
 22         return "";
 23     }
 24 
 25     @Broadcast({XSSHtmlFilter.class, JsonpFilter.class})
 26     @Consumes("application/x-www-form-urlencoded")
 27     @POST
 28     @Produces("text/html;charset=ISO-8859-1")
 29     public String publishMessage(MultivaluedMap form) {
 30         String action = form.getFirst("action");
 31         String name = form.getFirst("name");
 32 
 33         if ("login".equals(action)) {
 34             return ("System Message" + "__" + name + " has joined.");
 35         } else if ("post".equals(action)) {
 36             return name + "__" + form.getFirst("message");
 37         } else {
 38             throw new WebApplicationException(422);
 39         }
 40     }
 41 }

OK so what's the deal? What's makes Atmosphere so easy? The Servlet 3.0 new Async API offers:

  • Method to suspend a response, HttpServletRequest.startAsync()
  • Method to resume a response: AsyncContext.complete()
Atmosphere offers:
  • Annotation to suspend: @Suspend
  • Annotation or resume: @Resume
  • Annotation to broadcast (or push) events to the set of suspended responses: @Broadcast
  • Annotation to filter and serialize broadcasted events using BroadcasterFilter (XSSHtmlFilter.class, JsonpFilter.class)
  • Build it support for all browser implementation incompatible implementation (ex: no need to output comments like in the Servlet 3.0 sample (line 69)). Atmosphere will workaround all those issues for you.
With Servlet 3.0 Async API, the missing part is how you share information with suspended responses. In the current chat sample, you need to creates your own Thread/Queue in order to broadcast events to your set of suspended responses (line 32 to 56). This is not a big deal, but you will need to do something like that for all your Servlet 3.0 Async based applications...or use a Framework that do it for you!.

Still not convinced? Well, you can write your Atmosphere applications today and not have to wait for Servlet.3.0 implementation (OK easy plug for my other project: GlassFish v3 supports it pretty well!). Why? Atmosphere always auto-detected the best asynchronous API when you deploy your application. It always try first to look up the 3.0 Async API. If it fails, it will try to find WebServer's native API like Grizzly Comet (GlassFish), CometProcessor (Tomcat), Continuation (Jetty), HttpEventServlet (JBossWeb), AsyncServlet (WebLogic), Google App Engine (Google). Finally, it will fallback to use a blocking I/O Thread to emulate support for asynchronous events.

But you don't want to use Java? Fine, try the Atmosphere Grails Plug In, or Atmosphere in PrimesFaces if you like JSF, or use Scala:

  1 package org.atmosphere.samples.scala.chat
  2 
  3 import javax.ws.rs.{GET, POST, Path, Produces, WebApplicationException, Consumes}
  4 import javax.ws.rs.core.MultivaluedMap
  5 import org.atmosphere.annotation.{Broadcast, Suspend}
  6 import org.atmosphere.util.XSSHtmlFilter
  7 
  8 @Path("/chat")
  9 class Chat {
 10 
 11     @Suspend
 12     @GET
 13     @Produces(Array("text/html;charset=ISO-8859-1"))
 14     def suspend() = {
 15         ""
 16     }
 17 
 18     @Broadcast(Array(classOf[XSSHtmlFilter],classOf[JsonpFilter]))
 19     @Consumes(Array("application/x-www-form-urlencoded"))
 20     @POST
 21     @Produces(Array("text/html;charset=ISO-8859-1"))
 22     def publishMessage(form: MultivaluedMap[String, String]) = {
 23         val action = form.getFirst("action")
 24         val name = form.getFirst("name")
 25 
 26         val result: String = if ("login".equals(action)) "System Message" + "__" + name + " has joined."
 27              else if ("post".equals(action)) name + "__" + form.getFirst("message")
 28              else throw new WebApplicationException(422)
 29 
 30         result
 31     }
 32 
 33 
 34 }

Echec et Mat!

Now, I can understand you already have an existing application and just want to update it with suspend/resume/broadcast functionality, without having to re-write it completely. Fine, let's just use the Atmosphere's Meteor API:

  1 package org.atmosphere.samples.chat;
  2 
  3 import java.io.IOException;
  4 import java.util.LinkedList;
  5 import java.util.List;
  6 import javax.servlet.http.HttpServlet;
  7 import javax.servlet.http.HttpServletRequest;
  8 import javax.servlet.http.HttpServletResponse;
  9 import org.atmosphere.cpr.BroadcastFilter;
 10 import org.atmosphere.cpr.Meteor;
 11 import org.atmosphere.util.XSSHtmlFilter;
 12 
 13 public class MeteorChat extends HttpServlet {
 14 
 15     private final List list;
 16 
 17     public MeteorChat() {
 18         list = new LinkedList();
 19         list.add(new XSSHtmlFilter());
 20         list.add(new JsonpFilter());
 21     }
 22 
 23     @Override
 24     public void doGet(HttpServletRequest req, HttpServletResponse res) throws IOException {
 25         Meteor m = Meteor.build(req, list, null);
 26 
 27         req.getSession().setAttribute("meteor", m);
 28 
 29         res.setContentType("text/html;charset=ISO-8859-1");
 30         res.addHeader("Cache-Control", "private");
 31         res.addHeader("Pragma", "no-cache");
 32 
 33         m.suspend(-1);
 34         m.broadcast(req.getServerName() + "__has suspended a connection from " + req.getRemoteAddr());
 35     }
 36 
 37     public void doPost(HttpServletRequest req, HttpServletResponse res) throws IOException {
 38         Meteor m = (Meteor)req.getSession().getAttribute("meteor");
 39         res.setCharacterEncoding("UTF-8");
 40         String action = req.getParameterValues("action")[0];
 41         String name = req.getParameterValues("name")[0];
 42 
 43         if ("login".equals(action)) {
 44             req.getSession().setAttribute("name", name);
 45             m.broadcast("System Message from " + req.getServerName() + "__" + name + " has joined.");
 46             res.getWriter().write("success");
 47             res.getWriter().flush();
 48         } else if ("post".equals(action)) {
 49             String message = req.getParameterValues("message")[0];
 50             m.broadcast(name + "__" + message);
 51             res.getWriter().write("success");
 52             res.getWriter().flush();
 53         } else {
 54             res.setStatus(422);
 55 
 56             res.getWriter().write("success");
 57             res.getWriter().flush();
 58         }
 59     }
 60 }

Servlet 3.0 Async API is Game Over! Finally I must admit that Servlet 3.0 Async API have asynchronous dispatcher you can use to forward request asynchronously:

    public void doGet(HttpServletRequest req, HttpServletResponse res)
            throws IOException, ServletException {

        final AsyncContext ac = req.startAsync();
        final String target = req.getParameter("target");

        Timer asyncTimer = new Timer("AsyncTimer", true);
        asyncTimer.schedule(
            new TimerTask() {
                @Override
                public void run() {
                    ac.dispatch(target);
                }
            },
        5000);
    }

With Atmosphere, the same code will works but your application will only works when deployed on Servlet 3.0 WebServer. Instead, you can implement the same functionality using Broadcast's delayed broadcast API and still have a portable application without limiting you with Servlet 3.0 Async API...that's something I will talk in my next blog!

For any questions or to download Atmosphere, go to our main site and use our Nabble forum (no subscription needed) or follow us on Twitter and tweet your questions there!

technorati:

Related Topics >> Ajax      Blogs      Glassfish      
Comments
Comments are listed in date ascending order (oldest first)

Meteor and notifications

Hi, the Meteor/servlet integration is really interesting. Let's say I have an existing application and that I just want to be notified of when the client drops the connection. The Meteor API does not look like it's providing such ability. Is it implementable, or I'm just looking for unicorns here? ;-)

You are right. If you want to

You are right. If you want to get notified when the client close the remote connection. If you want to to get notified when the connection close and use the Meteor, you will need to extend the ReflectorServletProcessor ... let's continue the discussion on users@atmosphere.dev.java.net

See you in Europe in November

Posted by jfarcand on November 4, 2009 at 9:58 AM PST

I'm will be leaving Québec next week for talks at JFall, Paris JUG, Prague JUG and Devoxx. The talks will focus on Atmosphere and it's spin off (Grail Plug In, Akka, PrimesFaces) and a little bit of Grizzly's work-in-progress Servlet Container.

IMG_0171.JPG

First, I will stop by Amsterdam November 11 and talk about Atmosphere at JFall 2009. Next I will stop by Paris JUG November 12 for an al-in-French talk on Atmosphere (I means pure French from Québec). Then on November 16 I will talk about Atmosphere at the Prague JUG. Finally I will be in Anwerp for Devoxx 2009, presenting Atmosphere and our new Grizzly Servlet Container which support new OSGi Http Service spec.

See you in EU!

technorati:

Related Topics >> Blogs      Glassfish      
Comments
Comments are listed in date ascending order (oldest first)

Writing a RESTful and Comet based PubSub application using Atmosphere in less than 10 lines

Posted by jfarcand on November 3, 2009 at 12:58 PM PST

Writing a publisher/subscriber (PubSub) is quite simple with Atmosphere using the atmosphere-jersey module.

Boo

The main idea here is to use Comet for suspending the response when a client subscribe to a topic, and use REST for publishing messages to the those suspended responses. First, let’s bind our application to the root uri using the @path annotation

package org.atmosphere.samples.pubsub;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import org.atmosphere.annotation.Broadcast;
import org.atmosphere.annotation.Schedule;
import org.atmosphere.annotation.Suspend;
import org.atmosphere.cpr.Broadcaster;
import org.atmosphere.jersey.Broadcastable;

@Path("/") public class PubSub {

Next, let’s implement the subscribe operation by using the @Suspend and inject our Atmosphere’s Broadcaster using the @PathParam annotation:

    @Suspend
@GET
@Path("/{topic}")
@Produces("text/plain;charset=ISO-8859-1")
public Broadcastable subscribe(@PathParam("topic") Broadcaster topic) {
return new Broadcastable("",topic);
}

 

The code above will be invoked when we want to create a new topic (@Path("/{topic}"), we will return a Broadcastable instance which will tell Atmosphere to use the passed Broadcaster, who got injected using the @PathParam, when broadcasting {topic}. Finally, the underlying response will be suspended forever via @Suspend annotation. Next, let’s implement the publish operation:

    @GET
@Path("/{topic}/{message}")
@Produces("text/plain;charset=ISO-8859-1")
@Broadcast
public Broadcastable publish(@PathParam("topic") Broadcaster topic,
@PathParam("message") String message){

return new Broadcastable(message,topic);
}

To publish, we just need to send a uri that takes the form of "/{topic}/{message}" as defined by @Path("/{topic}/{message}") and again, the Broadcaster we want to use to broadcast messagse will be injected based on the {topic} value. Finally, we just return a Broadcastable object and let Atmosphere broadcast the value to all suspended connections. That’s it! Now we can see it in action by doing:

Create a topic
curl http://localhost:8080/atmosphere-pubsub/myAtmosphereTopic
Publish to that topic
curl http://localhost:8080/atmosphere-pubsub/myAtmosphereTopic/Atmosphere_is_cool

The source for the entire sample can be viewed here. Really simple, is it?

For any questions or to download Atmosphere, go to our main site and use our Nabble forum (no subscription needed) or follow us on Twitter and tweet your questions there!

technorati:

Related Topics >> Blogs      General      Web Applications      
Comments
Comments are listed in date ascending order (oldest first)

Atmosphere 0.4 Released

Posted by jfarcand on October 26, 2009 at 7:22 PM PDT

Atmosphere 0.4, the framework that allow the creation of RESTFul and Ajax Push/Comet applications, is ready for prime time. This release contains many new features and can be seen in action in many well-known frameworks.

First, I would like to thanks Paul Sandoz, Viktor Klang, Gerard Davison, Vlatko Davidobski, Hubert Iwaniuk, Bertrand Goetzmann,Cagatay Civici, Michal Hanti, A. Eduardo Garcia, Ransford Segu-Baffoe and Jim Driscoll for their contribution to that release!

Atmosphere's adoption continues with this release, starting with PrimeFaces' support for Ajax Push/Comet (event on the iPhone). You can also see Atmosphere in action within the Scala based Akka Project. Don't want to use Java? Why not using the Atmosphere Grails' plug in. Don't like Scala, Java and Groovy? Why not trying JRuby!

Boo

What's new since Atmosphere 0.3?

For any questions or to download Atmosphere, go to our main site and use our Nabble forum (no subscription needed) or follow us on Twitter and tweet your questions there!

technorati:

Related Topics >> Blogs      General      Web Applications      
Comments
Comments are listed in date ascending order (oldest first)

Congratulations!

Great work and Congratulations to all who contributed!!!

Atmosphere at Oracle OpenWorld Unconference

Posted by jfarcand on October 7, 2009 at 5:32 PM PDT

@jfarcand will talk about the Atmosphere Framework at the upcoming Oracle OpenWorld (October 13th Tuesday, 1-2pm). If you are around, come to learn what is Atmosphere and how you can write RESTful and Asynchronous Web (Comet/Ajax Push) application using Scala, JRuby, Java or Groovy!

IMG_0347.JPG

I will describe what is the Atmosphere Framework and its components, where the community is and how our next release 0.4 is going, demonstrate the framework power using JRuby, Scala, Java and Groovy (via the new Atmosphere Grails Plug In).  I will also talk about the role of the framework in akka (RESTFul Distributed Persistent Transactional Actors),  and conclude by transforming GlassFish V3 into an Atmosphere-OSGI-only Server. Sound like a lot of buzzwords, does it! If you can't come, just follow us on Twitter since everything about the framework and its evolution are twitted on line.

technorati:

Comments
Comments are listed in date ascending order (oldest first)

Grizzly 1.9.18 is out

Posted by jfarcand on September 14, 2009 at 10:13 AM PDT

We have been quiet for awhile with the monster...but we keep making improvements, and this week the team is proud to announce the GA availability of 1.9.18

IMG_0347.JPG

This version doesn't contains any new features as we have focused on performance and fixing bugs. The change logs can be found here. Note that we are more and more to work on the Grizzly Servlet Container and make it pass the Servlet 2.5 TCKs. Hopefully before the end of the year we will have it working on both Grizzly 1.9.x and 2.0.0! On The Grizzly 2.0.0 side, we have been swamped by the upcoming GlassFish v3 release all summer (hopefully Oracle allow us more resources :-))....but 1.9.18 should be the last one integrated in v3, hence we will resume our full time work on completing Grizzly 2.0.0. GlassFish v3 is a heavy user of the Grizzly HTTP Framework: ALL Scripting language support: JRuby, Python and Groovy, the Admin CLI, Monitoring/Management REST API (with the help of Jersey's GrizzlyAdapter), EJB WebServices, Java WebStart, etc.....

Finally, one interesting use of Grizzly can be seen with the Atmosphere Framework, which offer an end to end stack for writing Comet and REST applications with the help of Jersey.

For any questions or to download samples and tutorials, go to our main site and use our Nabble forum (no subscription needed) or follow us on Twitter and tweet your questions there!

technorati:

Related Topics >> Blogs      Java Enterprise      
Comments
Comments are listed in date ascending order (oldest first)