Grails and SPI

I am posting some materials I have found about Grails and how they can be used to back up SPI architecture.

Nice talk from Stephane Maldini about event architecture with Grails: Grails and the Real-time Web

Presentation from Álvaro Sánchez-Mariscal where he shows how SPI can be implemented with Grails and AngularJS

And at the end talk from Zan Thrash about Grails and tools like Yo, Bower and Grunt:

Grails HTTP caching annotation

This simple example shows, how we can build mini framework, that will allow use HTTP response caching header, by marking controller action with custom annotation. Lets start from defining annotation:

  1. @Target([ElementType.FIELD, ElementType.METHOD])
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @Documented
  4. public @interface WithHttpCache {
  5. CacheFor value()
  6. }

and enumeration that will allow us setup length of caching in convenient way:

  1. enum CacheFor {
  2.  
  3. ONE_HOUR(3600000),
  4. ONE_DAY(ONE_HOUR.value * 7),
  5. ONE_WEEK(ONE_DAY.value * 7),
  6. ONE_MONTH(ONE_WEEK.value * 4),
  7. ONE_YEAR(ONE_MONTH.value * 12)
  8.  
  9.  
  10. CacheFor(long value) {
  11. this.value = value
  12. }
  13. }

Now we need to build class, that base on name of controller and action, will return time of caching, if annotation was used for specific place:

  1. class WithHttpCacheService {
  2.  
  3. private static final String DEFAULT_ACTION = 'index'
  4.  
  5. private static final Map<String, Long> CACHE = [:]
  6.  
  7. @Autowired
  8. GrailsApplication grailsApplication
  9.  
  10. public void init(){
  11. grailsApplication.controllerClasses.each {controller ->
  12. final List elements = []
  13. elements.addAll(controller.clazz.declaredFields)
  14. elements.addAll(controller.clazz.methods)
  15.  
  16. elements.each { element ->
  17. final WithHttpCache annotation = element.getAnnotation(WithHttpCache)
  18. if (annotation) {
  19. final String key = buildKey(controller.name, element.name)
  20. CACHE[key] = annotation.value().value
  21. }
  22. }
  23. }
  24. }
  25.  
  26. public Long getCacheTime(final String controllerName, final String methodName){
  27. final String key = buildKey(controllerName, methodName ?: DEFAULT_ACTION)
  28. return CACHE[key]
  29. }
  30.  
  31. private String buildKey(final String controllerName, final String methodName){
  32. return "$controllerName#$methodName".toLowerCase()
  33. }
  34. }

This class need to be registered as a Spring bean, and initialized in bootstrap:

  1. class BootStrap {
  2.  
  3. def withHttpCacheService
  4.  
  5. def init = { servletContext ->
  6. withHttpCacheService.init()
  7. }
  8. def destroy = {
  9. }
  10. }

At the end, we need to build filter, in which we will setup caching headers, if for specific controller+action combination, WithHttpCacheService returns valid caching value:

  1. class WithHttpCacheFilters {
  2.  
  3. WithHttpCacheService withHttpCacheService
  4.  
  5. def filters = {
  6. all(controller:'*', action:'*') {
  7. before = {
  8. final long cacheTime = withHttpCacheService.getCacheTime(controllerName, actionName)
  9. if (cacheTime){
  10. long current = System.currentTimeMillis();
  11. long expires = current + cacheTime;
  12. int maxAge = Math.round(cacheTime / 1000)
  13. response.addHeader('Cache-Control', 'Public')
  14. response.addIntHeader('max-age', maxAge);
  15. response.addDateHeader('Expires', expires);
  16. response.addDateHeader('Last-Modified', current);
  17. }
  18. }
  19. }
  20. }
  21. }

Example of usage:

  1. class TestController {
  2.  
  3. @WithHttpCache(CacheFor.ONE_WEEK)
  4. def index = {
  5. ...
  6. }
  7. }

Grails and Ehcache Annotations for Spring

Ehcache Annotations for Spring is a project that allows "declarative, aspect based caching to be added to a Spring Framework based application by simple annotation". And because Grails stands on Spring, there is no problem to do that. We are using spring xml based configuration here, but if any one know how rewrite it to Groovy DSL, please leave a comment.

1) Configure dependencies in BuildConfig.groovy.

  1.  
  2. compile('com.googlecode.ehcache-spring-annotations:ehcache-spring-annotations:1.2.0'){
  3. excludes 'ehcache-core', 'slf4j-api'
  4. }
  5.  

