Saturday, October 19, 2013

Grails Mime Utility

In order to send emails using the grails mail plugin, I needed the mime type.  However, I had no idea how to get that correctly.  Fortunately, a quick google search turned up http://jira.grails.org/browse/GRAILS-6823, which asked for exactly what I was looking for.

Turns out Graeme did some work in 2.0 to put this feature in, but I don't think it's documented anywhere really.  So I thought I would do a quick post on this to help out others who are looking for this this same information.

Simply inject the bean into your service, controller, domain class, etc and call the getMimeTypeForExtension method.  Another tip: use the commons-io FilenameUtils.getExtension to get the extension from the file name without doing any manual parsing:

class MyController {
     DefaultMimeUtility grailsMimeUtility
    String getMimeTypeForFileName(String filename) {
          return grailsMimeUtility.getMimeTypeForExtension(FilenameUtils.getExtension(filename))
    } 
 
 

Friday, February 17, 2012

Upgrading Grails 1.3.7 to 2.0.1

Recently I've been slogging through the crud of trying to upgrade a semi-large grails 1.3.7 project into grails 2.0.1. I wanted to document some of the problems I've run into the upgrade process, their fixes if available, and some thoughts.  See also http://joesondow.blogspot.com/2012/02/ye-olde-tragic-journey-of-attempting-to.html for another attempt to upgrade to Grails 2.  They decided not to do it, but we have finished the conversion and everything is working as expected.

1) BuildConfig
The first issue I ran into was a continuous "Do you want to upgrade to this version of this plugin" every time I ran the application or compiled.  It was often switching between two versions.  This was a rather easy fix - I had old versions of some core plugins in BuildConfig (hibernate and tomcat).  Replacing the custom 1.3.7 version number I had in there with $grailsVersion did the trick.  This variable is built into BuildConfig and there is no reason to set it yourself.
I also had the app-info plugin installed, which is apparently incompatible with Grails 2, so I had to remove it.

2) Spring Security Not Working
After upgrading, my spring security configuration no longer worked - I couldn't log in, I received errors all over the place.  I was finally able to resolve these issues by running the s2-quickstart script provided by the spring security core plugin and then reintegrating my configuration again.  Namely my user and authority classes were replaced by the newer versions.  After I did this, it worked just fine.  Another small issue related to this was at one point I wrapped my tags in the tag or else I received errors.

3) Logging Changes
There were quite a few logging configuration changes that needed to occur.  Namely grails.app.service was renamed to grails.app.services and grails.app.tagLib was renamed to grails.app.taglib, little things like that.  I also had to add 'grails.app.resourceMappers' to the error log4j configuration or else I received lots and lots of unnecessary debug output from them.  One more thing - the grails.app.bootstrap logger was renamed to grails.app.conf.

4) Groovy Stricter Compiler
There were some minor tweaks I had to make to classes to get groovy to even compile.  Namely I had some static classes, which apparently are not allowed in Java and now not allowed in groovy either.  I had to remove the static keyword on the class definition to even compile.

5) Errors on Startup
I had listener and servlet errors on startup and trying to load pages, but then I remembered that I had installed the templates and the web.xml file is not updated on a grails upgrade.  So running grails install-templates again fixed the issue, I just needed to revert my custom scaffolding classes.

6) Groovy Properties with Maps
Maybe this is just me - but suddenly doing obj.properties = params no longer worked with Maps!  For example, I had a domain with a property called config of type Map, and when trying to do databinding with .properties = params, the config property was not set at all.  Adding this in manually after the .properties setting worked fine, but that's annoying to do every time at the least.  I have not tested if this fails with Grails databinding as well, but I hope that it works.

7) Domain Class Changes
We use json-lib for some JSON/XML conversion, and it started complaining mightily about how it couldn't convert some nested properties on ALL domain classes.  Turns out that the injection of certain properties - namely "errors" and "attached" - must have been changed.  I'm thinking that previously it was done through the metaClass, but it is now injected at compile time through AST transformations or something.  Setting the excludes property to "errors" and "attached" or applying a filter for the same names and corresponding classes fixed the issues. 

8) Testing
This is the big catch as I see it with the upgrade to Grails 2. I spent hours upon hours converting unit tests to work with the new testing mixins, etc.  The JUnit tests were fine for the most part, although there were some changes I had to make to fix newer groovy problems, etc.
The big problem was with our spock tests.  Grails 2 is touted as being easier to use with spock with the new testing mixin framework instead of concrete classes that you need to extend.  However, I found that the documentation was severely lacking for some key usage points with Spock.  Namely...

  • The registerMetaClass method is now gone from the mixins, which I took as an immediate red flag.  However, after finally searching the tickets in jira and the grails source, I realized that the mixins actually register a meta class listener with Groovy.  At the end of the test class, they revert all meta class changes and you're good to go.  Where is this in the documentation!?  I'm only slightly angry about this that I spent hours putting in the ConfineMetaClassChanges annotation from the spock code, only to remove it after I realized it was unnecessary.
  • The documentation assumes that you are always testing a typical grails artefact, such as Controllers, Services, etc.  However, we have many custom artefacts that we need to test as well.  Trying to use the technique shown here proved unfruitful but promising. I think after changing our custom artefacts to by the same type as their suffix (see artefact handlers), it will work as expected.  
  • So for now, we have classes that need testing that have no "TestFor" annotation.  After trying to use @Mixin(GrailsUnitTestMixin) unsuccessfully, I finally found another annotation by perusing the source - @TestMixin.  Using @TestMixin(GrailsUnitTestMixin) successfully fixed all of our problems with the spock tests, as far as getting them to actually run goes.
  • The implicit "TestFor" annotation doesn't work for seemingly random controllers, services, etc.  Not sure what is going on here, but I found that sometimes it worked and sometimes it didn't.
  • The name resolution for TestFor is messy and doesn't work as expected pretty often (see above point).  For example, we have a domain class called "Service" (yes, I know, bad naming).  So ServiceSpec and ServiceServiceSpec do not resolve their TestFor(Service) and TestFor(ServiceService) correctly.  It gives me errors such as "Service is not a service class!"  I know that, that's why I tried to get you do it as a domain class, doh!  In the end, I ended up mixing in the DomainClassUnitTestMixin and the ServiceUnitTestMixin and doing mockDomain and mockService to get this working.
After all is said and done, I'm still working on resolving some leakage from my domain classes between tests during unit testing (leaking in unit tests, what!?  yeah, that's the problem).  But things are starting to look pretty good, and we get the added advantages of grails 2.  Writing spock tests will be easier in the future - no worrying about registerMetaClass.  But I wish the conversion process for the spock tests wasn't so painful.  Besides that, the upgrade would have taken me a few hours at most!

UPDATE:
After writing this up, we found a couple of more bugs in the process -

  • The default environment for maven-deploy in the release plugin suddenly became development instead of production for some reason.  Mass chaos ensued until we changed our builds to use prod maven-deploy.
  • I had seen this somewhere but unwisely ignored it - the serverURL setting in Config.groovy was set to something like http://localhost:8080/${appName}.  This turned out to cause all redirects to send to this URL, localhost and all.  Removing the setting completely did the trick.

Friday, May 27, 2011

Grails

Just wanted to put a quick note here that I've started playing with Grails, and I've decided to rewrite pet projects using it as it seems to be more widely supported (at least a more active community with many, MANY plugins). It does just about everything I want it to do and more. Very cool. See it here.

Tuesday, September 7, 2010

Getting Rid of the Form Helper Hidden Checkboxes

For someone who's used the form helper in Monorail and can't seem to find a way to get rid of the extra hidden field that is created (so that you can use the true and false value), I found a dictionary entry hidden in the source that gets rid of it. Simply set suppressHiddenField to true and the hidden field will no longer show up!

Tuesday, August 31, 2010

Broken AspView Sending Emails

One more broken thing I've noticed and had to fix in the source - you cannot send emails when using a layout (non-deprecated methods) from monorail. In order to fix this, I had to change one line in the asp view source. In AspViewEngine.Process(string, string, TextWriter, IDictionary):

Process(templateName, output, null, null, controllerContext);
changes to
Process(templateName, output, EngineContextLocator.Instance.LocateCurrentContext(), null, controllerContext);

The problem was that the context being passed into the next process step was null, and I was getting a null object ref when trying to send an email. With this fix in place, everything works as expected. Not sure if this is an oversight on the AspView creator's part or just an odd chain of events.

Castle Monorail/Activerecord on Mono

I have had a heck of time trying to get Monorail working fully on Mono (2.4, 2.6, and even the latest tarballs). After weeks of sorting through code, I think I have finally figured out the solutions to the most common problems I've encountered:

1) Invalid casts in the form helper select generation. This has taken me quite awhile to track down, and the solution is simple enough that I thought I would blog about it to remember what I done in the future. The problem is with casting and the proxies for AR objects. The AR objects that are proxies, when used in a form helper select can sometimes generate an InvalidCastException if they have already been used. I ended up getting the source from GitHub and modifying the form helper itself in the ReflectionValueGetter. Here is the full GetValue method I used in the AbstractFormHelper.cs:

public override object GetValue(object instance)
   {
    try
    {
     try
     {
      return propInfo.GetValue(instance, null);
     } 
     catch (InvalidCastException)
     {
      propInfo = propInfo.ReflectedType.BaseType.GetProperty(Name);
      return propInfo.GetValue(instance, null);
     }
    }
    catch (TargetException)
    {
     var tempProp = instance.GetType().GetProperty(Name);

     if (tempProp == null)
     {
      throw;
     }

     return tempProp.GetValue(instance, null);
    }
   }

The new code is in the extra try/catch InvalidCastException block.  It merely gets the base type and tries to find the property there if it was an invalid cast (ie Instrument being cast as InstrumentProxy).  More can be found here.

2) Asynchronous actions do not work in mono (at least using the AspView engine and the ActiveRecord integration). I finally tracked this down due to the incorrect or null context being assigned on the End method. Luckily, the fix is VERY easy, especially considering how long it took me to track down. Merely do "HttpContext.Current = HttpContext" in the end method of the action, and it will correctly release the ActiveRecord session in the web module or in your custom methods to attach/release the session. More here.

Tuesday, May 25, 2010

ValidateIsUnique on Monorail

I have been playing with Castle Monorail quite a bit, and am currently working on building a large administration project using it.  The problem I've run across recently is with validating models when using ARDataBind (or the DataBinder in general).  Even though I was using the recommended code:

if (HasValidationError(model)) {
  Flash["summary"] = GetErrorSummary(model);
  RedirectToReferrer();
  return;
}

But I found that I was still getting an invalid object when the object was being saved, or in other words: the HasValidationError method wasn't catching invalid objects.  I have beating my head against the wall many a time until today when I realized something - the ValidateIsUnique attribute comes from Castle.Activerecord and NOT from Castle.Components.Validator.  Sure enough, I realized that any problems I had with HasValidationError not catching an invalid object came when I used ValidateIsUnique.

So after digging through some code for a couple of hours, I haven't quite found a solution that I like.  I may post on Castle Project mailing list to see if I get some answers there.  As of right now I believe the only way to really validate and catch this attribute if the validation failed is to include a separate catch block after the HasValidationError block:

if (!model.IsValid()) {
  ActiveRecordMediator.Evict(model);
  Flash["message"] = String.Join(",", model.ValidationErrorMessages);
  RedirectToReferrer();
  return;
}