librelist archives

« back to archive

mojaha: mongrel2 java handler

mojaha: mongrel2 java handler

From:
Karl Ostendorf
Date:
2011-04-30 @ 20:49
Hi,

I've created a new implementation of a Java handler for Mongrel2 which
might be of interest to some of you out there.

https://github.com/kwo/mojaha

It uses request and response classes that are similar but not
identical to HttpServletRequest/Response. The handler itself simple
with only a few methods for receiving and sending.

Karl

Karl Ostendorf | <karl@ostendorf.com> | http://karl.ostendorf.com/

Re: mojaha: mongrel2 java handler

From:
Karl Ostendorf
Date:
2011-05-02 @ 18:10
I pushed some changes out today that eliminated the remaining major
issues and feel confident that mojaha has reached the 'good enough'
level. Would be happy to get any feedback from other users out there
who try it out.

A brief note as to why there is now a 2d Java handler for Mongrel2:
the original java handler looks very similar to the python handler
while mojaha aims for similarity, but not compatibility, to the
Servlet API.

Best Regards,
Karl Ostendorf


Karl Ostendorf | <karl@ostendorf.com>
+49 30 8353 58235 | http://ostendorf.com/



On Sat, Apr 30, 2011 at 22:49, Karl Ostendorf <karl@ostendorf.com> wrote:
> Hi,
>
> I've created a new implementation of a Java handler for Mongrel2 which
> might be of interest to some of you out there.
>
> https://github.com/kwo/mojaha
>
> It uses request and response classes that are similar but not
> identical to HttpServletRequest/Response. The handler itself simple
> with only a few methods for receiving and sending.
>
> Karl
>
> Karl Ostendorf | <karl@ostendorf.com> | http://karl.ostendorf.com/
>

Re: [mongrel2] Re: mojaha: mongrel2 java handler

From:
Armando Singer
Date:
2011-05-03 @ 00:02
On May 2, 2011, at 11:10 AM, Karl Ostendorf wrote:
> I pushed some changes out today that eliminated the remaining major
> issues and feel confident that mojaha has reached the 'good enough'
> level. Would be happy to get any feedback from other users out there
> who try it out.
> 
> A brief note as to why there is now a 2d Java handler for Mongrel2:
> the original java handler looks very similar to the python handler
> while mojaha aims for similarity, but not compatibility, to the
> Servlet API.

Hi Karl

Very cool. I took a quick look.

I actually thought about doing something similar when creating my
hanlder. Here's why I decided against a servlet-like API:

- Mongrel2 is not just for HTTP requests and responses. reply,
  deliver, replyJson, etc. don't form HTTP replies. How is this
  possible in your API? (try the chat example below using your API).

- Mongrel2 handles N:M message patterns. The servlet API is
  request/response based, and I thought it was sort of confusing to
  model after the servlet API because of this. It's possible to create
  multipe response objects, but it gets tedious.

- API-wise, there are a couple ways to do this. One is to create a
  bunch of overloaded methods to reply, deliver, deliverHTTP,
  deliverJson, etc, which is what I did (sort of the Java version of
  the python API, but we overload because we sadly don't have default
  and named args.)

  The other way is to have fewer methods in the API, and construct
  objects.

  I went with the many overloaded methods approach, because I strongly
  prefer the much simpler client usage if calling one method vs
  building up an object first then calling a method. It sort of moves
  the work to the API client IMO.

  Example:

  replyHTTP(req, body);

  or with everything:

  replyHTTP(req, body, "OK", ImmutableMap.of("header1", "header-val1"));

  vs:

  HTTPResponse resp = new HTTPResponse();
  resp.setBody(body);
  resp.setStatus("OK");
  resp.setHeader("header1", "header-val1");
  myHandler.send(req, resp);

  Replying to multiple connection id's gets even more verbose.

  Using a builder would clean this up a bit, but I prefer the 1 liner
  usage of overloaded methods.
 
  Example:

  CONN.deliverJson(req.getSender(), ids, data);

  vs...???

- Also, I stuck close to the reference python impl to 1) be 100%
  compatible in behavior, and, yes, 2) be as close to the reference
  impl in API as possible but while being idiomatic in Java.

  One of the cool things about mongrel2 is that it's possible to build
  apps in multiple languages and leverage tons of libraries. I might
  use a python handler to do mail stuff and a java handler to, say,
  perform lucene search. If the handler APIs between the python and
  Java handler are wildly different, it becomes a bit of a pain to
  use.

  That said, I think the micro-frameworks built on top of mongrel2 are
  awesome, but those are more than simple handler impls.