2) Configure spring resources.xml. We are using proxy-target-class="true" option to avoid JdkDynamicAopProxy ClassCastException problem.

  1.  
  2. <?xml version="1.0" encoding="UTF-8"?>
  3. <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xmlns:context="http://www.springframework.org/schema/context" xmlns:ehcache="http://ehcache-spring-annotations.googlecode.com/svn/schema/ehcache-spring"
  5. xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
  6. http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
  7. http://ehcache-spring-annotations.googlecode.com/svn/schema/ehcache-spring http://ehcache-spring-annotations.googlecode.com/svn/schema/ehcache-spring/ehcache-spring-1.1.xsd">
  8.  
  9. <ehcache:annotation-driven proxy-target-class="true" />
  10.  
  11. <ehcache:config cache-manager="cacheManager">
  12. <ehcache:evict-expired-elements interval="60" />
  13. </ehcache:config>
  14.  
  15. <bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean"/>
  16.  
  17. </beans>
  18.  

3) Configure Ehcache in ehcache.xml.

  1.  
  2. <?xml version="1.0" encoding="UTF-8"?>
  3. <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:noNamespaceSchemaLocation="ehcache.xsd">
  5.  
  6. <diskStore path="java.io.tmpdir"/>
  7.  
  8. <defaultCache
  9. maxElementsInMemory="10000"
  10. eternal="false"
  11. overflowToDisk="false"
  12. timeToIdleSeconds="300"
  13. timeToLiveSeconds="300"
  14. diskPersistent="false"
  15. diskExpiryThreadIntervalSeconds="120"
  16. memoryStoreEvictionPolicy="LRU"
  17. />
  18.  
  19.  
  20. <cache name="cacheableService"
  21. maxElementsInMemory="30000"
  22. eternal="false"
  23. overflowToDisk="false"
  24. diskPersistent="false"
  25. timeToLiveSeconds="86400"
  26. timeToIdleSeconds="0"
  27. />
  28.  
  29. </ehcache>
  30.  
  31.  

4) Use annotations in code:

  1.  
  2. class CacheableService {
  3.  
  4. @Cacheable(cacheName="cacheableService")
  5. public MyData getData() {
  6. ...
  7. return myData
  8. }
  9.  
  10. @Transactional
  11. @TriggersRemove(cacheName="cacheableService", removeAll=true)
  12. public void updateData() {
  13. ...
  14. }
  15. }
  16.  

Grails Gmock partial mock example

  1.  
  2. class Foo {
  3.  
  4. def methodFoo(String param){
  5. return "methodFoo"
  6. }
  7. }
  8.  
  9. ...
  10.  
  11. class Bar {
  12.  
  13. def foo
  14.  
  15. def methodBar(String param){
  16. return foo.methodFoo(param) + methodBarSecond(param);
  17. }
  18.  
  19. def methodBarSecond(String param){
  20. return "methodBarSecond"
  21. }
  22. }
  23.  
  24. ...
  25.  
  26. import static org.hamcrest.Matchers.any
  27.  
  28. @WithGmock
  29. class BarTests {
  30.  
  31. @Test
  32. public void checkMethodBar(){
  33. def result = "my other result"
  34. def mockFoo = mock(Foo)
  35. mockFoo.testMethod(any(String)).returns(result)
  36.  
  37. Bar bar = new Bar()
  38. bar.foo = mockFoo
  39. mock(bar).methodBarSecond(any(String)).returns(result)
  40.  
  41. play {
  42. assertEquals (result * 2, bar.methodBar())
  43. }
  44. }
  45. }
  46.  
  47.  

Grails bean factory + method with parameters

  1.  
  2. class MyObjectFactory {
  3. static final int TYPE_ONE = 1
  4. static final int TYPE_TWO = 2
  5.  
  6. def produce(int type){
  7. if (type == TYPE_ONE){
  8. return ... // type one
  9. }
  10.  
  11. if (type == TYPE_TWO){
  12. return ... // type two
  13. }
  14.  
  15. }
  16. }
  17.  
  18. ...
  19.  
  20. beans {
  21.  
  22. myObjectFactoryBean(MyObjectFactory)
  23.  
  24. myObjectBean(myObjectFactoryBean:'produce'){bean->
  25. bean.constructorArgs = [MyObjectFactory.TYPE_ONE ]
  26. }
  27. }
  28.  

STS and Grails development

