Hacker News new | past | comments | ask | show | jobs | submit login
The Modern Java Ecosystem (for the Sinatra or web.py lover) (arantaday.com)
127 points by smanek on Dec 2, 2011 | hide | past | web | favorite | 68 comments



For those looking for something more like Sinatra in Java, check out webbit: https://github.com/joewalnes/webbit

Here's an example of a simple Websocket chat server: https://gist.github.com/1421652

Disclaimer: It's an open source project that I have some commits to


Webbit looks much more like embedded Jetty and plain servlets than Sinatra. If you really want something like Sinatra in you Java project, wire in Scala and try Scalatra.


>Disclaimer: It's an open source project that I have some commits to

Is that like a 'full disclosure' notice or are we supposed to be terrified of your code?


We use JAX-RS as a primary web framework in our current project and honestly I can't get happier! We use Guava for low-level basic stuff and Google GSON for JSON serialization. It seems to be on par with Jackson from ease-of-use point of view:

MyType myVar = gson.fromJson(jsonString);

JPA on a back, some basic IoC in the middle (nothing fancy). Mockito and jUnit for tests.

All in all it's just a bunch of small layered classes with some bits of annotations and no xml. Well, one web.xml file with single servlet declaration for JAX-RS :)

With good IDE (they all are great this days), Maven for dependencies, well-known practices and modern APIs Java isn't that scary or cumbersome anymore. It's just a nice small language. A bit verbose without closures and type inference but still Ok.

I should note, though, that we generally use our Java layer as a service provider. We keep main bits of logic on a client side in JavaScript and call Java either for transactions and data, or in cases when something is hard to do on a client. It's just easier that way.


Be careful with GSON. It by-passes your getter and setter.

That alone creates a bit of mismatch because JAXB (which is used by JAX-RS) uses getter/setter by default unless you specified it otherwise.

And sometime you do have some logic in your getter/setter for validation or other purposes.

I got bitten by this before while it seems like a small thing it's actually a bit problematic.

If you're using JPA2, take a look at Spring-Data (formerly Hades). Spring-Data helps you to reduce JPA boilerplate code.

The way Spring-Data works is by using a convention: specify your NamedQuery and a Java interface with method name == NamedQuery name. Then magic suddenly happens.


I found Jackson is better than GSON at deserializing nested standard object graph automatically. GSON needs me to define all the nested structures.


I'm not a big fan of the heavy use of annotations. When an annotation does something other than what I expected it becomes a pain in the butt to figure out why. Also, back in the day, jersey and guice didn't get along because their use of annotations on the same methods conflicted because they both wanted to drive the bus so to speak.

This has been resolved, but it left a bad taste in my mouth because there isn't a good solution other than waiting for them to fix it.


Annotations are a huge and important part of Java.


and I pointed out the headaches they caused me. I'm not really sure what point you are trying to make.


Conflicts in any piece of software are a pain in the butt. That is part of software development though.

You say you aren't a big fan of 'heavy use of annotations' but you don't qualify what 'heavy' is so I am guessing that it is pretty much all annotations.

How do you make your determination of what annotations are 'ok' and what annotations aren't? Or do you say something like 'oh, I won't use that annotation because it might blow up on me at some later date'? What is your mental process there? Is @GET good or is @POST bad? What about @Inject or @JsonProperty?

The 'annotations leave a bad taste in my mouth' statement seems kind of odd to me. Generally, a language feature isn't something that I'm careful about avoiding. I could see making a statement like "I tried to use software product XYZ and it was so full of bugs that I switched to software product ABC."

But annotations aren't a software product, they are a language feature that products can take advantage of. It just happens to be that two products you were using had conflicting annotations. No big deal, you still have choices as you can just swap out products. That in itself doesn't make the functionality that annotations provide taste bad.

I've met people who have had this view of annotations as being some magical creature. They didn't understand them or how they worked at all. They stuck it in their heads that 'annotations are bad', so we aren't going to use them and aren't going to even bother learning how they might help us. Everything was very black and white. I found this behavior really odd because everything in software development is grey.


