27

I have a webapp which requires the usage of Tomcat 7 web sockets.

In this webapp all standard Servlets (those extending javax.servlet.http.HttpServlet) work nicely (and correctly) with Google Guice. To make my Servlet work with Guice handlers I simply:

  1. decorate the servlet with @Singleton
  2. declare private Provider for MyHandler instance & generate a setter which is marked for injection
  3. decorate the Servlet's constructor with @Inject

Example to demonstrate points above:

@Singleton
public class MyServlet extends HttpServlet {

    private Provider<MyHandler> myHandler;

    @Inject
    MyServlet() {
    }

    @Override
    protected void service(..) throws ServletException { ... }

    @Inject
    public void setMyHandler(Provider<MyHandler> myHandler) {
        this.myHandler = myHandler;
    }

    ...
}

How can one call the same Guice handler, above called myHandler from a WebSocketServlet?

I can't adopt the same style as in the standard servlet use case because, rather than having a Singleton servlet as in the case of the standard servlets, each WebSocket communication results in an instance extending MessageInbound; then the appropriate method that would call MyHandler is called from a method (e.g. onOpen or onClose) within the MessageInbound instance; not from a method within an HttpServlet instance as MyServlet above.

What did I try? I did try some (conceptually wrong) solutions such as calling the websocket-servlet's handlers from within the MessageInbound instance; that of course results in scoping problems lower down the Guice stack trace. What is the conceptually correct way of doing this?

Joseph Victor Zammit
  • 14,760
  • 10
  • 76
  • 102
  • If I understand you, you override WebSocketServlet#createWebSocketInbound where you create a new instance of MessageInbound? If that is true, then simple just let Guice create the MessageInbound for you (assisted inject if needed). The only problem I see is with scoping of http request/responses as guice filter uses thread locals to keep track. But that can be compensated. Did I follow you correctly? – Alen Vrečko Jan 02 '13 at 22:17
  • thank you for your comment, but I didn't understand this part: "then simple just let Guice create the MessageInbound for you (assisted inject if needed)" – Joseph Victor Zammit Jan 08 '13 at 11:33

2 Answers2

1

Update after looking at GitHub example:

How you use Guice is just fine. Since there is just one particular usage of MessageInbound any sugar like with the AbstractGuiceWebSocketServlet is unnecessary. Provider chatLogHdlr and then doing manual construction is OK. But you lose AOP support. If that is needed you might want to do Assisted Inject. But for now this is fine.

On a side note, use construction injection instead of setter injection.

I saw immediately what is the problem. It is not Guice but rather how you use Guice-Persist. I didn't used GP a lot and still use the venerable Warp-persist. But I see 2 problems with how you use Guice-persist in your code:

  1. You need to inject the PersistService to start Guice-Persist. It is explained in the WIKI e.g.

    public class PocWebApp extends GuiceServletContextListener {
    
    @Inject
    PersistService ps;
    
    @Override
    protected Injector getInjector() {
        Injector injector = Guice.createInjector(new ServletModule() {
    
            @Override
            protected void configureServlets() {
                install(new JpaPersistModule("DesktopPU"));
                serve("/socket/main/").with(MainSocket.class);
            }
    
        });
        injector.injectMembers(this);
        return injector;
    }
    
    @Override
    public void contextInitialized(ServletContextEvent servletContextEvent) {
        super.contextInitialized(servletContextEvent);
        ps.start();
    }
    }
    
  2. The PersistFilter is useless as only the first time WebSocket will go trough filter but all subsequent communication will not go trough the filter. Using the txn just around @Transactional (Session-per-transaction) is the way to go.

Off-topic:

How many users do you intend to support? If this is going to be a hardcore chat server I'd use Netty instead but it is somewhat more involved. Googling found this:

http://comoyo.github.com/blog/2012/07/30/integrating-websockets-in-netty/

Original answer:

So this is a question about style?

WebSockets != Servlets. There is nothing wrong if they require a slightly different style. I'd even prefer to be reminded I am not dealing with vanilla servlets.

Some observations:

WebSocketServlet is nothing special. You can easily use it with Guice-Servlet. E.g.:

 @Singleton
 public class FooGuiceWebSocketServlet extends WebSocketServlet {...}

And then refernce it as

 serve("/foo").with(FooGuiceWebSocketServlet.class);

Now, MessageInbound that is special as is all handled by Tomcat as you explained. The MessageInbound is WebSocket scoped. Now Guice has no idea about this scope and it might make sense to leave it that way.

For starters I'd make sure MessageInbound is created by Guice. Something along this lines:

@Singleton
public class ExampleWebSocketServlet extends AbstractGuiceWebSocketServlet {

    @Override
    public Class<? extends StreamInbound> serveWith() {
        return Foo.class;
    }

    public static class Foo extends MessageInbound {

    @Inject GuiceCreatedAndInjected bar;

    @Override
    protected void onBinaryMessage(ByteBuffer byteBuffer) throws IOException {
        // do nothing
    }