- Plan on handling tnetstrings?

- Something to consider--if you want to add a nice object model on top
  of a mongrel2 hander, and my impl convers 100% of the mongrel2
  handler functionality--why not try your object model out by wrapping
  my impl? I think I've handled all the OCD stuff :) And I'd love
  feedback if you run into roadblocks with my impl. Just a thought.

Chat example time. How would you implement the following Chat handler
using your HTTP based API? This is of course a toy impl, but API-wise
it's a good example.

public final class Chat {

  private static final String SENDER_ID = "82209006-86FF-4982-B5EA-D1E29E55D481";
  private static final Connection CONN = Handler.connection(SENDER_ID,
    "tcp://127.0.0.1:9999", "tcp://127.0.0.1:9998");
  private static final ConcurrentMap<String, Object> USERS = 
Maps.newConcurrentMap();
  
  public static void main(String[] args) {
    
    for (;;) {
      final Request req = CONN.recvJson();
      
      final Map<String, Object> data = req.getData();
      final Object type = data.get("type");
      if ("join".equals(type)) {
        CONN.deliverJson(req.getSender(), USERS.keySet(), data);
        USERS.put(req.getConnId(), data.get("user"));
        CONN.replyJson(req, ImmutableMap.of(
          "type", "userList",
          "users", USERS.values()
        ));
      } else if ("disconnect".equals(type)) {
        System.out.println("DISCONNECTED" + req.getConnId());
        final Object removedUser = USERS.remove(req.getConnId());
        final Map<String, Object> updateData = Maps.newLinkedHashMap(data);
        updateData.put("user", removedUser);
        CONN.deliverJson(req.getSender(), USERS.keySet(), updateData);
      } else if (!USERS.containsKey(req.getConnId())) {
        USERS.put(req.getConnId(), data.get("user"));
      } else if ("msg".equals(type)) {
        CONN.deliverJson(req.getSender(), USERS.keySet(), data);
      }
      
      System.out.println("REGISTERED USERS: " + USERS.size());
    }
  }
}

Hope you find the feedback useful. I'd be happy to get yours as well!

Cheers,
Armando

> 
> Best Regards,
> Karl Ostendorf
> 
> 
> Karl Ostendorf | <karl@ostendorf.com>
> +49 30 8353 58235 | http://ostendorf.com/
> 
> 
> 
> On Sat, Apr 30, 2011 at 22:49, Karl Ostendorf <karl@ostendorf.com> wrote:
>> Hi,
>> 
>> I've created a new implementation of a Java handler for Mongrel2 which
>> might be of interest to some of you out there.
>> 
>> https://github.com/kwo/mojaha
>> 
>> It uses request and response classes that are similar but not
>> identical to HttpServletRequest/Response. The handler itself simple
>> with only a few methods for receiving and sending.
>> 
>> Karl
>> 
>> Karl Ostendorf | <karl@ostendorf.com> | http://karl.ostendorf.com/
>> 

Re: [mongrel2] Re: mojaha: mongrel2 java handler

From:
Karl Ostendorf
Date:
2011-05-03 @ 08:05
Hi Armando,

Mojaha uses objects similar to those in the Servlet API but is not
Servlet API compatible, main reason is that we are not running inside
a servlet container.  So while the objects HttpRequest and
HttpResponse are similar to HttpServletRequest and HttpServletResponse
they don't implement all of the methods, and also add a few new ones
while doing away with the old stuff like Enumerations.

The HttpHandler allows sending a response to multiple requests (there
are no doGet, doPost, etc. methods):
handler.send(rsp, req1, req2, req3, ...).

Support for decoding/encoding netstrings is built-in. No extra class
just a function. It works.

I'll have to get back to you on the Chat example, all the building
blocks are there in the mojaha code, I just need a couple of hours to
bang out an example.

Cheers,



Karl Ostendorf | <karl@ostendorf.com>
+49 30 8353 58235 | http://karl.ostendorf.com/