There are many features in many languages that look superficially benign (that's why they were added to the language in the first place), but that turn out to be so much trouble that people actively steer away from them. Some are just hard to use correctly, and will blow up in your face in unexpected ways. Others are impossible for a non-expert to use at all, and will make it much harder for others to work on the code. Some will have such awful tool support that using the feature is just to painful. Yet others will work fine in toy programs, but just won't scale to large programs.

It'd be silly to not dislike these features just because somebody thought that they would be a good idea, without being fully prescient about how the feature would be used in the wild.


I'm not really sure I understand your response because I can't tell if you are for or against annotations. But, I guess this begs the question... if it isn't done as annotations, what would the same functionality be? Spring's XML? Json? Just more coding?


I don't have an opinion on annotations either way, since the last Java I used was 1.4. I just found this idea of yours very odd: "Generally, a language feature isn't something that I'm careful about avoiding". That doesn't match my experience at all, people avoid specific language features (and with reason) all the time.


Got a specific example? I can't think of any that I avoid. I'm primarily in Java, CoffeeScript/JS these days. Maybe it is different for other languages?

Certainly, there are language features that I don't use, because I don't need them in what I do, but that is different than purposely avoiding something.


Sure. Start off with a couple from C++:

- Everyone should avoid global static initialization of complex data, even though it's the most convenient way to achieve certain effects. The chances of screwing up something with the initialization order of variables are just too high, and impossible to control. - There are people who like C++ template metaprogramming. Some very cool things can be achieved with it. But all C++ projects that I've seen ban it. Some because they want new people to be able to easily pick up the project. Others just because of the tool issues around heavy duty template use, like slow compilation speed and horrible error messages. - C++ has many different kinds of inheritance. In practice many projects will ban most of them, and only allow single public inheritance of bases classes with implementation, and single or multiple public inheritance of base classes with only virtual methods. The convenience of multiple implementation inheritance is just not viewed as sufficient to balance the risk of shooting yourself in the foot. And the utility of non-public inheritance is not viewed as high enough to balance needing to explain it to people coming from Java.

Or how about Perl?

- Anybody suggesting changing $[, the variable that controls the array indexing base, would generally be given a good thrashing. Allowing changing the language (at runtime!) from 0-based indexing to 1-based was presumably thought to be a benign feature at some point, but it's hard to see how in retrospect. - As a more modern example, later Perl got the ability to embed executable Perl code inside regular expressions with (??{}). Again, it's totally possible to see uses for this. But in practice it's so inscrutable (and at least in the past so crashy), that it's best reserved for stunt coding. Maybe not using it means rewriting some extremely compact regular expression in a way that's 3x longer, but that's still a net win.

Does that help showing the flavor of things you'd want to avoid?


Very good points that I can't disagree with at all. However, those are two languages I try to avoid entirely, not just partially. ;-)


"You say you aren't a big fan of 'heavy use of annotations' but you don't qualify what 'heavy' is so I am guessing that it is pretty much all annotations."

The problem is that guice is using these annotations to wire things together, but so was jax-rs. It's been over a year so I don't remember the exact problem I was having, but the short version is that annotations don't compose well and when two different frameworks require you to place annotations on the same class/method, and then they conflict with one another, it because very hard to fix.

This aligns with my feelings: http://www.nofluffjuststuff.com/blog/brian_gilstrap/2010/02/...


That is a great blog posting.

However, I don't know why the author would even suggest that annotations are innocent looking. They aren't any more 'innocent' than an if statement. Something like: 'if (myMethod())' could easily be a long running database query or call out to a fibonacci solver. Should we all be afraid of using if statements too?

I'm sorry, but there is really nothing 'magical' about annotations. It is simply a marker on a class, property or method that other code can read from and do things with. Prior to annotations, apps were processing Javadoc (@see early versions of GWT), which was an even more terrible idea since you didn't know if it was Javadoc or an annotation.

The summary is that a) software engineering is not easy and it does involve knowing what you are doing in order for things to not appear 'magical'. b) If you are going to put an annotation on something, then you should know what code is going to read that annotation and what effects it might have. That said, that isn't the fault of the annotation language feature.


My argument is that annotations don't compose well, interact in complex ways that are hard to reason about and debug, and that this is worse than non annotation based methods used in other languages.

Your response seems to be that one needs to understand the complex interactions of annotations before using them, therefore annotations are fine.

That's a non sequitur.


I disagree with your argument. To you, they are all of those things. To me, they are great and none of those things. That is all I'm saying.


> I'm sorry, but there is really nothing 'magical' about annotations. It is simply a marker on a class, property or method that other code can read from and do things with.

That is the magic. You escape your default programming paradigm of sequential statements and start doing metaprogramming.

