Blogs

SpringSource Blog

What's a plugin-oriented architecture?

Peter Ledbrook

Grails is a fantastic framework for developing web applications quickly and easily. You also have access to a plethora of plugins that provide features or make integration with other systems nice and easy. That's all good, but in this article I want to talk about what happens when your application grows and you start drowning in a sea of controllers, domain classes, and other files.

Separation of concerns

One of the most useful patterns in software architecture is called separation of concerns. The idea is that you group everything related to a particular feature or concern into a single, self-contained unit. The code in that unit should not take on any other responsibility. For example, the business logic of a web service should be in one class while the handling of SOAP messages should be in another: the business logic and SOAP handling are two different concerns.

The real beauty of this pattern is that you can aggregate these units into coarser-grained concerns, so you end up using the pattern at multiple levels. For example, say that the web service mentioned above provides a blog-like feature. You could package the business logic, SOAP gateway, and perhaps a REST interface all into a single unit that can be reused.

The standard form of packaging for such coarse-grained features in the Java world is the Java ARchive (JAR). If you're already a Grails developer, you will know that your Grails applications can use such JAR files. But they don't help when it comes to controllers, domain classes, and other special Grails "artifacts". Fortunately, we have a ready-made unit that can help: the Grails plugin.

Ryan Geyer has written a great post demonstrating how to use a plugin to provide a reusable domain model and why you might want to do so. Here's another example in diagrammatic form:

An example plugin-oriented architecture

We package a blog feature into one plugin and a payment processing feature into another. The idea here is that multiple applications are likely to use the same payment processing code and some may also require a blog feature. You can also package static resources to ensure that all your applications have a consistent look and feel. That's what the branding plugin is for. This could include views, layouts, and tag libraries.

Ryan's article shows how to do all this via the plugin installation mechanism, but let's face it, that's hardly conducive to a rapid feedback development cycle. Every change in a plugin requires you to package the plugin and then install it in your application. Fortunately, there is another way.

In-place plugins

What if Grails could load plugins direct from their development directory? You then wouldn't have to go through the intermediate steps of packaging and installing the plugin. In fact, this has been possible to some degree since Grails 1.1, but with version 1.3, it has now matured to a point that makes it much more usable. How do you do it? Simple.

Imagine you have a simple application and a blog plugin side by side in the same directory:

my-app
 +- blog
 +- app

To include the blog plugin into app, just add this entry to grails-app/conf/BuildConfig.groovy:

grails.plugin.location.blog = "../blog"

The required prefix is "grails.plugin.location." – what comes after is up to you, but I recommend you use the name of the plugin. On the right hand side of the expression is the path to the plugin's development directory. Note that this can be absolute or relative.

Voila! When you run the application, you will have all blog's domain classes, controllers, views, etc. available. Even better, if you modify one of blog's controllers or views while the application is running, those changes will automatically be picked up. In other words, you get automatic reloading with in-place plugin artifacts just as you do with your application artifacts.

So with in-place plugins you get the benefits of separation of concerns, but you also get the rapid-feedback development cycle from normal Grails development!

This technique works well while you're working on a plugin, but it doesn't scale particularly well between multiple applications and development teams. Fortunately, you can easily remove the grails.plugin.location line from BuildConfig.groovy, publish the plugin to an internal Maven repository as described here, and then add the plugin as a dependency via the dependency DSL.

Using plugins to break applications into reusable parts is an incredibly powerful and flexible technique that will allow teams to easily manage multiple applications or even complex standalone ones. As you have seen, Grails supports this approach with the in-place plugin mechanism, which allows for rapid development, and plugin dependencies. On top of that, you can easily transition between the two mechanisms by a quick change to the grails-app/conf/BuildConfig.groovy file.

I hope this article inspires some of you to try this technique and benefit from Grails' productivity boost for large applications as well as small.

Similar Posts

Share this Post
  • Digg
  • Sphinn
  • del.icio.us
  • Facebook
  • Mixx
  • Google Bookmarks
  • DZone
  • LinkedIn
  • Slashdot
  • Technorati
  • Twitter
 