On Tue, May 3, 2011 at 02:02, Armando Singer <armando.singer@gmail.com> wrote:
> On May 2, 2011, at 11:10 AM, Karl Ostendorf wrote:
>> I pushed some changes out today that eliminated the remaining major
>> issues and feel confident that mojaha has reached the 'good enough'
>> level. Would be happy to get any feedback from other users out there
>> who try it out.
>>
>> A brief note as to why there is now a 2d Java handler for Mongrel2:
>> the original java handler looks very similar to the python handler
>> while mojaha aims for similarity, but not compatibility, to the
>> Servlet API.
>
> Hi Karl
>
> Very cool. I took a quick look.
>
> I actually thought about doing something similar when creating my
> hanlder. Here's why I decided against a servlet-like API:
>
> - Mongrel2 is not just for HTTP requests and responses. reply,
>  deliver, replyJson, etc. don't form HTTP replies. How is this
>  possible in your API? (try the chat example below using your API).
>
> - Mongrel2 handles N:M message patterns. The servlet API is
>  request/response based, and I thought it was sort of confusing to
>  model after the servlet API because of this. It's possible to create
>  multipe response objects, but it gets tedious.
>
> - API-wise, there are a couple ways to do this. One is to create a
>  bunch of overloaded methods to reply, deliver, deliverHTTP,
>  deliverJson, etc, which is what I did (sort of the Java version of
>  the python API, but we overload because we sadly don't have default
>  and named args.)
>
>  The other way is to have fewer methods in the API, and construct
>  objects.
>
>  I went with the many overloaded methods approach, because I strongly
>  prefer the much simpler client usage if calling one method vs
>  building up an object first then calling a method. It sort of moves
>  the work to the API client IMO.
>
>  Example:
>
>  replyHTTP(req, body);
>
>  or with everything:
>
>  replyHTTP(req, body, "OK", ImmutableMap.of("header1", "header-val1"));
>
>  vs:
>
>  HTTPResponse resp = new HTTPResponse();
>  resp.setBody(body);
>  resp.setStatus("OK");
>  resp.setHeader("header1", "header-val1");
>  myHandler.send(req, resp);
>
>  Replying to multiple connection id's gets even more verbose.
>
>  Using a builder would clean this up a bit, but I prefer the 1 liner
>  usage of overloaded methods.
>
>  Example:
>
>  CONN.deliverJson(req.getSender(), ids, data);
>
>  vs...???
>
> - Also, I stuck close to the reference python impl to 1) be 100%
>  compatible in behavior, and, yes, 2) be as close to the reference
>  impl in API as possible but while being idiomatic in Java.
>
>  One of the cool things about mongrel2 is that it's possible to build
>  apps in multiple languages and leverage tons of libraries. I might
>  use a python handler to do mail stuff and a java handler to, say,
>  perform lucene search. If the handler APIs between the python and
>  Java handler are wildly different, it becomes a bit of a pain to
>  use.
>
>  That said, I think the micro-frameworks built on top of mongrel2 are
>  awesome, but those are more than simple handler impls.
>
> - Plan on handling tnetstrings?
>
> - Something to consider--if you want to add a nice object model on top
>  of a mongrel2 hander, and my impl convers 100% of the mongrel2
>  handler functionality--why not try your object model out by wrapping
>  my impl? I think I've handled all the OCD stuff :) And I'd love
>  feedback if you run into roadblocks with my impl. Just a thought.
>
> Chat example time. How would you implement the following Chat handler
> using your HTTP based API? This is of course a toy impl, but API-wise
> it's a good example.
>
> public final class Chat {
>
>  private static final String SENDER_ID = "82209006-86FF-4982-B5EA-D1E29E55D481";
>  private static final Connection CONN = Handler.connection(SENDER_ID,
>    "tcp://127.0.0.1:9999", "tcp://127.0.0.1:9998");
>  private static final ConcurrentMap<String, Object> USERS = 
Maps.newConcurrentMap();
>
>  public static void main(String[] args) {
>
>    for (;;) {
>      final Request req = CONN.recvJson();
>
>      final Map<String, Object> data = req.getData();
>      final Object type = data.get("type");
>      if ("join".equals(type)) {
>        CONN.deliverJson(req.getSender(), USERS.keySet(), data);
>        USERS.put(req.getConnId(), data.get("user"));
>        CONN.replyJson(req, ImmutableMap.of(
>          "type", "userList",
>          "users", USERS.values()
>        ));
>      } else if ("disconnect".equals(type)) {
>        System.out.println("DISCONNECTED" + req.getConnId());
>        final Object removedUser = USERS.remove(req.getConnId());
>        final Map<String, Object> updateData = Maps.newLinkedHashMap(data);
>        updateData.put("user", removedUser);
>        CONN.deliverJson(req.getSender(), USERS.keySet(), updateData);
>      } else if (!USERS.containsKey(req.getConnId())) {
>        USERS.put(req.getConnId(), data.get("user"));
>      } else if ("msg".equals(type)) {
>        CONN.deliverJson(req.getSender(), USERS.keySet(), data);
>      }
>
>      System.out.println("REGISTERED USERS: " + USERS.size());
>    }
>  }
> }
>
> Hope you find the feedback useful. I'd be happy to get yours as well!
>
> Cheers,
> Armando
>
>>
>> Best Regards,
>> Karl Ostendorf
>>
>>
>> Karl Ostendorf | <karl@ostendorf.com>
>> +49 30 8353 58235 | http://ostendorf.com/
>>
>>
>>
>> On Sat, Apr 30, 2011 at 22:49, Karl Ostendorf <karl@ostendorf.com> wrote:
>>> Hi,
>>>
>>> I've created a new implementation of a Java handler for Mongrel2 which
>>> might be of interest to some of you out there.
>>>
>>> https://github.com/kwo/mojaha
>>>
>>> It uses request and response classes that are similar but not
>>> identical to HttpServletRequest/Response. The handler itself simple
>>> with only a few methods for receiving and sending.
>>>
>>> Karl
>>>
>>> Karl Ostendorf | <karl@ostendorf.com> | http://karl.ostendorf.com/
>>>
>
>