The reason there are annotations is that the java language wasn't capable of doing certain things without annotations. Some of us think that java shouldn't do these things.


For web, Play Framework and Japid template are very good.

For distributed clustering, Hazelcast is amazingly good.

For massively scalable network services, Netty does a fantastic job.


Since we're on the subject of Play, I'm going to add Akka to this list. In my experience, programming for concurrency (and even distributed concurrency) is just way more enjoyable using Akka.


I'd never heard of Japid before. Thanks for pointing it out.

That said, using a backtick character like that seems like a really error prone approach. Definitely not my cup of tea.


That's what I thought as well when I first saw the backtick, but after using it for a while I found it to be perfect.

It's rarely used in any other context (besides Lisp). It's really unobtrude, making the rest of the code standing out. It's at an easy to remember place at the keyboard. The parser is good enough to catch any quote or backquote mistakes. It's really an non-issue and you forget about it after a while.

A sample,

    <ul>
    `    for (a : list)
             <li>$a  
    `
    </ul>


This is very cool. I am tied to Java b/c of Lucene and the Hadoop ecosystem as well. Some more things I would like to see:

-Templating. Something like SimpleTemplate Engine in Bottlepy. http://bottlepy.org/docs/dev/stpl.html

-How do I get rid of Tomcat at least for development. I would like to run java -jar MyApp.jar -Dport=8080 and get a live running webapp in my dev environment.


You can do "java -jar MyApp.jar -Dport=8080" with both Tomcat and Jetty using what they call "embedded mode". I like the Jetty embedded mode much more because it starts up so much faster. The Tomcat embedded mode does have the benefit of an easier to understand API, but it's not worth the speed trade-off for me.

Here's examples of both:

http://www.benmccann.com/dev-blog/embedded-jetty/

http://www.benmccann.com/dev-blog/embedded-tomcat/

I put together an example and uploaded it to GitHub. I'd love feedback on it:

https://github.com/benmccann/sprightly

P.S. Closure templates are cool because they can be used both client-side and server-side. I'm not aware of other templating languages that have this feature. I haven't used it much yet, but want to experiment with it more when I get time.


The list of technologies is really intimidating for outsiders. Its not like "Oh, XYZ, thats just Django templating language". When I see Struts and Hibernate, I know those things as gigantic opaque stuff that will take a long time to learn...


> How do I get rid of Tomcat at least for development.

Jetty everything. You can even keep it for production.


Take a look at Jetty (http://www.eclipse.org/jetty/) for the second option.

I tend to use 'mvn tomcat:run' to run my dev server, which I find even easier, fwiw.


Since everyones already covered the Jetty side of it, I personally like Freemarker as a templating language.

http://freemarker.sourceforge.net/

It's pretty simple and straigtforward, its what I use instead of JSP.

Also, if you're doing web stuff, I HIGHLY recommend sitemesh.

http://www.sitemesh.org/

It's a really clean way of implementing layouts and everytime I use other languages like Python, I always try and find equivalents but am always disappointed.


ruby on rails has sitemesh as its layout system. i agree, it is a great pattern for building web apps.


I've been using a very little know (and poorly named) template engine called Cambridge Template Engine (http://code.google.com/p/cambridge/) for my latest company.

The documentation is a bit thin, but the code is rock solid and very fast.

Combined with JEXL for an expression language, this is one of the best server side engines I've ever used.


>Javascript (which is basically just a Lisp with infix syntax ;-))

No, it's not.


To elaborate, JavaScript doesn’t support macros, which allow arbitrarily powerful semantic abstraction. And JavaScript does not have a regular syntax, so even if it did have macros, you would have to special-case each type of syntax (curly braces, statements, post-++ operators within lines) and handle them specifically. First-class functions and closures do not a Lisp make.


In general, the code is still impressively verbose. OTOH, it's incredibly short for Java code. I suspect that line/feature added count will increase more quickly than it would with Python or Ruby and it will quickly become a nightmare scenario indistinguishable from a more traditional Java web approach.


It may be slightly more verbose but you have to consider that the Java variant contains much more information in a single location. For example having a single file with methods annotated with @GET or @POST is so much cleaner to me than having routes.rb open in another editor window constantly Ctrl-Fing through it to sort out whats going on.


> For example having a single file with methods annotated with @GET or @POST is so much cleaner to me than having routes.rb open in another editor window