33 responses


  1. Awesome post thanks. This works well in grails 1.2…. When you say matured in 1.3, are you referring to the fact that you do not need to call 'package-plugin' on each individual plugin before creating the war??? Any other nuggets of info would be greatly appreciated! Think I shall have to forward my blog post to this one… :)
    (http://johnrellis.blogspot.com/2010/03/grails-inplace-plugin-tip.html)
    Thanks for the post!


  2. It's matured over both the 1.2 and 1.3 releases. But as you say, the key improvement in 1.3 is that you no longer need to run package-plugin, which makes for a much smoother experience.


  3. Great thanks! Hope to attend your talk in Dublin next week! All going well!


  4. Thanks for the honorable mention Peter. :-)

    When I wrote that post I was thinking along the lines of a larger scale project where different development teams might be maintaining the different parts, and multiple applications would be using the plugins. Plus, I'm still a bit of a newbie and didn't know about this particular feature of Grails!

    Looks like I'll have to modify my approach a bit, since this is without question a more dynamic approach.


  5. Hey Peter,

    This is a great post. Next month, I'll do a grails workshop focused on exactly this.
    The talk is titled: "Grails plugins: Modularizing your apps successfully" (sounds better in pt_BR :P )

    Thanks,


  6. Don't forget to mention some drawback using the inline plugins. Plugins dependencies are completly broken and so it is for IDE support ! And also, not all feature of Grails works in plugin such as Subflow (GRAILS-5976).


  7. @Nick Consider them mentioned :) Note that STS now has initial support for in-place plugins.

    It's true that automatic plugin dependency resolution doesn't work (it's tied to the plugin installation mechanism) but I don't see that as a massive inconvenience. You can manually install the relevant plugin(s) or declare them as in-place plugins too. Ideally the dependency resolution would work, but we may have to wait for Grails 2.0 for that.


  8. @Nick "completely broken" is a bit extreme. When using transitive plugin dependencies with inline plugins you just need to make sure those transitive dependencies are either already installed or configure them as inplace plugins. A minor nuisance more than anything else.

    With regards to GRAILS-5976 if you could attach an example to the issue that would help in getting the issue fixed promptly. For those who don't use or need Webflow in their apps it would be good if you could raise issues for other features you see as "not working" when using plugins.


  9. @Graeme by "completely broken" i meant the "dependsOn" is ignored (as peter said).

    The minor inconvenience you both mentions is becoming a bit more than minor for our team which is working on a large application built completely this way (~20 plugins).

    Untill then, i'll wait 2.0


  10. @Nick In what way is it turning into more than a minor problem? I'm asking out of genuine interest. When you add items to the "dependsOn" property, do you not simply add them as an in-place plugins, add them to the dependency DSL, or install them? I would expect that a developer only does it once and then the rest of the team will pick up the changes and not have to do any extra work themselves.

    On a more general note, do you intend to maintain the project with in-place plugins or eventually move to a plugin repository model once the various plugins have matured? Do you see longer term advantages in the in-place mechanism in your particular case?

    Thanks, Peter


  11. I'm glad that you have interest in my comments.

    The problem is many of our plugins interact with each other. When updating a dependency we have to check ALL plugins and apps if they have a direct or indirect dependency to this plugin and this is becoming a long task in a large project.

    Example :
    (all apps) dependsOn AuthorPlugin dependsOn BookPlugin dependsOn ISBNPlugin

    In this scenario, we add a dependency to ISBNPlugin
    We then have to update it in BookPlugin, AuthorPlugin and all plugins/apps using AuthorPlugin.

    At the end, almost all of our plugin depends on each other in the inline declaration because of this spagetti. I wish that it can be done automatically and the team working on the AuthorPlugin don't have to worry about the dependencies added to a plugin's plugin.

    To awnser your last question, we do not aim to maintain our project this way. After the first release, all plugins will be on maven or some other repository.


  12. @Nick OK, I see the problem. You basically need Grails to recursively pick up in-place plugins that are defined in dependent plugins. That seems pretty doable, particularly since changes in Grails 1.3 mean that the plugins no longer need to be compiled first.

    I've raised [an issue|http://jira.codehaus.org/browse/GRAILS-6380] for this. If you can, please attach an example project with an tree of in-place plugins:


  13. Ok no problem and by the way, you and the Grails team are doing a fantastic job !


  14. I write a plugin,it's name is "myplugin" and contain a domain class which named "Test". I used 'grails.plugin.location.myplugin = "../myplugin" in another grails application. In one controller, there code is "import xxx.Test ….def test = new Test();….",it show me that "Groovy:unable to resolve calss xxx.Test".

    But I used the command "grails run-app", it worked, the page is right.
    who can tell me how to resolve the "Groovy:unable to resolve calss xxx.Test"?
    by the way, I used sts 2.3.2.


  15. @oriental I take it you are seeing the error in Eclipse or STS? If yes, then this is fixed in a recent snapshot of the STS Grails support. I'm currently using in-place plugins successfully with STS 2.3.3 M1 and the snapshot from this update site:

    http://dist.springsource.com/snapshot/STS/nightly/e3.5

    @Nick Don't forget to vote for that Grails issue!


  16. hi,Peter Ledbrook. Thank you very much. We need to config the snapshot of the STS Grails support, in addition, need to update the Groovy-Eclipse to the latest snapshot from this update site:http://dist.codehaus.org/groovy/distributions/greclipse/snapshot/e3.5/. Then,it will work.


  17. Hi,
    Great post!
    My question is can I use this to handle updates to a deployed application ?
    And by this way avoid the full redeploy the full app every time need to make small bug
    fix or add new featueres?
    Thank you, Oscar


  18. @Luis No. OSGi is probably the best future option for allowing partial updates while an application is running. That would be a nice feature for sure!

    There is an option that allows you to make changes to GSPs without a full redeployment of your application. I need to add this to the Grails documentation, but once I do, I'll put a reference here.


  19. You'll find information on grails.gsp.view.dir in section 6.2.6 of the Grails 1.3.3 user guide when that's released. In the meantime, you can look at the raw docs here:

    http://github.com/grails/grails-doc/blob/master/src/guide/6.2.6%20Making%20Changes%20to%20a%20Deployed%20Application.gdoc


  20. Thank you,
    OSGI is definitly a real need for grails I would say because, I found this recently wil deploying several grails app into a Tomcat server.
    I got several memory problems to do with permgen space, but mostly I found a show stopper to have 32 MB's per app.
    And I'm not using several grails plugins just: jquery, quartz,searchable.
    But the at the moment there is no friendly windows installer for OSGI server like the one for Tomcat.


  21. Hi,
    Server restart to deploy a new feature or fix a bug is ok for me.
    What is hard is 32MB upload over a poor network connection to make a fix or add new features, my a app is just providing reporting over automated industrial process.
    Can I use the plug-ins to make the smaller file uploads to the server ?
    Thank you, Oscar


  22. about ibatis plugin for grails 1.3.2(or 1.3.1)
    who use the ibatis plugin?
    I have some questions.
    1.download the ibatis plugin from "http://www.grails.org"
    2.run the command "grails install-plugin /home/….grails-ibatis-1.1.0.zip"
    it creates a folder named gateways in the grails-app directory.
    3.create some POJO,such as com.example.Book
    4.run the command "grails create-gateway com.example.Book"
    if it is right, there will be "grails-app/gateways/com/example/BookGateway.groovy"
    "grails-app/gateways/com/example/book.xml"
    but actually,it can not create the groovy and xml file.
    5.so I look at the source code in the plugin, and I find a file named CreateGateway.grooy, I changed some code.
    6.grails package-plugin
    7.grails install-plugin /home/….grails-ibatis-0.1.zip
    8.grails create-gateway com.example.Book
    good, I can see the BookGateway.grooy and book.xml file.
    9.next,how to use the the BookGateway?
    my code as flow:
    package ff
    import com.example.Book;
    import com.example.BookGateway

    class BookController {
    def index = {
    BookGateway b = new BookGateway();
    def book = b.getBookByBname('grails');
    render "$book.bname"
    }
    }

    Am I right to use the BookGateway as above?
    It show me "can not find the class BookGateway",but when I run the "grails run-app" command, it works.
    and can I add the "grails-app/gateways" directory to source folder?

    help!!!


  23. @Luis No. Plugins are currently packaged into the WAR file, so there is no way to deploy them separately. What you can do (if you're using Tomcat (or GlasshFish I believe) is deploy the JARs as shared libraries, as described here:

    http://stackoverflow.com/questions/2166938/multiple-grails-applications-on-tomcat

    Note that the --nojars option excludes all JARs, not just those required by Grails. Nonetheless, it's probably the best option currently for faster uploads.

    @oriental I have no experience with the iBatis plugin and can only direct you to the documentation for the plugin and the Grails user mailing list:

    http://grails.1312388.n4.nabble.com/Grails-user-f1312389.html


  24. Hi,
    Peter Ledbrook. I have a question about the grails plugin-oriented architecture.
    The detail is that now I use a grails-mybatis-plugin, there some codes(such as println "doWithDynamicMethods……………………….") in the doWithDynamicMethods with the plugin.

    then I create a grails-plugin project "domainplugin",install the mybatis plugin.
    run-app. I can see "doWithDynamicMethods….." on the grails Console.

    last I create a grails project "bookapp", add the "grails.plugin.location.domainplugin=../domainplugin" in the BuildConfig.groovy.
    run-app. And this time I can not see the "doWithDynamicMethods….." from the Console.

    In aother word,such as we have plugin A(there is some codes in the doWithDynamicMethods method), plugin B(in B, install-plugin A),and a grails app named testApp(install-plugin B),when we run the testApp, the doWithDynamicMethods method does not work? Can you give me some advice,thank you very much.


  25. I have got it. in the plugin B –BGrailsPlugin.groovy file, we need config the def dependsOn = [a: "version"], so that when the testApp run, the doWithDynamicMethods method run too.

    But I find the another question.
    in doWithDynamicMethods method of the plugin B,I add some dynamic methods to some class.
    When I run-app(testApp),those dynamic methods works.
    Then
    1>grails war testApp.war,
    2> deployment the testApp.war to my tomcat(6.0)
    3>start tomcat
    4>http://localhost:8081/testApp/xxxx/xxxxx,
    5>It lost the dynamic methods again. can you give some advice? Thank you very much!!!


  26. I have a question about the "watchedResources" config of plugin

    def watchedResources = "file:./src/ddd/*.groovy"
    it works.

    Can I config the absolute dir with the watchedResources?
    such as : def watchedResources = "file:E:/xxx/src/*.groovy"?
    I have tried. But it did not work. Who can give me some advice?
    thanks.


  27. I think Spring may be treating that as a relative URL. Try: "file:/E:/xxx/src/*.groovy"


  28. I have tried, not work.


  29. Hi Peter,

    Great post, thanks! We are currently working on a very similar process of refactoring our ocean of controllers and services. We've already exported some module to plugins and it's working great, in both ways: As Maven-Artifacts, and as in-place plugins.

    Can you point out any study-cases for such a refactoring process? It's always good to know which kind of problems and solutions did other people encounter.

    Keep on the good work,

    Amit.


  30. @Amit: I don't know of any public case studies I'm afraid, but I'll see whether I can get some responses on Google .


  31. @oriental: my bad. It seems that Windows file URIs look like file:///C:/some/path/FileSchemeURIs.doc


  32. Excellent article! It really helped me understand the power of using in-place plugins within my Grails apps. I plan on using this a lot.


  33. Nice post Peter … It will definitely help in modularizing my current app specially separating front end and backend as different apps.

5 trackbacks

Leave a Reply