Re: [mongrel2] Re: mojaha: mongrel2 java handler

From:
Armando Singer
Date:
2011-05-03 @ 17:09
On May 3, 2011, at 1:05 AM, Karl Ostendorf wrote:

> Hi Armando,
> 
> Mojaha uses objects similar to those in the Servlet API but is not
> Servlet API compatible, main reason is that we are not running inside
> a servlet container.  So while the objects HttpRequest and
> HttpResponse are similar to HttpServletRequest and HttpServletResponse
> they don't implement all of the methods, and also add a few new ones
> while doing away with the old stuff like Enumerations.

Yup. I had seen that you were trying to be similar to the servlet API,
but not exactly the same. My argument is that the API is verbose and
that the HTTPX classes don't handle, or are not named well for
non-HTTP reply and deliver scenarios. The servlet API works well for
simple request/response, and one doesn't even need to create the
objects because they are passed in as a callback by the container.

> 
> The HttpHandler allows sending a response to multiple requests (there
> are no doGet, doPost, etc. methods):
> handler.send(rsp, req1, req2, req3, ...).

Yup. I had seen that you had a varargs method to handle sending to a
response to, uh...multiple "requests".

I'll fully type out my example (I didn't write out the full deliver
example because I was illustrating that it's verbose).

 Example:

 replyHTTP(req, body);

 or with everything:

 replyHTTP(req, body, "OK", ImmutableMap.of("header1", "header-val1"));

 vs servlet-like:

 HttpRequest req = this.handler.recv();

 HTTPResponse resp = new HTTPResponse();
 resp.setBody(body);
 resp.setStatus("OK");
 resp.setHeader("header1", "header-val1");
 myHandler.send(resp, req);

 Replying to multiple connection id's gets even more verbose.

 Using a builder would clean this up a bit, but I prefer the 1 liner
 usage of overloaded methods.

 Example:

 CONN.deliverJson(req.getSender(), ids, data);

 vs servlet-like:

 HttpRequest req1 = this.handler.recv();

 HTTPResponse resp = new HTTPResponse();
 resp.setBody(body);
 resp.setStatus("OK");
 resp.setHeader("header1", "header-val1");

 HttpRequest req2 = new HttpRequest();
 req2.setId(id2); // ??

 HttpRequest req3 = new HttpRequest();
 req2.setId(id3); // ??

 myHandler.send(resp, req1, req2, req3);
 // but this still isn't the same as non-http deliverJson...

