Announcement Announcement Module
Collapse
No announcement yet.
Bringing Groovy into webapp development: Part 1 Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Bringing Groovy into webapp development: Part 1

    I'm one of those Java developers that tried Ruby on Rails, was extremely impressed, and now wishes he could have that kind of development experience with Java. That may be a cliche, but the cliche sure fits me.

    Because of this wish, I developed GroovyFlow, a Groovy framework that works inside Spring. (GroovyFlow is a SourceForge project, but won't be available on SourceForge until they fix an SSH issue they're having with CVS. Until then, look for GroovyFlow at the very humble http://home.comcast.net/~charlestassoni.)

    Usually the idea behind an open source project is pretty simple-- the idea is "please use my framework." It would be great if you did use GroovyFlow, but guess what: If you look at GroovyFlow a bit, you can figure out how to bring Groovy, along with quick iterative development, into your Spring app. And if you want, you can do so without using a line of code from GroovyFlow. If you don't like how I coded it, you code it. It's not hard.

    And GroovyFlow is there if you want to save some time and use something that already has 1) controllers without configuration, 2) a source directory under which any Groovy class is reloadable while your servlet engine is running 3) Rails-style interceptors, 4) decent JSON support, and 5) Rails-like partials (sort of like little JSPs that fit into the big JSP-- useful for AJAX).

    Of course you don't want to build your own solution, or use GroovyFlow, unless other obvious solutions don't work for you. I'll discuss alternate solutions (Spring's groovy:lang tag, Grails) in a subsequent post. For now, I've attaches a sample GroovyFlow controller.

  • #2
    Bringing Groovy into webapp development: Part 2

    Hope you had a chance to look at a GroovyFlow controller. If you didn't like it, remember that you can easily make your own Groovy framework, and have controllers that work the way you want. I also plan to add more options to GroovyFlow, so it's possible that you'll eventually see something in it that suits you.

    In my first post I said I'd talk about alternatives to GroovyFlow (temporarily at http://home.comcast.net/~charlestassoni, soon to be on SourceForge), or to building your own Groovy framework, which is easier than it sounds. The most obvious alternatives are Spring's groovy:lang tag, and Grails.

    If you've tried using the groovy:lang tag, I'll bet you weren't satisfied. You need to use that tag on every Groovy class that you want to make reloadable. Here there's no notion of a reloadable source tree. When I tried using the tag, I started making fairly large controllers rather than stopping the server and adding more xml. I soon stopped using the tag.

    Grails is another option for developers who use Spring, and a much better one. It's very cleverly done and has a great amount of functionality. (If you're already using it, and are happy with it, then why did you read this far?)

    For me, Grails didn't work. I make a living working on a Spring app that's been around for a few years. Grails is built on top of Spring (not inside Spring, like GroovyFlow) so I had to figure out how to force my app into a quasi-Rails directory structure. How to work my applicationContext into Grails' Spring usage, how to work my Hibernate and Sitemesh configuration into that of Grails. And how to get my controllers to redirect back and forth from the Grails controllers.

    I never actually did figure it all out. Probably my fault, not Grails'. Then recently, I started working on a new project with a friend, and I thought now's my chance to use Grails.

    A few things stopped me. One was when I realized that I wasn't overjoyed about working with Spring through Grails interpretation of Spring. (I probably could've worked through this in time, or maybe just realized I could get away without knowing that much about how Grails uses Spring.) Another was that I didn't like deploying to an in-memory Jetty server during development. I often like to have a webapp depend on another webapp; for example, throwing the full dojo build into the Tomcat webapps directory, and referencing dojo files from the webapp I'm developing. I don't know how to get something like that working with in-memory Jetty, which is what Grails uses in its development mode.

    The kicker, though, was when I discovered that classes in Grails's src/groovy directory didn't reload for me. Now apparently this experience is controversial, as I found out when I asked about it in the Grails forum. Some said that talking about a Grails reloading problem is spreading FUD, while others in the same thread said they couldn't get reloading to work either, or only sporadically. In any case, it really didn't work for me. Not one bit.

    But Groovy reloadability is actually easy, unless you're attached to an architectural habit that can make reloading difficult, and that has many other bad effects as well. I'll talk about that habit next time.

    Comment


    • #3
      Bringing Groovy into webapp development: Part 3

      Here I discuss an architectural tic that many Spring developers have. It can prevent you from happily using GroovyFlow, or from understanding an easy way to write your own Groovy framework. Worse, it can make you put in a lot of unneeded effort while you code, just as someone who needs to avoid every crack in the pavement takes longer to reach his destination.

      A common usage in Spring, or any IOC framework, is to have singleon objects holding other singletons that hold still other singletons. For example, a controller holds a business object that in turn holds a data access object. IOC makes it easy to set that up, and so you do.

      In fact, it almost seems like heresay not to do it that way. But here's what's happens to your code: obviously enough, you end up with a bunch of semantic singletons. Your singletons don't actually follow the Gang of Four singleton pattern (private constructor, static synchronzed getInstance() method), but they act like singletons.

      To see how this can be a problem, consider what happens when you need to add a conditional behavior to your singleton. You make a base class, pull common logic into it, your old singleton becomes one subclass, and you make a new subclass to handle the new requirement. Oh, and don't forget to modify your old xml stanza and add a new one, so that Spring hands out the right singletons to the singletons that need them. That took awhile, didn't it?

      If you don't have a singleton, you have another choice. You instantiate your object in code, set properties or classes into it, and those make the object behave differently. You subclass when necessary, and find it's often not necessary, and that when it is, at least you don't also have to update your xml. You can do all this because you there's no worry about thread synchronization issues now; your object isn't a singleton.

      When you think about it, how many singletons do you really need? The datsource, of course, and maybe a JDBC or Hibernate transactionTemplate, maybe twenty or thirty others for all I know. The rest of your code can just locate these when needed.

      You can either use a bean locator (which holds a reference to the Spring applicationContext) or access commonly used objects in a ThreadLocal. GroovyFlow offers both, (named, respectively, BeanLocator and GroovyFlowContext), and these ideas are easy enough to implement outside of GroovyFlow. (With the ThreadLocal context, you only need to remember that managed environments, such as Tomcat, keep a pool of threads rather than using new threads. Therefore you have to clear the ThreadLocal, just as you close a connection. Otherwise old values may reapper when the thread is re-used.)

      But isn't this the locator pattern I'm talking about, and aren't there problems with that pattern? Yes, that pattern was overused in EJB 1.1, and it was part of what made EJBs hard to test. But it's easy to fix that now. When testing an object, you give it a BeanLocator or ThreadLocal context that's implemented by a Map, and the map holds whatever stubs/mocks you need for the test.

      Similarly, instantiating colloborators in an object, rather than having a singleton injected into your object, doesn't have to cut down testability. When you want to, you can use Factory Method, so that
      new X()
      becomes:
      makeX(){
      return new X()
      }
      and you can give the object under test whatever X you want.

      After reading this, I hope that if you've ever thought injecting singletons into singletons is the way to build an architecture, you're at least a bit unsure of that now. If so, you'll be interested in the next discussion: What happens when you free your controllers from dependency injection.

      Comment


      • #4
        Bringing Groovy into webapp development: Part 4

        We've talked about how an over-reliance on dependency injection can slow you down. Now let's see how using controllers that don't require depency injection can speed you up.

        Your controllers can be dynamically instantiated on each request. Whatever colloborating objects they need, they locate, and the need to locate becomes increasingly rare. Mostly your controllers make new objects, and configure them as needed. Thus configuration moves from xml (slow, inflexible) into your code (fast).

        This is what happens in Rails-- a dynamically instantiatied controller for each request. This is also what GroovyFlow does, and I hope it's the decision you make if you want to write your own Groovy mvc framework.

        If you make this decision, then you can have a source tree of Groovy classes that, in development mode, are not compiled. When a request comes in, you figure out which controller to dynamically instantiate, and which of its methods to invoke. For reloadability's sake, you have a thread checking whether any files in this source tree have been modified. If a file has been modified, you 'uncompile' the Groovy classes that have been compiled so far, and now the next request is filled by code that includes the modification.

        This is a large oversimplification, especially that notion of 'uncompile'. I'll provide details later, discussing what really happens, and how GroovyFlow does it. For now, note that this oversimplificaiton (and the real implementation of the idea) makes no sense if you require controllers that must have dependencies injected into them. The big problem would be: How do you inject dependencies into controllers that don't exist at the time the Spring ApplicatonContext is created? Obviously impossible.

        There's also a technical reloading problem. If you have a controller that lives on from request to request, then if you change one of its colloborating objets while the server is running, the controller can still have a reference to the old, already compiled object. And that can prevent any changes in the new colloborating object from manifesting themselves. That problem doesn't exist in the simplified world where there's a magic uncompile concept, but it does exist in the real world.

        As an aside, Grails pays homage to the idea that controllers should be able to have dependencies injected into them. That makes class reloading a very difficult problem that Grails developers must work on diligently. I'm sure that's why, when you use Grails, you need to run development mode in an in-memory Jetty instance, and why reloading often doesn't work. (As I've said in a previous post, it may work for you. I haven't seen it work for me.)

        I suggest that you be effective, rather than diligent. To that end, in my next post I'll show the details behind the simplification I gave here.

        Comment


        • #5
          Bringing Groovy into webapp development: Part 5

          If you've read my earlier posts on GroovyFlow, you may want to see how I implemented the important features, if not to make yourself comfortable with GroovyFlow, then to better create your own Groovy framework. I'll point to classes and configuration of note in this post. GroovyFlow can be downloaded in its temporary home at http://home.comcast.net/~charlestassoni.

          There's a sample app in samples/prices/prices that I'll refer to as the Prices app. (There's also a smaller sample in samples/small/dynaGroovy that may be of interest-- it shows how GroovyFlow controllers interect with pre-existing Spring MVC controllers, but I won't refer to that here.) I'll also be talking about the core GroovyFlow classes that appear under the src directory.

          You run GroovyFlow in either dev or prod mode. Only the former allows you to change classes while your servlet engine is running. In fact, dev mode is not thread safe, and thus must not be used in a real production, or even QA, environment. Your build needs to set up one of these modes, and also declare a groovy source root that is reloadable in dev mode and pre-compiled in prod mode.

          You can see how I did this in the the build.xml file that's inside the Prices app. If you use GroovyFlow, or if you create something like it, you'll need to include into your build process something analogous to this property: conditional-compilation.dir, and to these tasks: groovy-conditional-compilation-classpath, conditional_compile, decide_conditional_compile, check-mode-exists, and tokenize.

          The Prices app declares a dynamic servlet in its web.xml, and dynamic-servlet.xml sets this servlet up. That file is extremely small for a Spring servlet.xml file. It's a configuration of a regular Spring DispatcherServlet. The configuration forces the DispatcherServlet to always choose just one controller, which will in turn route to all the other, unconfigured controllers. GroovyFlow's DynamicHandlerMapping is declared in that file, and it tells the DispatcherServlet to always choose GroovyFlow's RouterController as its controller. (Note that this version of DynamicHandlerMapping works with Spring 2.0.8. To change to 2.5.4, see VERSION_INFO.txt, which is at the root of the current GroovyFlow distribution.)

          When in prod mode, the RouterController will use a GroovyFlow DynamicFinder to instantiate a controller and invoke a method on it. In dev mode, it will delegate that work to a controllerScript that lives in the reloadable direcory. The controllerScript is extremely simple. Currently it looks like this:

          modelAndView = beanLocator.getBean("dynamicFinder").invoke(reques t, response, getClass().getClassLoader())

          To make the script work, the Routercontroller locates a GroovyScriptEngine (part of the standard Groovy distribution). The router binds the necessary variables into that GroovyScriptEngine, and tells the engine to run the script. The script asks a beanLocator to grab the dynamicFinder. The dynamicFinder then instantiates the appropriate controller, which also lives in the reloadable directory. Sounds like a mouthful, but that's it.

          (Note that there's currently a bug in GroovyScriptEngine that prevents this kind of dynamic instantiation. GroovyFlow has its own GroovyScriptEngine that's the same as the reqular one, except that it doesn't have the bug. The fix is easy, and has been reported. See http://jira.codehaus.org/browse/GROOVY-2861. When the bug is fixed, GroovyFlow will use the standard GroovyScriptEngine.)

          Classes in the reloadable directory are monitored to see if their last modified date has changed. The monitoring is done by the GroovyFlowFilter. When there is a modification, the filter causes a new GroovyScriptEngine to be instantiated, and this allows modifications to take effect. Not just modifications to a controller, but modifications to an object that was used, directly or indirectly, by the controller. The GroovyFlowFilter also clears any caches that might depend on classes in the reloadable directory. For exmaple, at present there's a cache that keeps tabs on what interceptors are run before and after controller methods. The GroovyFlowFilter clears this cache.

          __________________________________________________

          If you're looking at GroovyFlow, you might also want to see 1) how it allows controllers to declare Rails-style interceptors before and after controller methods (see RailsStyleInterceptorLogic), how it enables partials (see PartialsBuilder and PartialsBuilderWrapper), and 3) how it adds methods to HttpServletRequest and Response to make using JSON a little easier (see StartupAop).

          Also, I personally don't like putting validations on model objects, and have come to believe that Spring's own validation framework requires too much effort. I prefer something along the lines of the Rails Presenter pattern (http://blog.jayfields.com/2007/03/ra...r-pattern.html). My initial attempt at a Groovy version is shown in the Prices app, under the reloadable groovy directory. See objects in the org.groovyflow.validation and org.groovyflow.util packages.

          By the way, all of these capabilites are excercised in the PriceChangeController, which is in the Prices app, and which appeared in an earlier post.

          __________________________________________________


          Thanks for spending all this time reading my posts. Now go out and save time by bringing Groovy into your app.

          Comment


          • #6
            Well. I did not read your complete post, But I'm not sure why you couldn't grails with your existing spring app. You can definitely use your existing spring configs, java code that you already created, sitemesh template etc into grails app with very minimal effort.

            Comment


            • #7
              If you migrated to Grails, I salute you! I had trouble, but as I said in my post, I was probably missing something. (Patience, brains, ...)

              Comment


              • #8
                Groovy ActiveRecord

                GroovyFlow now has an initial implementation of a Groovy ActiveRecord. At this point the ActiveRecord could best be described as an on-the-fly IBatis, but one that supports domain-driven design. That is, you are encouraged to put business logic on your domain objects.

                Of course, the ActiveRecords reload while the server is running, so long as you place them in the rerloadable directory. Another nice feature: since ActiveRecord is dynamic, you can add read-only join fields via activeRecord.findBySql() whenever a view or other client code requires them. And then when it comes time to save the ActiveRecord, only fields on the corresponding database table are persisted.

                One correction from an earlier post: I had said that GroovyFlow would become a SourceForge project. In fact, I ended up decding on Google code open source hosting. GroovyFlow can be found here:

                http://code.google.com/p/groovyflow/

                Downloads, including the initial ActiveRecord implementation, can be found here:

                http://code.google.com/p/groovyflow/downloads/list

                If you're interested in joining the GroovyFlow development team, please email me at [email protected].

                Comment


                • #9
                  I have implemented a JPA+Spring+AspectJ based implementation modeled to be very close to Grails GORM, i.e. a pure Java implementation of ActiveRecord.

                  Early stages working source code exists, but documentation, project templates and examples still to come before first release (planned for December ).

                  It supports all major vendors out of the box. Hibernate, Toplink, OpenJpa and EclipseLink is part of the test suite.

                  Things like:

                  List<User> user = User.findAll();
                  User.get(12).delete();
                  User.findAllBy("NameAndAddressAndAgeBetween", "Joe","Street",20,25, asc("name"),max(10));

                  are all possible.

                  http://jraptor.svn.sourceforge.net/viewvc/jraptor/

                  Cheers

                  G

                  Comment

                  Working...
                  X