After long, long time I have back to eclipse as a main IDE for Grails development. There are two reason to do that. First, my license on IntelliJ was token (end of project == end of license), and before I give lots of $ for my own copy of JetBrains product, I want to test an alternative solutions. Second, I wanted to test Spring Source work (honestly!).

What I have remembered from my last contact with Eclipse + Grails combination was pain. Not even pain but PAIN. But, almost 2 years ago there wasn't to much alternatives. Yes, there was IntelliJ but even my beloved IDE wasn't perfect.

But now, everything changed. Grails became popular framework, Spring buy it and rush began in IDE development.  So how its looking now with STS and Grails? 

We got new project explorer view with nice separations of project elements (controllers, services, domains and so one). What is nice, STS will handle plugins and dependencies and display its source in explorer too. Even more - it will handle project that base on plugin oriented architecture. Sweet.

Other nice feature will appear when you press Alt+Ctrl+Shift+G (ok, one finger left). Its Grails console, with autocomplete (just write "ins" and press Ctrl + Space) similar to console used in Roo/STS integration.

What most important, code autocomplete  is working.  We can navigate through project classes structure, plugins and dependencies.

So is everything shiny? Unfortunately  no - in my case there were some problems with import visibility: even if class was correctly reflected, and autocompletion shows methods (even dynamic, yupi!) eclipse shows nasty error in line where import statement was. Fortunately "clean project" allows to correct this problem.  

Another problem was with inner classes. For class like this:

  1.  
  2. class Role {
  3. static class Type {
  4. static final ADMIN = 'ROLE_ADMIN'
  5. static final PLAYER = 'ROLE_PLAYER'
  6. }
  7. }
  8.  

STS will not see ADMIN and PLAYER fields.

So, what opinion I give to Spring Soruce IDE? Not bad. I will stick to it for now. Definitely I am able to work with Grails project without headache. There are still problems, but not critical. IDE is still evolving and I am sure that in near future it will be great tool. But to be hones, IntelliJ at this moment is just better.

Burning Image 0.5 released

New version of Grails plugin for image manipulation released. Current version ships with ImageMagick support.

To read more go to project home page.

Burning Image 0.4 released

New version of Grails plugin for image manipulation released. Current version allows to:

  • mark domain class as DB image container by using @DBImageContainer annotation
  • configure image storage outside of application dir

To read more go to project home page.

I want to thanks Руслан Бычков for help with works on this version and solving major problem that i encountered. Cheers man!

Groovy magic: asType

Every one who used Grails had to deal (or will have) with XML/JSON converters. They allow to transform object by using syntax like:

  1.  
  2. import grails.converters.*
  3.  
  4. class TestController {
  5. def index = {
  6. [a:1, b:2, c:3] as JSON
  7. }
  8. }
  9.  
  10.  

I like this "as" syntax, is very verbose and clear. How we can implement in our code? Simple, just use asType method:

  1.  
  2. class Foo {
  3. def name
  4.  
  5. String toString(){
  6. "Foo name is ${name}"
  7. }
  8. }
  9.  
  10. class Bar {
  11. def name
  12.  
  13. String toString(){
  14. "Bar name is ${name}"
  15. }
  16. }
  17.  
  18. class Bas {
  19.  
  20. def name
  21.  
  22. String toString(){
  23. "Bas name is ${name}"
  24. }
  25.  
  26. Object asType(Class type){
  27. typeWorker[type](this)
  28. }
  29.  
  30. @Lazy
  31. private def typeWorker = [(Foo):asFoo, (Bar):asBar]
  32.  
  33. private def asFoo = {it ->
  34. new Foo(name:it.name)
  35. }
  36.  
  37. private def asBar = {it ->
  38. new Bar(name:it.name)
  39. }
  40. }
  41.  
  42. def bas = new Bas(name:"Gringo")
  43.  
  44. println bas as Foo
  45. println bas as Bar
  46.  
  47.  

and output will be :

Bas name is Gringo
Foo name is Gringo
Bar name is Gringo

Grails, sky is the limit

Yesterday during my research about fronted (Flex) <-> backend (anything) communication solutions, I hit in Grails Red5 plugin. It take only a few minutes to setup fully accessible RTMP server, that will allow you to build, e.g. video chat. Only several commands change Grails into something totally different. And I like it.

Powered by WordPress with GimpStyle Theme design by Horacio Bella.
Entries and comments feeds. Valid XHTML and CSS.