My point was that, when I was thinking about doing something similar,
I didn't like like having to create objects like this when 1 line
method calls are so simple to work with. It also makes more sense when
doing both simple request/reply and deliver (N:M) scenarios.

Also, I found it a bit weird that one response is created by the
handler with handler.recv(), but the rest you need to create manually
to deliver a message to multiple clients. And it's a bit weird that
you are sending messages to multiple "HTTPRequests". You really want
to send it to multiple ids. The name abstraction is a bit weird.

Also, the your classes are called HTTPRequests and HTTPResponse (like
the servlet API), but handlers should handle non-http deliver,
deliverJson, replyJson. So it's unclear in your API how you'd do
deliverJson. Or you'd have to use classes called HTTPRequest for
non-HTTP replies?
  
Hope that makes sense. My comments were both on API design as well as
how to support other scenarios that handlers are meant to handle, like
the chat example, or streaming mp3.

> 
> Support for decoding/encoding netstrings is built-in. No extra class
> just a function. It works.

Yup. I saw that you parse netstrings. I'm was talking about
tnetstrings, which handlers are now to implement to remain fully
compatible with mongrel2. Have you seen http://tnetstrings.org/ ?

(Yup, mine was just a trivial function until tnetstrings. I created a
TNetstrings class to handle the full codec for parsing/dumping. You
can use my impl :)

> 
> I'll have to get back to you on the Chat example, all the building
> blocks are there in the mojaha code, I just need a couple of hours to
> bang out an example.

Oh, the chat example is trivial. It should hopefully take a couple
minutes to write out a non-compiling version just to illustrate the API
usage. Just transliterate my example or the python chat example to use
your API.

Hope that helps!

Cheers,
Armando

> 
> Cheers,
> 
> 
> 
> Karl Ostendorf | <karl@ostendorf.com>
> +49 30 8353 58235 | http://karl.ostendorf.com/
> 
> 
> 
> On Tue, May 3, 2011 at 02:02, Armando Singer <armando.singer@gmail.com> wrote:
>> On May 2, 2011, at 11:10 AM, Karl Ostendorf wrote:
>>> I pushed some changes out today that eliminated the remaining major
>>> issues and feel confident that mojaha has reached the 'good enough'
>>> level. Would be happy to get any feedback from other users out there
>>> who try it out.
>>> 
>>> A brief note as to why there is now a 2d Java handler for Mongrel2:
>>> the original java handler looks very similar to the python handler
>>> while mojaha aims for similarity, but not compatibility, to the
>>> Servlet API.
>> 
>> Hi Karl
>> 
>> Very cool. I took a quick look.
>> 
>> I actually thought about doing something similar when creating my
>> hanlder. Here's why I decided against a servlet-like API:
>> 
>> - Mongrel2 is not just for HTTP requests and responses. reply,
>>  deliver, replyJson, etc. don't form HTTP replies. How is this
>>  possible in your API? (try the chat example below using your API).
>> 
>> - Mongrel2 handles N:M message patterns. The servlet API is
>>  request/response based, and I thought it was sort of confusing to
>>  model after the servlet API because of this. It's possible to create
>>  multipe response objects, but it gets tedious.
>> 
>> - API-wise, there are a couple ways to do this. One is to create a
>>  bunch of overloaded methods to reply, deliver, deliverHTTP,
>>  deliverJson, etc, which is what I did (sort of the Java version of
>>  the python API, but we overload because we sadly don't have default
>>  and named args.)
>> 
>>  The other way is to have fewer methods in the API, and construct
>>  objects.
>> 
>>  I went with the many overloaded methods approach, because I strongly
>>  prefer the much simpler client usage if calling one method vs
>>  building up an object first then calling a method. It sort of moves
>>  the work to the API client IMO.
>> 
>>  Example:
>> 
>>  replyHTTP(req, body);
>> 
>>  or with everything:
>> 
>>  replyHTTP(req, body, "OK", ImmutableMap.of("header1", "header-val1"));
>> 
>>  vs:
>> 
>>  HTTPResponse resp = new HTTPResponse();
>>  resp.setBody(body);
>>  resp.setStatus("OK");
>>  resp.setHeader("header1", "header-val1");
>>  myHandler.send(req, resp);
>> 
>>  Replying to multiple connection id's gets even more verbose.
>> 
>>  Using a builder would clean this up a bit, but I prefer the 1 liner
>>  usage of overloaded methods.
>> 
>>  Example:
>> 
>>  CONN.deliverJson(req.getSender(), ids, data);
>> 
>>  vs...???
>> 
>> - Also, I stuck close to the reference python impl to 1) be 100%
>>  compatible in behavior, and, yes, 2) be as close to the reference
>>  impl in API as possible but while being idiomatic in Java.
>> 
>>  One of the cool things about mongrel2 is that it's possible to build
>>  apps in multiple languages and leverage tons of libraries. I might
>>  use a python handler to do mail stuff and a java handler to, say,
>>  perform lucene search. If the handler APIs between the python and
>>  Java handler are wildly different, it becomes a bit of a pain to
>>  use.
>> 
>>  That said, I think the micro-frameworks built on top of mongrel2 are
>>  awesome, but those are more than simple handler impls.
>> 
>> - Plan on handling tnetstrings?
>> 
>> - Something to consider--if you want to add a nice object model on top
>>  of a mongrel2 hander, and my impl convers 100% of the mongrel2
>>  handler functionality--why not try your object model out by wrapping
>>  my impl? I think I've handled all the OCD stuff :) And I'd love
>>  feedback if you run into roadblocks with my impl. Just a thought.
>> 
>> Chat example time. How would you implement the following Chat handler
>> using your HTTP based API? This is of course a toy impl, but API-wise
>> it's a good example.
>> 
>> public final class Chat {
>> 
>>  private static final String SENDER_ID = 
"82209006-86FF-4982-B5EA-D1E29E55D481";
>>  private static final Connection CONN = Handler.connection(SENDER_ID,
>>    "tcp://127.0.0.1:9999", "tcp://127.0.0.1:9998");
>>  private static final ConcurrentMap<String, Object> USERS = 
Maps.newConcurrentMap();
>> 
>>  public static void main(String[] args) {
>> 
>>    for (;;) {
>>      final Request req = CONN.recvJson();
>> 
>>      final Map<String, Object> data = req.getData();
>>      final Object type = data.get("type");
>>      if ("join".equals(type)) {
>>        CONN.deliverJson(req.getSender(), USERS.keySet(), data);
>>        USERS.put(req.getConnId(), data.get("user"));
>>        CONN.replyJson(req, ImmutableMap.of(
>>          "type", "userList",
>>          "users", USERS.values()
>>        ));
>>      } else if ("disconnect".equals(type)) {
>>        System.out.println("DISCONNECTED" + req.getConnId());
>>        final Object removedUser = USERS.remove(req.getConnId());
>>        final Map<String, Object> updateData = Maps.newLinkedHashMap(data);
>>        updateData.put("user", removedUser);
>>        CONN.deliverJson(req.getSender(), USERS.keySet(), updateData);
>>      } else if (!USERS.containsKey(req.getConnId())) {
>>        USERS.put(req.getConnId(), data.get("user"));
>>      } else if ("msg".equals(type)) {
>>        CONN.deliverJson(req.getSender(), USERS.keySet(), data);
>>      }
>> 
>>      System.out.println("REGISTERED USERS: " + USERS.size());
>>    }
>>  }
>> }
>> 
>> Hope you find the feedback useful. I'd be happy to get yours as well!
>> 
>> Cheers,
>> Armando
>> 
>>> 
>>> Best Regards,
>>> Karl Ostendorf
>>> 
>>> 
>>> Karl Ostendorf | <karl@ostendorf.com>
>>> +49 30 8353 58235 | http://ostendorf.com/
>>> 
>>> 
>>> 
>>> On Sat, Apr 30, 2011 at 22:49, Karl Ostendorf <karl@ostendorf.com> wrote:
>>>> Hi,
>>>> 
>>>> I've created a new implementation of a Java handler for Mongrel2 which
>>>> might be of interest to some of you out there.
>>>> 
>>>> https://github.com/kwo/mojaha
>>>> 
>>>> It uses request and response classes that are similar but not
>>>> identical to HttpServletRequest/Response. The handler itself simple
>>>> with only a few methods for receiving and sending.
>>>> 
>>>> Karl
>>>> 
>>>> Karl Ostendorf | <karl@ostendorf.com> | http://karl.ostendorf.com/
>>>> 
>> 
>>