URL mapping is a framework detail, not a language detail — although the framework can be limited by the language. In Sinatra you'd write:

    get 'someroute' do
        # do stuff
    end
http://www.sinatrarb.com/intro.html#Routes

each style (these and half a dozen others) has its advantages and inconvenients. For instance, separate urls mapping (Rails, Django) give a starting point laying out the structure of the site and make views/controllers easier to reuse (as they're not coupled to URLs)[0], whereas annotating the handler directly gives a better view of where they're involved and how.

[0] They also make it simpler to "graft" sub-sites in


I think that's a matter of preference.

I don't mind both styles (after working with Spring MVC 3, Servlet 3, JAX-RS, and Rails)


How would these be any shorter in Python or Ruby?

  @GET
  public Collection<Integer> listStudentIds() {
    return STUDENTS.keySet();
  }
or

  @DELETE
  @Path("{uid: [0-9]+}")
  public boolean deleteStudent(@PathParam("uid") int uid) {
    final Student deletedStudent = STUDENTS.remove(uid);

    if (deletedStudent == null) {
      throw new WebApplicationException(Response.Status.NOT_FOUND);
    } else {
      return true;
    }
  }
I'll grant that Java programs often become more verbose than their Python/Ruby counterparts - but I would argue that trait can be held to a minimum with the proper libraries and coding style.


First example, Python (keeping a similar API):

    list_student_ids = get(STUDENTS.keys) # [0]
second example, Python:

    @delete
    @path(r'{uid: [0-9]+}')
    def delete_student(uid):
        if STUDENTS.pop(uid, None): # [0]
            return True
        raise WebApplicationException(Response.Status.NOT_FOUND)
the last line generally wouldn't be this complex, in Flask you'd just call `flask.abort(404)` (which can be used inline with an `or`, as with waffle_ss's ruby example, but that's not usual/good Python style), in Django it'd be `raise Http404()`.

Although in Django you'd really use the `get_object_or_404` shortcut for ORM objects:

    def delete_student(uid):
        get_object_or_404(StudentModel, pk=uid).delete()
        return True
And you wouldn't bother with the return since a 200 result would mean the deletion was correctly executed.

[0] Used Python's MutableMapping API here, it works slightly differently than Java's equivalent Map interface: `.keys()` returns a sequence of the mapping's keys and `.pop()` asks for a default value to return in case nothing was found there, otherwise it raises `KeyError` if the key was not found)


An other note: the @path decorator would probably use named groups instead of some sort of DSL, so

    r'(?P<uid>[0-9]+)'
(the `r` prefix is for `r`awstring, so the developer does not have to string-escape each backslash used in the regex among other things)


In Ruby, everything evaluates to true except false and nil. Also, the last statement executed is your return value. Therefore your first function could be written as

  def delete_student(uid)
    STUDENTS.remove(uid) || raise WebApplicationException.new(Response.Status.NOT_FOUND)
  end


I have no idea what the annotations mean, but if we use exceptions for transmitting error conditions, then there is no point having a true | exception function.

    public void deleteStudent(int uid) {
      if (STUDENTS.remove(uid) == null) {
        throw new WebApplicationException(Response.Status.NOT_FOUND);
      }
    }


I love it!

And that is the reason I still write in Python or Lisp when I don't need something (Hadoop, Lucene, etc) from the JVM ;-)


If you need something from the JVM and want to write Lisp, use Clojure. Checkout Hadoop extensions in Clojure at https://github.com/jblomo/oddjob .


In that case it is preferable to use "or" over "||" since then you do not have to worry so much about operator precedence because "or" has about the weakest of all operators. This means in this case that you can skip the explicit ".new" for the exception.

  def delete_student(uid)
    STUDENTS.remove(uid) or raise WebApplicationException, Response.Status.NOT_FOUND
  end


I seem to remember this same feature causing lots of headache in C in the form of subtle bugs mostly the result of this being used in awkward ways.

Personally I'd rather have a distinct boolean value to test for, the java version is much more explicit as to exactly what its doing.


Hmm can't say I've ever encountered something like that in Ruby so far. However, if you want a strict boolean it's pretty easy to get:

  !!STUDENTS.remove(uid)


In Ruby, you don't write code, code writes you! ;-)


You could make it shorter by using something like sinatra[1]. Here is my pseudo-sinatra code.

  get "/list_student_ids" do
    STUDENTS.key_set
  end

  delete "/delete_student/:uid" do
    STUDENTS.remove(params[:uid]) or raise WebApplicationException, Response::Status::NOT_FOUND
    true
  end
1. http://www.sinatrarb.com/


Your savings here are mainly from removing types. That's another matter entirely.


1. Thing's most of those types provide no value in being spelled out like that. The ones on the methods have documentary advantages, but that's about it, the rest is magical invocations to make a stupid compiler happy.

2. There's also a gain from the framework being able to map things to each other without being told explicitly. `@PathParam("uid")` for instance, it's only there because the framework has no way to match the parameter name `uid` to the URL-extracted parameter `uid`. Likewise with HTTP methods being part of the routing instead of a separate annotation, in part.

3. Java's APIs and type system, such as implicit nulls and not being able to use arbitrary types in boolean contexts


I would say they are mainly from removing the need for decorators due to there being a nice lambda function/block syntax. So we can define methods like "get" and "delete" which does the job of the decorators, creating something kind of like a mini-DSL.

Some savings through the removal of types, sure, but that is not what I personally see as most import here.


With Ruby folks can always try to remove redundancy. :-)