    @Override
    protected void onTextMessage(CharBuffer charBuffer) throws IOException {
        // this getSwOutbonds() is not very friendly to testing
        getWsOutbound().writeTextMessage(bar.echo(charBuffer));
    }

   }
}

Where

public abstract class AbstractGuiceWebSocketServlet extends WebSocketServlet {

    @Inject Injector injector;

    @Override
    protected StreamInbound createWebSocketInbound(String subProtocol, HttpServletRequest request) {
        return injector.getInstance(serveWith());
    }

    public abstract Class<? extends StreamInbound> serveWith();

}

You can go from here to higher abstractions and/or scopings as needed. I don't particularly like #getWsOutbound() as it hinders testing.

Just keep on improving the style until you are satisfied. Say if you need more help (will modify answer).

Alen Vrečko
  • 886
  • 10
  • 13
  • Thanks for your answer. Issue is this is the simplest case; I need to keep track of connections and invidual connection IDs; take a look at a simple proof-of-concept to replicate the issue [on github](https://github.com/jvzammit/wspoc), and my `createWebSocketInbound` method in [MainSocket.java](https://github.com/jvzammit/wspoc/blob/master/wspoc/poc-web/src/main/java/com/cif/dt/app/websocket/MainSocket.java) where I set a `clientNo` to each opened websocket connection. Where do I do that in this structure you suggest? – Joseph Victor Zammit Feb 19 '13 at 11:27
  • I applied your recommended structure to my [proof of concept](https://github.com/jvzammit/wspoc) and got exactly the same `java.lang.IllegalStateException` as before. – Joseph Victor Zammit Feb 19 '13 at 12:34
  • Awesome stuff; I'll push my changes to github, based on your suggested fixes; it works! Many thanks! – Joseph Victor Zammit Feb 19 '13 at 14:51
  • Side note: `@Transactional` is used only at the lowest level, i.e. when interacting with the `EntityManager`. – Joseph Victor Zammit Feb 19 '13 at 14:58
  • Only issue: there's a side-effect to persist data changes effected by normal servlets; i.e. changes performed through standard servlets are not being committed to DB; not part of the proof-of-concept shown though. Therefore question is, how to use your suggested structure in the `GuiceServletConfig` to cater for both websocket and standard servlet usage? – Joseph Victor Zammit Feb 19 '13 at 17:59
  • Unfortunately I had to unmark this from correct. Reason being if I use the suggested structure, servlets do not persist correctly anymore; and I receive no exception. – Joseph Victor Zammit Feb 19 '13 at 18:34
  • I only said that PersistFilter is useless with WebSockets. I didn't said anything about usage with Servlets. By all means use PersistFilter with the Servlets. I trust you understand the difference between using this filter and @Transactional. – Alen Vrečko Feb 19 '13 at 22:57
  • I'm a Guice beginner; I'm more of a client-side dev but working on a full-stack project which already has a working Guice-based structure (for traditional servlets) as explained. If you can be more clear about what you're suggesting it would be beneficial. I'll mark this question as correct since you did answer *this* question. – Joseph Victor Zammit Feb 20 '13 at 08:45
  • 1
    The PersistFilter starts the transaction at the beginning of the request and commits it before response is returned. With [@Transactional] the transaction is started before the method enters and committed after the method returns. I very much prefer to mark transactions on business logic level than anywhere else. In your case ChatLogHandlerImpl is the business logic and I'd mark logChatMsg as [@Transactional] so both methods insert and refresh are done within the same transaction (if PersistFilter is not used, in case it is there are already with the same txn). No need refresh as id is set. – Alen Vrečko Feb 20 '13 at 10:48
  • Are there any side-effects to putting `@Transactional` at both `Handler` and `DAO` level within the current project structure? – Joseph Victor Zammit Feb 20 '13 at 11:23
  • You get **correct** transaction semantic on the business logic level, if not using the PersistFilter. With _@Transactional_ on the _Handler_ a single transaction will be started around this method call, any _@Transactional_ methods called (on the DAO) will continue using the single transaction. This way if you do DAO#insert DAO#delete DAO#update inside a single Handler method (annotated with _@Txn_) either all or no changes will be applied to the DB. If not marked _@Txn_ each DAO call will run in own transaction (given PersistFilter is not used). – Alen Vrečko Feb 20 '13 at 11:47
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/24827/discussion-between-josvic-zammit-and-alen-vrecko) – Joseph Victor Zammit Feb 20 '13 at 11:50
0

I didn't quite understand what you're trying to accomplish. I would look for AOP support in Guice. It seems that you need that MyHandler is set (inject) before being used in some methods of MessageInbound subclass' instance. An aspect could do that.

However, there is one question you need to ask: where is the (instantialization) control? If there's some way to add some kind of delegation in your Tomcat application configuration, Guice could "enhance" the MessageInbound instances which, in turn, would have the proper MyHandler set before use.

Robson França
  • 661
  • 8
  • 15