tag:blogger.com,1999:blog-43537065599270877902008-08-02T01:20:52.113+08:00Stephan FebruaryStephan Februaryhttp://www.blogger.com/profile/09641721620582976781noreply@blogger.comBlogger5125tag:blogger.com,1999:blog-4353706559927087790.post-28618332711394014612008-04-07T13:23:00.014+08:002008-05-07T13:36:58.960+08:00Grails Acegi ACL HowtoAcegi ACLs are a daunting subject. However I have recently integrated Acegi ACL into the Grails Acegi Plugin, and with that made it much easier to implement fine-grained ACLs in your Grails projects.<br /><br /><span style="font-weight: bold;">Prerequisites</span><br />Please refer to the <span style="font-style: italic;">Resources</span> section at the end of this article for downloads<br /><br />There are primarily two use-cases for the current Acegi Plugin's ACL extensions:<br /><ol><li>Protecting Method Invocations</li><li>Protecting Domain object instances<br /></li></ol><br /><span style="font-size:130%;">1. Protecting Method Invocations</span><br /><br />You can protect method invocations by requiring logged in user to be in certain roles(ROLE_xxx). This check ignores all method parameters, and will throw an exception if the current user is not in the required Role.<br />e.g. method signature:<br /><div class="codeBG"><pre><span class="syntax0"><span class="gutter"> 1:</span><span class="syntax8">public</span> ReportLog <span class="syntax6">getReportLog</span><span class="syntax18">(</span>Integer reportId<span class="syntax18">)</span>{<br /><span class="gutter"> 2:</span> <span class="syntax8">return</span> Report.<span class="syntax6">get</span><span class="syntax18">(</span>reportId<span class="syntax18">)</span>.log<br /><span class="gutter"> 3:</span>}<br /></span></pre></div><span style="font-size:130%;"><span style="font-size:130%;">2. Protecting domain object instances</span></span><br /><br />You can protect domain object instances (tuples in database) using a variety of fine-grained access controls. Currently Acegi supports three (3) distinct methods out of the box, two (2) of which are currently directly supported by the Grails plugin.<br /><br /><span style="font-size:130%;">2.1. Applying ACLs to method parameters <span style="font-style: italic;">(CURRENTLY UNSUPPORTED!)</span><br /></span><br />This ACL checks the first parameter of a method for a matching domain class to see if current user has ACL on that object instance (ACL_ENTRY).<br />e.g. method signature:<br /><div class="codeBG"><pre><span class="syntax0"><span class="gutter"> 1:</span><span class="syntax8">public</span> <span class="syntax10">void</span> <span class="syntax6">saveReport</span><span class="syntax18">(</span>Report report<span class="syntax18">)</span>{<br /><span class="gutter"> 2:</span> report.<span class="syntax6">save</span><span class="syntax18">(</span><span class="syntax18">)</span><br /><span class="gutter"> 3:</span>}<br /></span></pre></div><span>will result in a check being done on the Report parameter to saveReport()<br />to see if the current user has an ACL granting permission for the domain object<br />instance in the parameter.</span><br /><br /><span style="font-size:130%;">2.2 Filtering collections using ACLs</span><br /><br />You can filter the domain objects in a collection returned from a method to see if the user has appropriate ACLs on each object instance in the collection. Items in collection to which the current user does not have access to are automatically removed from the list. (AFTER_ACL_COLLECTION_READ)<br />e.g. method signature:<br /><div class="codeBG"><pre><span class="syntax0"><span class="gutter"> 1:</span><span class="syntax8">public</span> List <span class="syntax6">getAllReports</span><span class="syntax18">(</span><span class="syntax18">)</span>{<br /><span class="gutter"> 2:</span> <span class="syntax8">return</span> Report.<span class="syntax6">list</span><span class="syntax18">(</span><span class="syntax18">)</span><br /><span class="gutter"> 3:</span>}<br /><span class="gutter"> 4:</span><br /></span></pre></div><span style="font-size:130%;">2.2. Applying ACLs to method return value</span><br /><br />You can check the domain object returned from a method invocation to see if the current user as an appropriate ACL granting permission to that domain object. (AFTER_ACL_READ)<br />e.g. method signature:<br /><div class="codeBG"><pre><span class="syntax0"><span class="gutter"> 1:</span><span class="syntax8">public</span> Report <span class="syntax6">getReport</span><span class="syntax18">(</span>Integer id<span class="syntax18">)</span>{<br /><span class="gutter"> 2:</span> <span class="syntax8">return</span> Report.<span class="syntax6">get</span><span class="syntax18">(</span>id<span class="syntax18">)</span><br /><span class="gutter"> 3:</span>}<br /><span class="gutter"> 4:</span><br /></span></pre></div><br /><span style="font-size:130%;">Protecting a Service Class</span><br />Let us now look at a service class which has ACL protection turned on:<br /><div class="codeBG"><pre><span class="syntax0"><span class="gutter"> 1:</span><span class="syntax10">class</span> ReportService{<br /><span class="gutter"> 2:</span><br /><span class="gutter"> 3:</span> <span class="syntax8">static</span> acegiACL <span class="syntax18">=</span> {<br /><span class="gutter"> 4:</span> Report{<br /><span class="gutterH"> 5:</span> <span class="syntax6">getReportLog</span><span class="syntax18">(</span>[<span class="syntax13">'</span><span class="syntax13">ROLE_USER</span><span class="syntax13">'</span>, <span class="syntax13">'</span><span class="syntax13">ROLE_ADMIN</span><span class="syntax13">'</span>]<span class="syntax18">)</span><br /><span class="gutter"> 6:</span> <span class="syntax6">getAllReports</span><span class="syntax18">(</span>[<span class="syntax13">'</span><span class="syntax13">ROLE_USER</span><span class="syntax13">'</span>, <span class="syntax13">'</span><span class="syntax13">AFTER_ACL_COLLECTION_READ</span><span class="syntax13">'</span>]<span class="syntax18">)</span><br /><span class="gutter"> 7:</span> <span class="syntax6">getReport</span><span class="syntax18">(</span>[<span class="syntax13">'</span><span class="syntax13">ROLE_USER</span><span class="syntax13">'</span>, <span class="syntax13">'</span><span class="syntax13">AFTER_ACL_READ</span><span class="syntax13">'</span>]<span class="syntax18">)</span><br /><span class="gutter"> 8:</span> <span class="syntax6">saveReport</span><span class="syntax18">(</span>[<span class="syntax13">'</span><span class="syntax13">ACL_REPORT_WRITE</span><span class="syntax13">'</span>]<span class="syntax18">)</span> <span class="syntax1">//</span><span class="syntax1">CURRENTLY</span><span class="syntax1"> </span><span class="syntax1">UNSUPPORTED!</span><br /><span class="gutter"> 9:</span> }<br /><span class="gutterH"> 10:</span> }<br /><span class="gutter"> 11:</span><br /><span class="gutter"> 12:</span> <span class="syntax10">boolean</span> transactional <span class="syntax18">=</span> <span class="syntax14">true</span><br /><span class="gutter"> 13:</span><br /><span class="gutter"> 14:</span> <span class="syntax9">def</span> <span class="syntax6">getReport</span><span class="syntax18">(</span>Integer reportId<span class="syntax18">)</span> {<br /><span class="gutterH"> 15:</span> <span class="syntax8">return</span> Report.<span class="syntax6">get</span><span class="syntax18">(</span> reportId <span class="syntax18">)</span><br /><span class="gutter"> 16:</span> }<br /><span class="gutter"> 17:</span><br /><span class="gutter"> 18:</span> <span class="syntax9">def</span> <span class="syntax6">getAllReports</span><span class="syntax18">(</span><span class="syntax18">)</span>{<br /><span class="gutter"> 19:</span> <span class="syntax8">return</span> Report.<span class="syntax6">list</span><span class="syntax18">(</span><span class="syntax18">)</span><br /><span class="gutterH"> 20:</span> }<br /><span class="gutter"> 21:</span><br /><span class="gutter"> 22:</span> <span class="syntax9">def</span> <span class="syntax6">getReportLog</span><span class="syntax18">(</span>Integer reportId<span class="syntax18">)</span>{<br /><span class="gutter"> 23:</span> <span class="syntax8">return</span> Report.<span class="syntax6">get</span><span class="syntax18">(</span>reportId<span class="syntax18">)</span>.log<br /><span class="gutter"> 24:</span> }<br /><span class="gutterH"> 25:</span><br /><span class="gutter"> 26:</span> <span class="syntax9">def</span> <span class="syntax6">saveReport</span><span class="syntax18">(</span>Report report<span class="syntax18">)</span>{<br /><span class="gutter"> 27:</span> report.<span class="syntax6">save</span><span class="syntax18">(</span><span class="syntax18">)</span><br /><span class="gutter"> 28:</span> }<br /><span class="gutter"> 29:</span><br /><span class="gutterH"> 30:</span>}<br /></span></pre></div>Note that there is a static closure defined called <span style="font-weight: bold; font-style: italic;">'acegiACL'</span>. This naming is important, and each service class you wish to protect must have this closure definition.<br /><span style="font-weight: bold;"><span style="font-style: italic;">NOTE:</span> </span>The <span style="font-weight: bold; font-style: italic;">'acegiACL' </span>closure is not currently supported on domain objects.<br /><br />The <span style="font-weight: bold; font-style: italic;">'acegiACL'</span> closure can be explained as follows:<br /><ul><li><span style="font-weight: bold;">line 3 </span>- <span style="font-style: italic;">"Report {... "</span><br /></li><ul><li>refers to the domain object we are protecting. This name is used when applying ACL controls for <span style="font-weight: bold;">AFTER_ACL_READ</span> and <span style="font-weight: bold;">AFTER_ACL_COLLECTION_READ.</span></li></ul><li><span style="font-weight: bold;">line 5</span> - <span style="font-style: italic;">getReportLog(['ROLE_USER', 'ROLE_ADMIN'])</span><br /></li><ul><li>Only allow users in the <span style="font-weight: bold;">ROLE_USER</span> and <span style="font-weight: bold;">ROLE_ADMIN</span> roles to call the <span style="font-style: italic;">getReportLog()</span> method</li></ul><li><span style="font-weight: bold;">line 6</span> - <span style="font-style: italic;">getAllReports(['ROLE_USER', 'AFTER_ACL_COLLECTION_READ'])</span><br /></li><ul><li>Only allow users in the <span style="font-weight: bold;">ROLE_USER</span> role to call the <span style="font-style: italic;">getAllReports()</span> method.<br /></li><li>Additionally filter all returned results checking that user has <span style="font-weight: bold;">READ</span> permission on the domain objects.</li></ul><li><span style="font-weight: bold;">line 7 </span>- <span style="font-style: italic;">getReport(['ROLE_USER', 'AFTER_ACL_READ'])</span><br /></li><ul><li>Only allow users in the <span style="font-weight: bold;">ROLE_USER</span> role to call this method.</li><li>Additionally check the returned domain instance to see if the current user has the <span style="font-weight: bold;">READ</span> permission on the object.</li></ul><li><span style="font-weight: bold;">line 8</span> - <span style="font-style: italic;">saveReport(['ACL_REPORT_WRITE'])</span> - <span style="font-weight: bold;">(UNSUPPORTED!)</span><br /></li><ul><li>Check the custom permission to verify that the current user has <span style="font-weight: bold;">WRITE</span> permission on domain object instance passed into the method.<br /></li></ul></ul><span style="font-weight: bold;">Future improvements</span><br /><ul><li>Allow fine-grained ACLs of the type ACL_REPORT_SAVE, by automatically creating instances of AclEntryVoter</li><li>Create a mechanism allowing users to easily hook up their own custom instances of AbstractAclVoter into the ACL mechanism</li></ul><span style="font-weight: bold;">Acknowledgments:</span> Thanks to Tsuyoshi Yamamoto San, and Haotian Sun for their excellent initial work on the <a href="http://grails.codehaus.org/AcegiSecurity+Plugin">Grails Acegi Plugin</a> <span style="font-weight: bold;"><br /><br />Resources:<br /></span><ul><li>Acegi Security - <a href="http://www.acegisecurity.org/">http://www.acegisecurity.org/</a></li><li>Download example application demonstrating ACL integration <a href="http://www.bruary.net/acegitest.7April08-01.tgz">here</a>.</li><li>Download development version of Grails Acegi Plugin with ACL <a href="http://www.bruary.net/grails-acegi-0.3.zip">here</a>.<br /> Update: [7May2008] - This plugin download is <span style="font-weight: bold;">NOT</span> the same as the 0.3 version of the plugin available for download from grails.codehaus.org!<br /></li></ul>Stephan Februaryhttp://www.blogger.com/profile/09641721620582976781noreply@blogger.comtag:blogger.com,1999:blog-4353706559927087790.post-41103367991312056842008-03-21T00:47:00.008+08:002008-03-21T10:52:18.704+08:00Two ways to model One-to-Many in GORMThere are two subtly different, but distinct ways to model a <span style="font-weight: bold;">One-to-Many</span> relationship in <span style="font-weight: bold;">GORM,</span> each with some rather interesting implications.<br /><br /><div style="text-align: center;"><span style="font-weight: bold;font-size:130%;" >Method One:</span><br /></div><div class="codeBG"><pre><span class="syntax0"><span class="gutter"> 1:</span>class Book{<br /><span class="gutter"> 2:</span> String title<br /><span class="gutter"> 3:</span><span class="syntax8"> static</span> belongsTo <span class="syntax18">=</span> Author<br /><span class="gutter"> 4:</span>}<br /><span class="gutterH"> 5:</span><br /><span class="gutter"> 6:</span>class Author {<br /><span class="gutter"> 7:</span> String <span class="syntax8">name</span><br /><span class="gutter"> 8:</span><span class="syntax8"> static</span> hasMany <span class="syntax18">=</span> <span class="syntax13">[</span><span class="syntax13">books:</span><span class="syntax13"> </span><span class="syntax13">Book</span><span class="syntax13">]</span><br /><span class="gutter"> 9:</span>}<br /></span></pre></div><br />This method results in the schema:<br /><br /><div style="text-align: center;"><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp1.blogger.com/_U9LkJjne44M/R-MfoGDwZfI/AAAAAAAAABE/KoKdvMDmodo/s1600-h/one_to_many_link.png"><img style="cursor: pointer;" src="http://bp1.blogger.com/_U9LkJjne44M/R-MfoGDwZfI/AAAAAAAAABE/KoKdvMDmodo/s400/one_to_many_link.png" alt="" id="BLOGGER_PHOTO_ID_5180018770087798258" border="0" /></a></div><br />Furthermore, the following applys:<br /><ol><li>An additional "link" table (<span style="font-style: italic;">author_book</span>) will be created in the database.</li><li>A <span style="font-style: italic;">books </span>property will be available on <span style="font-style: italic;">Author</span></li><li>There is no direct way of querying the reverse of the relationship. i.e. no straightforward way to lookup an <span style="font-style: italic;">Author</span> if you're holding a <span style="font-style: italic;">Book</span> object.(<a href="http://blog.bruary.net/2008/03/gorm-criteria-query-for-one-to-many.html">See this post for a solution</a>)<br /></li></ol><br /><div style="text-align: center;"><span style="font-weight: bold;"><span style="font-size:130%;">Method Two:</span></span><br /></div><div class="codeBG"><pre><span class="syntax0"><span class="gutter"> 1:</span>class Book{<br /><span class="gutter"> 2:</span> String title<br /><span class="gutter"> 3:</span><span class="syntax8"> static</span> belongsTo <span class="syntax18">=</span> Author<br /><span class="gutter"> 4:</span> Author author<br /><span class="gutterH"> 5:</span>}<br /><span class="gutter"> 6:</span><br /><span class="gutter"> 7:</span>class Author {<br /><span class="gutter"> 8: </span>String <span class="syntax8">name</span><br /><span class="gutter"> 9:</span><span class="syntax8"> static</span> hasMany <span class="syntax18">=</span> <span class="syntax13">[</span><span class="syntax13">books:</span><span class="syntax13"> </span><span class="syntax13">Book</span><span class="syntax13">]</span><br /><span class="gutterH"> 10:</span>}<br /></span></pre></div><span style="font-weight: bold;">Note: </span>The addition of <span style="font-style: italic;">"author"</span> property at <span style="font-weight: bold;">line 4 </span>above.<br /><br />This method results in the schema:<br /><br /><div style="text-align: center;"><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp3.blogger.com/_U9LkJjne44M/R-MdqmDwZdI/AAAAAAAAAA0/LWvplZwKcOg/s1600-h/one_to_many.png"><img style="cursor: pointer;" src="http://bp3.blogger.com/_U9LkJjne44M/R-MdqmDwZdI/AAAAAAAAAA0/LWvplZwKcOg/s320/one_to_many.png" alt="" id="BLOGGER_PHOTO_ID_5180016614014215634" border="0" /></a><br /><div style="text-align: left;"><br />Furthermore:<br /></div></div><ol><li>An additional column called <span style="font-style: italic;">author_id</span> will be created on the <span style="font-style: italic;">Book</span> table in the database<br /></li><li>No "link" table will be created in the database</li><li>It is possible to query both the forward and reverse relationship by refering to the <span style="font-style: italic;">Books.</span><span style="font-style: italic;">author</span> and <span style="font-style: italic;">Author.books</span> properties respectively</li></ol><div style="text-align: center;">FIN.<br /></div>Stephan Februaryhttp://www.blogger.com/profile/09641721620582976781noreply@blogger.comtag:blogger.com,1999:blog-4353706559927087790.post-82907822642605767322008-03-20T16:00:00.011+08:002008-03-21T01:26:12.616+08:00GORM Criteria Query for One-to-ManyIn Grails, <span style="font-style: italic;">GORM</span> has some very nice features, not the least of which are <span style="font-style: italic;">Dynamic Finders</span>. It's quite easy to sometimes overlook a really powerful and elegant alternative to dynamic finders in the form of <span style="font-weight: bold;">Criteria.</span> In this article I will cover one specific case in which there exists no dynamic finder for a specific use case, but for which a simple, elegant <span style="font-weight: bold;">Criteria</span> will do.<br />Consider the following one-to-many relationship in which One <span style="font-style: italic;">Author</span> has many <span style="font-style: italic;">Books.</span><br /><br /><div class="codeBG"><pre><span class="syntax0"><span class="gutter"> 1:</span>class Book{<br /><span class="gutter"> 2:</span> String title<br /><span class="gutter"> 3:</span> <span class="syntax8">static</span> belongsTo <span class="syntax18">=</span> Author<br /><span class="gutter"> 4:</span>}<br /><span class="gutterH"> 5:</span><br /><span class="gutter"> 6:</span>class Author {<br /><span class="gutter"> 7:</span> String <span class="syntax8">name</span><br /><span class="gutter"> 8:</span> <span class="syntax8">static</span> hasMany <span class="syntax18">=</span> <span class="syntax13">[</span><span class="syntax13">books:</span><span class="syntax13"> </span><span class="syntax13">Book</span><span class="syntax13">]</span><br /><span class="gutter"> 9:</span>}<br /></span></pre></div><span style="font-weight: bold;">The problem:</span> Assuming we are holding a <span style="font-style: italic;">Book</span> object, how do we find the associated <span style="font-style: italic;">Author</span> for that book ? Here's how:<br /><div class="codeBG"><pre><span class="syntax0"><span class="gutter"> 1:</span><span class="syntax9">def</span> book <span class="syntax18">=</span> Book.<span class="syntax6">findByTitle</span><span class="syntax18">(</span><span class="syntax13">'</span><span class="syntax13">Definitive</span><span class="syntax13"> </span><span class="syntax13">Guide</span><span class="syntax13"> </span><span class="syntax13">to</span><span class="syntax13"> </span><span class="syntax13">Grails</span><span class="syntax13">'</span><span class="syntax18">)</span><br /><span class="gutter"> 2:</span><br /><span class="gutter"> 3:</span><span class="syntax9">def</span> criteria <span class="syntax18">=</span> Author.<span class="syntax6">createCriteria</span><span class="syntax18">(</span><span class="syntax18">)</span><br /><span class="gutter"> 4:</span><br /><span class="gutterH"> 5:</span><span class="syntax9">def</span> author <span class="syntax18">=</span> criteria {<br /><span class="gutter"> 6:</span> books{<br /><span class="gutter"> 7:</span> <span class="syntax6">eq</span><span class="syntax18">(</span><span class="syntax13">'</span><span class="syntax13">id</span><span class="syntax13">'</span>,book.id<span class="syntax18">)</span><br /><span class="gutter"> 8:</span> }<br /><span class="gutter"> 9:</span>}<br /></span></pre></div>The often overlooked "trick" to the above criteria, is the fact that we can use the "books" property on Author in our Criteria to match against (<span style="font-weight: bold; font-style: italic;">line 6 above</span>).<br /><br />References:<br /><ul><li>The <span style="font-style: italic;">GORM</span> section of the <a href="http://grails.org/doc/1.0.x/guide/5.%20Object%20Relational%20Mapping%20%28GORM%29.html">Grails User Guide</a><br /></li></ul><p><br /></p><p><br /></p>Stephan Februaryhttp://www.blogger.com/profile/09641721620582976781noreply@blogger.comtag:blogger.com,1999:blog-4353706559927087790.post-79287841431052763692008-03-20T14:19:00.018+08:002008-03-22T01:39:07.620+08:00Grails Ajax File Upload ProgressBarGrails has the basic infrastructure in place to support AJAX-based file upload progress bars on your pages. In this article I will show you how to leverage the <span style="font-weight: bold;">ExtJS</span> Javascript library along with the <span style="font-weight: bold;">Apache Commons File Upload</span> library to achieve a nice user-friendly file upload progress bar in your Grails project. You can download a working Grails demo application at the end of this article in the <span style="font-weight: bold;">Resources</span> section.<br /><br />Below is a screenshot of the demo application in action.<br /><div style="text-align: center;"><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp2.blogger.com/_U9LkJjne44M/R-Po7WDwZgI/AAAAAAAAABU/q6QUug6GMig/s1600-h/upload_progress.png"><img style="cursor: pointer;" src="http://bp2.blogger.com/_U9LkJjne44M/R-Po7WDwZgI/AAAAAAAAABU/q6QUug6GMig/s400/upload_progress.png" alt="" id="BLOGGER_PHOTO_ID_5180240102637463042" border="0" /></a><br /></div><br /><span style="font-weight: bold;">Background</span><br /><span><span>File upload handling is, as everything else in Grails stacked on top of the Spring Framework. The Spring Framework in turn has delegated the job of managing streaming uploads to the Apache Commons File Upload library.<br />Commons File Upload takes care of very important server-side memory-management by allowing you to "stream" your uploads to disk instead of them being kept and handled entirely in memory. Read more about this at the <a href="http://commons.apache.org/fileupload/">Commons File Upload site</a>.<br /></span></span><br />In this article we will need to complete the following steps in order to achieve a working client-side progressbar:<br /><ol><li>Implement a custom <span style="font-style: italic;">ProgressListener</span></li><li>Subclass Spring's <span style="font-style: italic;">CommonsMultipartResolver</span></li><li>Inject our custom <span style="font-style: italic;">ProgressListener</span> and <span style="font-style: italic;">CommonsMultipartResolver</span> classes into Spring via <span style="font-style: italic;">"conf/spring/resources.xml"</span></li><li>Create an action in a controller which can be polled for upload progress info from client-side javascript<br /></li><li>Write appropriate client-side Javascript to poll the server for progress info<br /></li></ol><br /><span style="font-weight: bold;">Prerequisites:</span><br /><ul><li>At the time of writing, Grails 1.0.1 ships with version 1.1.x of Apache commons-fileupload. Visit <a href="http://commons.apache.org/fileupload/">http://commons.apache.org/fileupload/</a> and get at least version 1.2</li><li>For the purposes of this tutorial I will be using ExtJS version 2.x (<a href="http://extjs.com/download">http://extjs.com/download</a>), which comes with a nifty ready-built javascript progressbar.</li></ul><br /><span style="font-weight: bold;"><span style="font-weight: bold;">Implement a custom ProgressListener</span></span><span><span><br />The commons file upload API allows us to register a class which implements the <span style="font-style: italic;">org.apache.commons.fileupload.ProgressListener</span> interface. Once registered, this will allow us to receive progress updates as multipart data is being streamed to the server. Below is a code snippet from <span style="font-style: italic;">AjaxProgressListener.groovy</span> <span style="font-weight: bold;">(</span>see <span style="font-weight: bold;">Resources</span> at bottom of article<span style="font-weight: bold;">)</span><br /><div class="codeBG"><pre><span class="syntax0"><span class="gutterH"></span><br /><span class="gutter"> 6:</span><span class="syntax10">class</span> AjaxProgressListener <span class="syntax8">implements</span> ProgressListener {<br /><span class="gutter"> 7:</span><br /><span class="gutter"> 8:</span><br />.<br />.<br /><span class="gutter"> 22:</span> <span class="syntax10">void</span> <span class="syntax6">update</span><span class="syntax18">(</span><span class="syntax10">long</span> pBytesRead, <span class="syntax10">long</span> pContentLength, <span class="syntax10">int</span> pItems<span class="syntax18">)</span>{<br /><span class="gutter"> 23:</span> <br /><span class="gutter"> 24:</span> session.<span class="syntax6">setAttribute</span><span class="syntax18">(</span><span class="syntax13">"</span><span class="syntax13">progressMap</span><span class="syntax13">"</span>, [<span class="syntax13">"</span><span class="syntax13">bytesRead</span><span class="syntax13">"</span>: pBytesRead, <span class="syntax13">"</span><span class="syntax13">length</span><span class="syntax13">"</span> : pContentLength, <span class="syntax13">"</span><span class="syntax13">items</span><span class="syntax13">"</span> : pItems]<span class="syntax18">)</span><br /><span class="gutterH"> 25:</span> <span class="syntax11">println</span><span class="syntax18">(</span><span class="syntax13">"</span><span class="syntax13">DEBUG</span><span class="syntax13"> </span><span class="syntax13">UPLOAD</span><span class="syntax13">:</span><span class="syntax13"> </span><span class="syntax9">${</span><span class="syntax9">pBytesRead</span><span class="syntax9">}</span><span class="syntax13"> </span><span class="syntax13">:</span><span class="syntax13"> </span><span class="syntax9">${</span><span class="syntax9">pContentLength</span><span class="syntax9">}</span><span class="syntax13"> </span><span class="syntax13">:</span><span class="syntax13"> </span><span class="syntax9">${</span><span class="syntax9">pItems</span><span class="syntax9">}</span><span class="syntax13">"</span><span class="syntax18">)</span><br /><span class="gutter"> 26:</span> <br /><span class="gutter"> 27:</span> <span class="syntax8">if</span> <span class="syntax18">(</span>pBytesRead <span class="syntax18">=</span><span class="syntax18">=</span> pContentLength<span class="syntax18">)</span>{<br /><span class="gutter"> 28:</span> session.<span class="syntax6">setAttribute</span><span class="syntax18">(</span><span class="syntax13">"</span><span class="syntax13">progressStatus</span><span class="syntax13">"</span>, STATUS_DONE<span class="syntax18">)</span><br /><span class="gutter"> 29:</span> }<br /><span class="gutterH"> 30:</span><br /><span class="gutter"> 31:</span> }<br /><span class="gutter"> 32:</span>}<br /></span></pre></div>As you can see in the above code snippet on <span style="font-weight: bold;">line 24</span>, we place the progress information into our session. We will need access to this information in our controller for retrieving progress information via javascript.<br /></span></span><span style="font-weight: bold;"><span style="font-weight: bold;"><br /><span style="font-weight: bold;">Subclassing Spring's MultipartResolver</span></span></span><br /><span>Grails injects a <span style="font-style: italic;">"multipartResolver"</span> property into our applicationContext. This property is actually an instance of Spring's CommonsMultipartResolver. CommonsMultipartResolver actually wraps the commons <span style="font-style: italic;">FileUpload</span><span style="font-weight: bold;"> </span>class. However it does not expose the <span style="font-style: italic;">FileUpload </span>functionality allowing us to register a progress listener.<br />In order to get our custom ProgressListener registered, we have to subclass CommonsMultipartResolver, and expose getter/setter methods for ProgressListener registration see <span style="font-weight: bold;">lines 18 to 26</span> and our overridden factory method on <span style="font-weight: bold;">line 47)</span> in the<br /><div class="codeBG"><pre><span class="syntax0"><span class="gutter"> 1:</span><span class="syntax8">package</span> com.adeptiva.commons.upload<br /><span class="gutter"> 2:</span><br /><span class="gutter"> 3:</span><span class="syntax8">import</span> org.springframework.web.multipart.commons.CommonsMultipartResolver<br /><span class="gutter"> .<br />.<br /></span><span class="gutterH"></span><span class="gutter"> 11:</span><span class="syntax8">import</span> javax.servlet.http.HttpServletRequest<br /><span class="gutter"> 12:</span><br /><span class="gutter"> 13:</span><span class="syntax10">class</span> AjaxMultipartResolver <span class="syntax8">extends</span> CommonsMultipartResolver{<br /><span class="gutter"> 14:</span><br /><span class="gutterH"> 15:</span> <span class="syntax8">private</span> ProgressListener pListener<br /><span class="gutter"> 16:</span> <span class="syntax8">private</span> HttpServletRequest request<br /><span class="gutter"> 17:</span><br /><span class="gutter"> 18:</span> <span class="syntax8">public</span> <span class="syntax10">void</span> <span class="syntax6">setProgressListener</span><span class="syntax18">(</span>ProgressListener p<span class="syntax18">)</span>{<br /><span class="gutter"> 19:</span> <span class="syntax14">this</span>.pListener <span class="syntax18">=</span> p<br /><span class="gutterH"> 20:</span> }<br /><span class="gutter"> 21:</span><br /><span class="gutter"> 22:</span> <span class="syntax8">public</span> ProgressListener <span class="syntax6">getProgressListener</span><span class="syntax18">(</span><span class="syntax18">)</span>{<br /><span class="gutter"> 23:</span> <br /><span class="gutter"> 24:</span> <span class="syntax8">return</span> <span class="syntax14">this</span>.pListener<br /><span class="gutterH"> 25:</span> <br /><span class="gutter"> 26:</span> }<br /><span class="gutter"> 27:</span><br /><span class="gutter"> 28:</span><br />.<br />.<br /><span class="gutter"> 46:</span><br /><span class="gutter"> 47:</span> <span class="syntax8">protected</span> FileUpload <span class="syntax6">newFileUpload</span><span class="syntax18">(</span>FileItemFactory fileItemFactory<span class="syntax18">)</span> {<br /><span class="gutter"> 48:</span> <br /><span class="gutter"> 49:</span> FileUpload fu <span class="syntax18">=</span> <span class="syntax14">super</span>.<span class="syntax6">newFileUpload</span><span class="syntax18">(</span>fileItemFactory<span class="syntax18">)</span><br /><span class="gutterH"> 50:</span> <br /><span class="gutter"> 51:</span> <span class="syntax8">if</span> <span class="syntax18">(</span><span class="syntax14">this</span>.pListener <span class="syntax18">!</span><span class="syntax18">=</span> <span class="syntax14">null</span><span class="syntax18">)</span>{<br /><span class="gutter"> 52:</span> <span class="syntax14">this</span>.pListener.session <span class="syntax18">=</span> <span class="syntax14">this</span>.request.session<br /><span class="gutter"> 53:</span> fu.<span class="syntax6">setProgressListener</span><span class="syntax18">(</span><span class="syntax14">this</span>.pListener<span class="syntax18">)</span><br /><span class="gutter"> 54:</span> }<br /><span class="gutterH"> 55:</span> <br /><span class="gutter"> 56:</span> <span class="syntax14">this</span>.pListener<span class="syntax18">?</span>.<span class="syntax6">updateStatus</span><span class="syntax18">(</span>AjaxProgressListener.STATUS_UPLOADING<span class="syntax18">)</span><br /><span class="gutter"> 57:</span> <br /><span class="gutter"> 58:</span> <span class="syntax8">return</span> fu<br /><span class="gutter"> 59:</span> }<br /><span class="gutterH"> 60:</span><br /><span class="gutter"> 61:</span>}<br /></span></pre></div></span><br /><br /><span style="font-weight: bold;">Injecting our AjaxMultipartResolver into Grails/Spring</span><br /><span>Now that we have our own implementation of the Spring <span style="font-style: italic;">CommonsMultipartResolver,</span> it's time to take advantage of dependency injection to ensure that Grails uses our <span style="font-style: italic;">AjaxMultipartResolver.</span> We modify our "<span style="font-style: italic;">conf/spring/resources.xml</span>" as follows:</span><br /><div class="codeBG"><pre><span class="syntax0"><span class="gutter"> 1:</span><span class="syntax17"><</span><span class="syntax17">bean</span><span class="syntax17"> </span><span class="syntax17">id</span><span class="syntax17">=</span><span class="syntax13">"</span><span class="syntax13">multipartResolver</span><span class="syntax13">"</span><span class="syntax17"> </span><span class="syntax17">class</span><span class="syntax17">=</span><span class="syntax13">"</span><span class="syntax13">com.adeptiva.commons.upload.AjaxMultipartResolver</span><span class="syntax13">"></span><span class="syntax17"></span><br /><span class="gutter"> 2:</span> <<span class="syntax17"></span><span class="syntax17">property</span><span class="syntax17"> </span><span class="syntax17">name</span><span class="syntax17">=</span><span class="syntax13">"</span><span class="syntax13">maxInMemorySize</span><span class="syntax13">"</span>><br /><span class="gutter"> 3:</span> <span class="syntax1"><!--</span><span class="syntax1"> </span><span class="syntax1">Max</span><span class="syntax1"> </span><span class="syntax1">in</span><span class="syntax1"> </span><span class="syntax1">memory</span><span class="syntax1"> </span><span class="syntax1">100kbytes</span><span class="syntax1"> </span><span class="syntax1">--></span><br /><span class="gutter"> 4:</span> <<span class="syntax17"></span><span class="syntax17">value</span>>10240<<span class="syntax17">/</span><span class="syntax17">value</span><span class="syntax17">></span><br /><span class="gutterH"> 5:</span> <<span class="syntax17"></span><span class="syntax17">/</span><span class="syntax17">property</span><span class="syntax17">></span><br /><span class="gutter"> 6:</span> <<span class="syntax17"></span><span class="syntax17">property</span><span class="syntax17"> </span><span class="syntax17">name</span><span class="syntax17">=</span><span class="syntax13">"</span><span class="syntax13">maxUploadSize</span><span class="syntax13">"></span><span class="syntax17"></span><br /><span class="gutter"> 7:</span> <span class="syntax1"><!--</span><span class="syntax1"> </span><span class="syntax1"> </span><span class="syntax1">1Gb</span><span class="syntax1"> </span><span class="syntax1">Max</span><span class="syntax1"> </span><span class="syntax1">upload</span><span class="syntax1"> </span><span class="syntax1">size</span><span class="syntax1"> </span><span class="syntax1">--></span><br /><span class="gutter"> 8:</span> <span class="syntax17"><</span><span class="syntax17">value></span><span class="syntax17"></span>1024000000<<span class="syntax17"></span><span class="syntax17">/</span><span class="syntax17">value</span><span class="syntax17">></span><br /><span class="gutter"> 9:</span> <span class="syntax17"><</span><span class="syntax17">/</span><span class="syntax17">property</span>><br /><span class="gutterH"> 10:</span> <span class="syntax17"><</span><span class="syntax17">property</span><span class="syntax17"> </span><span class="syntax17">name</span><span class="syntax17">=</span><span class="syntax13">"</span><span class="syntax13">progressListener</span><span class="syntax13">"></span><span class="syntax17"></span><br /><span class="gutter"> 11:</span> <<span class="syntax17"></span><span class="syntax17">ref</span><span class="syntax17"> </span><span class="syntax17">bean</span><span class="syntax17">=</span><span class="syntax13">"</span><span class="syntax13">progressListener</span><span class="syntax13">"</span><span class="syntax17">/</span><span class="syntax17">></span><br /><span class="gutter"> 12:</span> <span class="syntax17"><</span><span class="syntax17">/</span><span class="syntax17">property></span><span class="syntax17"></span><br /><span class="gutter"> 13:</span> <span class="syntax17"><</span><span class="syntax17">/</span><span class="syntax17">bean></span><br /><span class="gutter"> 14:</span><br /><span class="gutterH"> 15:</span><span class="syntax17"><</span><span class="syntax17">bean</span><span class="syntax17"> </span><span class="syntax17">id</span><span class="syntax17">=</span><span class="syntax13">"</span><span class="syntax13">progressListener</span><span class="syntax13">"</span><span class="syntax17"> </span><span class="syntax17">class</span><span class="syntax17">=</span><span class="syntax13">"</span><span class="syntax13">com.adeptiva.commons.upload.AjaxProgressListener</span><span class="syntax13">"</span><span class="syntax17">/</span><span class="syntax17">></span><br /><span class="gutter"> 16:</span><br /></span></pre></div><br /><span>Progress Information<br />The action which retrieves progress information is rather simple, and beyond doing some minor error checking is only responsible for constructing a JSON response (using builders of course).</span><br /><div class="codeBG"><pre><span class="syntax0"><span class="gutter"> 1:</span> <span class="syntax9">def</span> uploadInfo <span class="syntax18">=</span> {<br /><span class="gutter"> 2:</span> <br /><span class="gutter"> 3:</span> <span class="syntax9">def</span> progressMap <span class="syntax18">=</span> session.<span class="syntax6">getAttribute</span><span class="syntax18">(</span><span class="syntax13">"</span><span class="syntax13">progressMap</span><span class="syntax13">"</span><span class="syntax18">)</span><br /><span class="gutter"> 4:</span> <span class="syntax9">def</span> progressStatus <span class="syntax18">=</span> session.<span class="syntax6">getAttribute</span><span class="syntax18">(</span><span class="syntax13">"</span><span class="syntax13">progressStatus</span><span class="syntax13">"</span><span class="syntax18">)</span><br /> .<br /> . <span class="syntax1"></span><br /><span class="gutter"> 22:</span> <span class="syntax6">render</span><span class="syntax18">(</span>builder:<span class="syntax13">'</span><span class="syntax13">json</span><span class="syntax13">'</span><span class="syntax18">)</span>{<br /><span class="gutter"> 23:</span> <span class="syntax6">bytesRead</span><span class="syntax18">(</span>progressMap[<span class="syntax13">'</span><span class="syntax13">bytesRead</span><span class="syntax13">'</span>] <span class="syntax18">)</span> <br /><span class="gutter"> 24:</span> <span class="syntax6">totalSize</span><span class="syntax18">(</span>progressMap[<span class="syntax13">'</span><span class="syntax13">length</span><span class="syntax13">'</span>] <span class="syntax18">)</span><br /><span class="gutterH"> 25:</span> <span class="syntax6">status</span><span class="syntax18">(</span>progressStatus<span class="syntax18">)</span><br /><span class="gutter"> 26:</span> } <br /><span class="gutter"> 27:</span> }<br /></span></pre></div><br /><span style="font-weight: bold;">Our .gsp and Javascript</span><span><br />Between the .gsp and javascript files there remains only a few items to take care of:<br /></span><ul><li>Perform a form submission containing our file input via Javascript. This prevents the browser from navigating away from the current page after upload is complete.</li><li>Start an asynchronous task which polls the server for upload progress, updating our progressbar on each return.<br /></li></ul>The above is rather straightforward, and should be self-explanatory based on the supplied source code (<span style="font-style: italic;">js/application.js</span> and <span style="font-style: italic;">views/home/index.gsp</span> respectively) (see <span style="font-weight: bold;">Resources</span> section).<br /><br /><span style="font-weight: bold;">Resources:<br /></span><span>You can download the full project demonstrating the above from <a href="http://www.bruary.net/filebag.demo.tar.gz">HERE</a>.<br /><br /><span style="font-weight: bold;">References:</span><br /><a href="http://grails.org/doc/1.0.x/guide/6.%20The%20Web%20Layer.html#6.1.8%20Uploading%20Files">File Uploading section</a> from the Grails User Guide.<br />Apache Commons File Upload <a href="http://commons.apache.org/fileupload">website</a><br /><br /><br /></span><div style="text-align: center;"><span>FIN.</span><br /></div>Stephan Februaryhttp://www.blogger.com/profile/09641721620582976781noreply@blogger.comtag:blogger.com,1999:blog-4353706559927087790.post-36213054643825647362008-03-19T12:16:00.009+08:002008-07-02T21:17:01.168+08:00Grails Custom Exception Handling<b>Updated On: 2 Jul 2008 - Tested on Grails 1.0.1</b><br />Grails allows you to perform custom , fine-grained Exception handling for error conditions in your controllers. As per usual, you can take advantage of this feature by making use of Grails' nifty <span style="font-style: italic;">Dependency Injection</span> features. In this case, we will depend on the <code style="font-style: italic;"></code><span style="font-style: italic;">GrailsExceptionResolver</span> class being injected into <span style="font-style: italic;">/conf/BootStrap.groovy</span> as <span style="font-style: italic;">"exceptionHandler".</span><br /><br /><span style="font-weight: bold;">Note:</span> This article shows you a fine-grained alternative to the "500 Internal Server" catch-all which is generated by default in <span style="font-style: italic;">/conf/UrlMappings.groovy<br /><br /></span>The important changes are in the below bit of code on lines <span style="font-weight: bold; font-style: italic;">3,</span> and <span style="font-weight: bold; font-style: italic;">7 to 9</span> of my <span style="font-weight: bold;">BootStrap.groovy</span> file:<span style="font-style: italic;"><br /></span><br /><div class="codeBG"><pre><span class="syntax0"><span class="gutter"> 1:</span><span class="syntax10">class</span> BootStrap {<br /><span class="gutter"> 2:</span><br /><span class="gutter"> 3:</span> <span class="syntax9">def</span> exceptionHandler<br /><span class="gutter"> 4:</span><br /><span class="gutterH"> 5:</span> <span class="syntax9">def</span> init <span class="syntax18">=</span> { servletContext <span class="syntax18">-></span><br /><span class="gutter"> 6:</span><br /><span class="gutter"> 7:</span> exceptionHandler.exceptionMappings <span class="syntax18">=</span><br /><span class="gutter"> 8:</span> [ <span class="syntax13">'</span><span class="syntax13">NoSuchFlowExecutionException</span><span class="syntax13">'</span> :<span class="syntax13">'</span><span class="syntax13">/my/doIt</span><span class="syntax13">'</span>,<br /><span class="gutter"> 9:</span> <span class="syntax13">'</span><span class="syntax13">java.lang.Exception</span><span class="syntax13">'</span> : <span class="syntax13">'</span><span class="syntax13">/error</span><span class="syntax13">'</span>]<br /><span class="gutterH"> 10:</span><br /><span class="gutter"> 11:</span> }<br /><span class="gutter"> 12:</span><br /><span class="gutter"> 13:</span> <span class="syntax9">def</span> destroy <span class="syntax18">=</span> { }<br /><span class="gutter"> 14:</span>}<br /></span></pre></div><br />In the above code snippet we set the <span style="font-style: italic;">exceptionHandler</span>'s <span><span style="font-style: italic;">exceptionMappings</span> </span>property to a Map containing the name of the exception we wish to handle, mapped to the URI we wish to handle our error condition.<br />Line 9 above is very important. This is our <span style="font-weight: bold;">"catchall"</span> for all other <span style="font-style: italic;">java.lang.Exception</span>s to be handled by Grails' internal error handler. It is worth noting that leaving out "java.lang" will have the incorrect bevaviour<span style="font-weight: bold;">.</span> Taken from the javadoc for GrailsExceptionResolver's setExceptionMappings() method:<br /><br /><blockquote><br /><b>NB:</b> Consider carefully how specific the pattern is, and whether to include package information (which isn't mandatory). For example, "Exception" will match nearly anything, and will probably hide other rules. "java.lang.Exception" would be correct if "Exception" was meant to define a rule for all checked exceptions. With more unusual exception names such as "BaseBusinessException" there's no need to use a FQN.<br /></blockquote><br /><span style="font-weight: bold;"><br /></span><span style="font-weight: bold;">NOTE: </span>In the above code the URI maps onto our <span style="font-style: italic;">urlMappings,</span> therefore<br /><ul><li>'/my/doIt' - invokes <span style="font-style: italic;">doIt</span> closure on <span style="font-style: italic;">MyController</span></li><li>'/error' - skips straight to <span style="font-style: italic;">/views/error.gsp</span> <span style="font-weight: bold;"><br /></span></li></ul><br />Lastly, in order for all the above to work, we must remove the default <span style="font-style: italic;">"500"</span> error handler from your <span style="font-style: italic;">/conf/UrlMappings.groovy</span> as follows<span style="font-weight: bold; font-style: italic;"> (line 8)</span>:<br /><div class="codeBG"><pre><span class="syntax0"><span class="gutter"> 1:</span><span class="syntax10">class</span> UrlMappings {<br /><span class="gutter"> 2:</span> <span class="syntax8">static</span> mappings <span class="syntax18">=</span> {<br /><span class="gutter"> 3:</span> <span class="syntax13">"</span><span class="syntax13">/</span><span class="syntax9">$</span><span class="syntax9">controller</span><span class="syntax13">/</span><span class="syntax9">$</span><span class="syntax9">action</span><span class="syntax13">?</span><span class="syntax13">/</span><span class="syntax9">$</span><span class="syntax9">id</span><span class="syntax13">?</span><span class="syntax13">"</span>{<br /><span class="gutter"> 4:</span> constraints {<br /><span class="gutterH"> 5:</span> <span class="syntax1">//</span><span class="syntax1"> </span><span class="syntax1">apply</span><span class="syntax1"> </span><span class="syntax1">constraints</span><span class="syntax1"> </span><span class="syntax1">here</span><br /><span class="gutter"> 6:</span> }<br /><span class="gutter"> 7:</span> }<br /><span class="gutter"> 8:</span><span class="syntax1">//</span><span class="syntax1"> </span><span class="syntax1"> </span><span class="syntax1"> </span><span class="syntax1">"500"(view:</span><span class="syntax1"> </span><span class="syntax1">null)</span><br /><span class="gutter"> 9:</span> }<br /><span class="gutterH"> 10:</span>}<br /></span></pre></div>References:<br /><ul><li>Spring 2.5 API Documentation for <a href="http://static.springframework.org/spring/docs/2.5.x/api/org/springframework/web/servlet/handler/SimpleMappingExceptionResolver.html#setExceptionMappings">SimpleMappingExceptionResolver</a></li><li>Grails 1.0.1 API Document for <a href="http://grails.codehaus.org/doc/1.0.x/api/org/codehaus/groovy/grails/web/errors/GrailsExceptionResolver.html">GrailsExceptionResolver</a><br /></li></ul><br /><blockquote></blockquote>Stephan Februaryhttp://www.blogger.com/profile/09641721620582976781noreply@blogger.com