I find it amazing that "DRY" has become such a positive pattern when it was touted as one of the driving points of the Ruby on Rails framework. Don't repeat yourself.

For instance, Java folks are always ready to throw exceptions instead of letting stuff resolve itself if it doesn't quite work.


In that example I only throw an exception when you try to delete a student who doesn't exist. The reason I do so is that it contains semantic information which is useful for the client.

If I returned a fake value - the client would get a 200 response on his attempt to delete a non-existent resource, which is clearly the incorrect behavior. I could return nothing - but then the client would see a 204. So, I choose to throw an exception with a response 'NOT_FOUND'

  throw new WebApplicationException(Response.Status.NOT_FOUND);
so that the client gets a 404 response.


I did try and build your code to run the test (I've no idea where the CsvParam and DateParam classes come from) but I think that the following will give you what you want without throwing the exception.

  @DELETE
  @Path("{uid: [0-9]+}")
  public Response deleteStudent(@PathParam("uid") int uid) {
    final Student deletedStudent = STUDENTS.remove(uid);

    if (deletedStudent == null) {
      return Response.status(Response.Status.NOT_FOUND).build();
    } else {
      return Response.ok().entity(true).build();
    }
  }


Ah, I see what you mean - thanks!

The full (buildable) source code - including the CsvParam and DateParam classes - are here: https://github.com/smanek/students


Yes, this is only an example.

There are a few things I'd like to point out to add to this example:

1) JAX-RS is still new. In the future, they might improve the exception handling or whatnot (so if you return null, they might decided to return 404 ... who knows)

2) We can always wrap all JAX-RS with a Filter. So you can throw JPA level exception (not found) and let the filter catch it and convert it to WebApplicationException(404). Thus in theory, your JAX-RS implementation won't have any RuntimeException explicitly in the code.


I've written large Rails and Sinatra apps, as well as a couple Spring apps and straight Java apps. I"m evaluating Play now and even though I usually prefer Ruby to Java, I'm finding Play (with Eclipse configured to be Textmate like) as productive, if not more so, than Rails.


I'd like to add a few things here.

Jersey is an implementation of JAX-RS. It has 3 major pieces: jersey-core, jersey-client, jersey-server. (The name should be obvious what they are for).

If you're writing JAX-RS services, you can return a few different formats: XML, JSON, ATOM. All you need to do is to annotate the method with the following annotation:

  // will return XML or JSON depending on the request from the client.

  @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) 
This is a big win if you need to support both.

  - Object to XML conversion is done by JAXB.
  - Object to JSON conversion is done by Jackson via JAXB.
Jersey is part of JavaEE 6 standard (part of your Application Server if it supports it).

What's lacking from JavaEE 6 is an MVC framework which is targeted for JavaEE 7.

Another key feature is JAX-WS (the plain old SOAP WebService). The nice thing about JavaEE 6 is that the minimum differences in the programming style between JAX-RS and JAX-WS.

JAX-RS operates according to resources (e.g.: give me all students, give me student with id=1, delete student with id=1, etc). So some of the examples would be:

  // Rough pseudo-code, omitting a few JAX-RS annotation

  public class StudentResource{

  @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })

  public <List> all(){}

  @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })

  public Student get(long id){}
  }
JAX-WS operates according to services (e.g.: initiateInvoiceWorkflow, performPayment, etc).

  public interface AccountingService{

    @WebMethod(operationName="initiateInvoiceWorkflow" ...)

    Invoice initiateInvoiceWorkFlow();

  }

  // have your implementation...
So in theory, you can have something that's called StudentRepository where you can use that repository with both JAX-RS (REST) and JAX-WS (WebService) implementation easily (I've done this and it's quite straightforward) if your "Enterprise client" forces you to do so.

The important bit here is testing. You can easily test both JAX-RS and JAX-WS implementation in both unit-test or integration-test. You can easily do unit-test because you don't need to deploy them to the server: they're just normal Java classes. You can do integration test by deploying them to the server and generates the client implementation (in which I'll cover next).

The client-side implementation of JAX-RS is also similar to that of JAX-WS programming style.

In JAX-WS, you grab a WSDL, throw it to a generator tool that comes with JDK to generate the model (Invoice, Student, etc) and the proxy client-side to call the server-side. Very very straightforward, 5 minute job.

In JAX-RS, you'd use jersey-client to perform HTTP call as follow:

  // url  => is a string that points to JAX-RS end-point e.g.: student/1
  // header => json? xml? atom?
  // type => (typeof Student) (well... it's Java).
  Student student = Client.create().resource(url).accept(header).get(type);
Keep in mind that in the client-side, your Student class must have roughly the same structure and must be annotated using JAXB XML annotation (the client-side also relies on JAXB -> Jackson -> Java object conversion for the case of JSON, or just JAXB -> JAva object for the case of XML).

So no hacking using XPath or something like that (I work in Ruby once in a while and when I read some of the 3rd-party libraries/gems that implement client-side API against popular service provider, most of the implementations do brute force using XPath querying node element and stuff).

PS: Excuse me for the poor formatting, where can I learn to format my comment?

UPDATE: fix the format.

Oh and one more thing: JAX-RS (Jersey) is just an implementation on top of Servlet. So all of your previous knowledge regarding to Servlet (Context, deployment, URL, Filter) will be definitely useful.


Thanks for the clarification and expansion! I was worried about dumping too many details on people while trying to convince them that Java can be easy too ;-)

But you're absolutely right on most of these points.

All the formatting tricks are here: http://news.ycombinator.com/formatdoc


Loved the original article and the expansion in this post made it even better. Would you guys recommend any good books that cover JEE6 web services thoroughly? I'm an experienced java guy but have not dealt much with web services.


None. I usually rely on the source code + javadocs. I might be biased but I find that most Java source code and documentations tend to be easier to read due to common practices employed by newer projects.

I can only give you pointers to JavaEE 6 books (overall).

Beginning GF3 is an OK introduction to JavaEE 6: http://www.amazon.com/Beginning-GlassFish-Experts-Voice-Tech...

Oracle Tutorial (formerly SUN tutorial) for JavaEE 6 is another OK one (reviews were meh, but you've got limited choice so...) http://docs.oracle.com/javaee/6/tutorial/doc/

I saw Amazon has the print edition and another one to be in stock by mid 2012 (Advanced Java EE 6...)

Here's another book covering Java EE6 (intro):

http://www.amazon.com/Java-EE-GlassFish-Application-Server/d...

Seems to cover Servlet more than Apress book (first book).

You probably would need to know JPA 2.0 (ORM) as well.

And some more "best practices/real world-ish" tutorials:

http://www.amazon.com/World-Night-Hacks-Dissecting-Business/...

Good luck.

http://www.amazon.com/Pro-JPA-Mastering-Persistence-Technolo...


Java is still verbose though :).

If JavaEE can reduce JPA2/Model boiler plate code, then it'll be much improved.


For me, the modern Java ecosystem is more about a sweet combination of groovy at the edges with a minimalist Java core. it seems to be the best combo of static and dynamic typing and capture a lot of the best of both worlds without buying into too much religion or "enterprise" type complexity.


But Play 2.0 is moving away from Java Servlets, IIRC... how do the modern solutions compare in that regard?


Play 1.x is NOT based on Java Servlets. No version of Play ever was, never will be. From the BDFL himself:

http://iam.guillaume.bort.fr/post/558830013/why-there-is-no-...




Guidelines | FAQ | Support | API | Security | Lists | Bookmarklet | Legal | Apply to YC | Contact

Search: