tag:blogger.com,1999:blog-76519124085805299382023-12-13T06:31:47.616-06:00Filsfilshttp://www.blogger.com/profile/04578829160811273052noreply@blogger.comBlogger42125tag:blogger.com,1999:blog-7651912408580529938.post-31904858771115479912009-05-01T15:44:00.001-05:002009-05-01T15:44:10.699-05:00Varnish Reverse Proxy Accelerator (Ca...<i>Intro:</i><br>The need to offer a mechanism to accelerate the performance of web applications could arguably be even more relevant as we move into a a more linked data approach. With the need to possible offer multiple representations of a single resource URI to address needs for XML, RDF or XHTML+RDFa (or microformats) etc. the need to improve performance and address request from cache when possible increases. <br><br><i>Related:</i><br>Past efforts in this area have involved Apache Connectors to Tomcat (Ref: <a title="http://tomcat.apache.org/connectors-doc/index.html" target="_blank" href="http://tomcat.apache.org/connectors-doc/index.html" id="fb0l">http://tomcat.apache.org/connectors-doc/index.html</a> ) and also reviewing Glassfish. Glassfish Portfolio (Ref: <a title="http://www.sun.com/software/products/glassfish_portfolio/" target="_blank" href="http://www.sun.com/software/products/glassfish_portfolio/" id="dm1h">http://www.sun.com/software/products/glassfish_portfolio/</a> ) has many elements focused on performance and there is also Galssfish Grizzly (Ref: <a title="https://grizzly.dev.java.net/" target="_blank" href="https://grizzly.dev.java.net/" id="fg0f">https://grizzly.dev.java.net/</a> ) which offers some some impressive sounding performance aspects using NIO. <br><br><i>Approach:</i><br>However, to address needs of maintaining URI persistence and caching what was really needed was a "reverse-proxy accelerator". Given that set of criteria I arrived soon at using Varnish (Ref: <a title="http://varnish.projects.linpro.no/" target="_blank" href="http://varnish.projects.linpro.no/" id="dnyi">http://varnish.projects.linpro.no/</a> ). The target application is hosted on Glassfish but obviously Varnish can reverse proxy for a number of machines and any backend talking over http. Indeed, it's this ability that is important to me in order to address my desire to be able to alter systems and networking aspects behind Varnish and have it maintain my URI's in a "Cool URIs" style/approach (Ref: <a title="http://www.w3.org/Provider/Style/URI" target="_blank" href="http://www.w3.org/Provider/Style/URI" id="vj5p">http://www.w3.org/Provider/Style/URI</a> ). <br><br>Following the nice tutorial by Jay Kuri, that is located on that site it is rather easy to get Varnish up and running on Linux and doing reverse proxy and caching. I would like highlight a few things Jay, mentioned in that write up and detail a few aspects I ran into. <br><br>One of the biggest that Jay, mentions is the issue around Varnish not wanting to cache anything that has a cookie reference. Since almost all web based applications are going to do this as a simple session management approach (even if you are not doing accounts), Varnish by default will not cache your URIs. Jay, goes on to recommend code like:<br><br><div style="margin-left: 40px;"><span style="font-family: Courier New;"> if (obj.http.Cache-Control ~ "max-age") {</span><br style="font-family: Courier New;"><span style="font-family: Courier New;"> unset obj.http.Set-Cookie;</span><br style="font-family: Courier New;"><span style="font-family: Courier New;"> deliver;</span><br style="font-family: Courier New;"><span style="font-family: Courier New;"> }</span><br></div><br>to override this behavior and respond from cache for content. As noted this does mean that is starts to become depending on the content provider/web app developer to issue the commands to inform cache system like Varnish about the behavior we expect from them.<br><br><i>Setting behavior in Grails:</i><br>Using Grails (Ref: <a title="http://www.grails.org" target="_blank" href="http://www.grails.org" id="t.g8">http://www.grails.org</a> ) it's easy to set the format of our return in via the <span style="font-family: Courier New;">withFormat{}</span> syntax. Note we would want to make the approapriate entries in our Config.groovy grails.mime.types section (Ref: The "content negotiation" section of <a title="http://www.grails.org/1.0+Release+Notes" target="_blank" href="http://www.grails.org/1.0+Release+Notes" id="y_ix">http://www.grails.org/1.0+Release+Notes</a> ) <br><br>So something like:<br><br> <span style="font-family: Courier New;"> withFormat {</span><br style="font-family: Courier New;"><span style="font-family: Courier New;"> html {</span><br style="font-family: Courier New;"><span style="font-family: Courier New;"> response.setHeader("Vary", "Accept")</span><br style="font-family: Courier New;"><span style="font-family: Courier New;"> def nowPlusHour = new Date().time + 3600000</span><br style="font-family: Courier New;"><span style="font-family: Courier New;"> response.addHeader("Last-Modified", </span><br><span style="font-family: Courier New;"> String.format('%ta, %<te %<tb %<tY %<tH:%<tM:%<tS %<tZ', new Date()))</span><br style="font-family: Courier New;"><span style="font-family: Courier New;"> response.addHeader("Expires", <br> String.format('%ta, %<te %<tb %<tY %<tH:%<tM:%<tS %<tZ', <br> new Date(nowPlusHour)))</span><br style="font-family: Courier New;"><span style="font-family: Courier New;"> [allSites: allSites, allAutoSites: onlyAutoSites]</span><br style="font-family: Courier New;"><span style="font-family: Courier New;"> }</span><br style="font-family: Courier New;"><span style="font-family: Courier New;"> rdf {</span><br style="font-family: Courier New;"><span style="font-family: Courier New;"> def data = modelAsRDFService.asRDF(AgeModel.findAllByLeg(params.id),<br> "/loc/sites/${params.id}")</span><br><span style="font-family: Courier New;"> response.setHeader("Vary", "Accept")</span><br style="font-family: Courier New;"><span style="font-family: Courier New;"> response.contentType = "application/rdf+xml"<br></span><span style="font-family: Courier New;"> def nowPlusHour = new Date().time + 3600000</span><br><span style="font-family: Courier New;"> response.addHeader("Last-Modified", </span><br><span style="font-family: Courier New;"> String.format('%ta, %<te %<tb %<tY %<tH:%<tM:%<tS %<tZ', new Date()))</span><br style="font-family: Courier New;"><span style="font-family: Courier New;"> response.addHeader("Expires", <br> String.format('%ta, %<te %<tb %<tY %<tH:%<tM:%<tS %<tZ', <br> new Date(nowPlusHour)))</span><span style="font-family: Courier New;"> </span><br style="font-family: Courier New;"><span style="font-family: Courier New;"> response.outputStream << data</span><br style="font-family: Courier New;"><span style="font-family: Courier New;"> }</span><br style="font-family: Courier New;"><span style="font-family: Courier New;"> }</span><br style="font-family: Courier New;"><span style="font-family: Courier New;"> }</span><br><br>In this code we have set the LAST-MODIFIED and EXPIRES header entries. Note in this simple example I have simply pushed the expires time ahead by one hour. You can set this however you wish depending on your view of the relative age your resources can be and still be valid.<br><br>The VARY header is set to address some linked data best practices. This informs the client that the representation of this resource URI can change based on how we call it. Here it can be requested as HTML (what is in fact XHTML+RDFa in our case.. a whole topic in itself) and RDF.<br><br>There is no builder for RDF, so I simply pass the model to a service that generates and returns this for me. The Export Plugin (Ref: <a title="http://www.grails.org/Export+Plugin" target="_blank" href="http://www.grails.org/Export+Plugin" id="rtcb">http://www.grails.org/Export+Plugin</a> ) might be worth looking at if you are looking for various other formats to serialize your data to. I don't set the content type for the HTML in the code above, on review I likely should though by default it is coming back as text/html so that may be fine. <br><br>Be sure to have your RDF contain a reference to itself and its resource URI if you are doing a linked data approach. <br><br>There is also likely other response header elements one could consider here based on the reverse proxy and caching needs. A review of <a title="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9.4" target="_blank" href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9.4" id="e3d4">http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9.4</a> and your accelerator package may reveal other setting of value for your particular environment. <br><br><i>Using curl to validate behavior:</i><br>While I really love Firebug, I found it to not be the easiest to use to verify proper cache behavior by Varnish. Rather, I used (being a bit of CLI lover), curl. A curl request on a resource URI is made using the -D out option to capture the headers. Two consecutive headers are shown below:<br><br><span style="font-family: Courier New;">HTTP/1.1 200 OK</span><br style="font-family: Courier New;"><span style="font-family: Courier New;">X-Powered-By: Servlet/2.5</span><br style="font-family: Courier New;"><span style="font-family: Courier New;">Server: Sun Java System Application Server 9.1</span><br style="font-family: Courier New;"><span style="font-family: Courier New;">Vary: Accept</span><br style="font-family: Courier New;"><span style="font-family: Courier New;">Last-Modified: Fri, 1 May 2009 16:10:00 CDT</span><br style="font-family: Courier New;"><span style="font-family: Courier New;">Expires: Fri, 1 May 2009 17:10:00 CDT</span><br style="font-family: Courier New;"><span style="font-family: Courier New;">Content-Type: image/png</span><br style="font-family: Courier New;"><span style="font-family: Courier New;">Content-Length: 20951</span><br style="font-family: Courier New;"><span style="font-family: Courier New;">Date: Fri, 01 May 2009 19:59:32 GMT</span><br style="font-family: Courier New;"><b style="font-family: Courier New;">X-Varnish: 868897910</b><br style="font-family: Courier New;"><span style="font-family: Courier New;">Age: 0</span><br style="font-family: Courier New;"><span style="font-family: Courier New;">Via: 1.1 varnish</span><br style="font-family: Courier New;"><span style="font-family: Courier New;">Connection: keep-alive</span><br style="font-family: Courier New;"><br style="font-family: Courier New;"><span style="font-family: Courier New;">HTTP/1.1 200 OK</span><br style="font-family: Courier New;"><span style="font-family: Courier New;">X-Powered-By: Servlet/2.5</span><br style="font-family: Courier New;"><span style="font-family: Courier New;">Server: Sun Java System Application Server 9.1</span><br style="font-family: Courier New;"><span style="font-family: Courier New;">Vary: Accept</span><br style="font-family: Courier New;"><span style="font-family: Courier New;">Last-Modified: Fri, 1 May 2009 16:10:00 CDT</span><br style="font-family: Courier New;"><span style="font-family: Courier New;">Expires: Fri, 1 May 2009 17:10:00 CDT</span><br style="font-family: Courier New;"><span style="font-family: Courier New;">Content-Type: image/png</span><br style="font-family: Courier New;"><span style="font-family: Courier New;">Content-Length: 20951</span><br style="font-family: Courier New;"><span style="font-family: Courier New;">Date: Fri, 01 May 2009 19:59:34 GMT</span><br style="font-family: Courier New;"><b style="font-family: Courier New;">X-Varnish: 868897911 868897910</b><br style="font-family: Courier New;"><span style="font-family: Courier New;">Age: 2</span><br style="font-family: Courier New;"><span style="font-family: Courier New;">Via: 1.1 varnish</span><br style="font-family: Courier New;"><span style="font-family: Courier New;">Connection: keep-alive</span><br style="font-family: Courier New;"><br>Note the two values in the X-Varnish response header entry indicating to us that the content came from the previously cached content. If we continually saw only one number here would need to investigate why our request is being passed through to the backend server.<br><br><i>Conclusion:</i><br>Varnish, with a few considerations related to explicate cache control instructions provides a nice acceleration to web application performance. As a from the start reverse proxy (vs something like Squid which started life as a forward proxy) it plays a valuable roll in linked data approaches by allowing changes to systems and networks behind the scene while allowing URI persistence to be maintained. I've also had no issues with it with respect to 303 redirects on generic documents, also important in linked data approaches. <br>filshttp://www.blogger.com/profile/04578829160811273052noreply@blogger.com0tag:blogger.com,1999:blog-7651912408580529938.post-28434455612911576542009-02-16T11:48:00.003-06:002009-02-16T11:53:00.214-06:00Neptune Database Services and Scripting:The use of services on the net has on interesting set of issues to address with respect to acting as generic data resources. Doing mash ups with RSS or ATOM feeds or weather or other somewhat periodic and serial data has become rather common. Yahoo Pipes (Ref: <a title="http://pipes.yahoo.com" target="_blank" href="http://pipes.yahoo.com" id="hgdx">http://pipes.yahoo.com</a> ) and other such web applications do a good job dealing with these types of data end points. <br><br><div style="text-align: left;">However, when working with scientific data sets and services like those at CHRONOS (Ref: <a title="http://portal.chronos.org/gridsphere/gridsphere?cid=res_dev" target="_blank" href="http://portal.chronos.org/gridsphere/gridsphere?cid=res_dev" id="gc06">http://portal.chronos.org/gridsphere/gridsphere?cid=res_dev</a> ) a different set of use issues comes up.<br><br>Let's look at a basic XML-RPC styled service endpoint for the Neptune database at CHRONOS (Ref: <a title="http://www.chornos.org" target="_blank" href="http://www.chronos.org/" id="vc5m">http://www.chronos.org</a> ):<br></div><br><a title="http://services.chronos.org/xqe/public/chronos.neptune.samples.advanced?callback=displayNexus&time_range=10-20&fossil_group=Diatoms&include_dated=true&serializeAs=wrs&" target="_blank" href="http://services.chronos.org/xqe/public/chronos.neptune.samples.advanced?callback=displayNexus&time_range=33-34&fossil_group=Diatoms&include_dated=true&serializeAs=wrs&" id="eh_v">http://services.chronos.org/xqe/public/chronos.neptune.samples.advanced?callback=displayNexus&time_range=33-34&fossil_group=Diatoms&include_dated=true&serializeAs=wrs&</a> <br><br>Here I have used a service from the XQE engine that has set the time range to 33-34 Ma, fossil group to Diatoms, indicated that only dated items should be used and set the output serialization to web row sets (generic XML). <br><br>However, let's say by use of a recent example, we wanted to locate all the unique taxon names and drill holes from various time ranges and fossil types from the CHRONSO Neptune database. We now need to loop on a set time times and names and then locate the unique, not full, set of names and locations. We can do this with the service above, but not directly. We need to collect and process a range of calls on that service to achieve this. <br><br>One could develop a small program in Java, Ruby Groovy or some other language. Dynamic languages like Groovy are especially well suited for such efforts and a a simple script like:<br><pre><a name="l1"><span class="ln"></span></a><span class="s1"><font size="1"><a style="font-family: Courier New;" name="l8"><span class="ln"></span></a><span style="font-family: Courier New;"></span><a style="font-family: Courier New;" name="l10"><span class="ln">10 </span></a></font></span><font size="1"><span style="font-family: Courier New;" class="s0">//Upper Oligocene Sub-Series/Sub-Epoch 23.8 28.5 Oligocene</span><span style="font-family: Courier New;" class="s1"> <br><a name="l11"><span class="ln">11 </span></a></span><span style="font-family: Courier New;" class="s0">//Lower Oligocene Sub-Series/Sub-Epoch 28.5 33.7 Oligocene</span><span style="font-family: Courier New;" class="s1"> <br><a name="l12"><span class="ln">12 </span></a></span><span style="font-family: Courier New;" class="s0">//Upper Eocene Sub-Series/Sub-Epoch 33.7 36.9 Eocene</span><span style="font-family: Courier New;" class="s1"> <br><a name="l13"><span class="ln">13 </span></a></span><span style="font-family: Courier New;" class="s0">//Middle Eocene Sub-Series/Sub-Epoch 36.9 49 Eocene</span><span style="font-family: Courier New;" class="s1"> <br><a name="l14"><span class="ln">14 </span></a></span><span style="font-family: Courier New;" class="s0">//Lower Eocene Sub-Series/Sub-Epoch 49 55.05 Eocene</span><span style="font-family: Courier New;" class="s1"> <br><a name="l15"><span class="ln">15 </span></a></span><span style="font-family: Courier New;" class="s0">//Paleocene Series/Epoch 55.05 65 Paleogene</span><span style="font-family: Courier New;" class="s1"> <br><a name="l16"><span class="ln">16 </span></a> <br><a name="l17"><span class="ln">17 </span></a></span><span style="font-family: Courier New;" class="s2">import </span><span style="font-family: Courier New;" class="s1">sun.net.www.http.HttpClient <br><a name="l18"><span class="ln">18 </span></a></span><span style="font-family: Courier New;" class="s2">import </span><span style="font-family: Courier New;" class="s1">org.apache.commons.httpclient.methods.GetMethod <br><a name="l19"><span class="ln">19 </span></a> <br><a name="l20"><span class="ln">20 </span></a></span><span style="font-family: Courier New;" class="s2">def </span><span style="font-family: Courier New;" class="s1">ageRanges = [</span><span style="font-family: Courier New;" class="s3">'23.8-28.5'</span><span style="font-family: Courier New;" class="s1">, </span><span style="font-family: Courier New;" class="s3">'28.5-35.7'</span><span style="font-family: Courier New;" class="s1">, </span><span style="font-family: Courier New;" class="s3">'33.7-36.9'</span><span style="font-family: Courier New;" class="s1">, </span><span style="font-family: Courier New;" class="s3">'36.9-49'</span><span style="font-family: Courier New;" class="s1">, </span><span style="font-family: Courier New;" class="s3">'49-55.05'</span><span style="font-family: Courier New;" class="s1">, </span><span style="font-family: Courier New;" class="s3">'55.05-65'</span><span style="font-family: Courier New;" class="s1">] <br><a name="l21"><span class="ln">21 </span></a></span><span style="font-family: Courier New;" class="s2">def </span><span style="font-family: Courier New;" class="s1">fossilTypes = [</span><span style="font-family: Courier New;" class="s3">"Diatoms"</span><span style="font-family: Courier New;" class="s1">, </span><span style="font-family: Courier New;" class="s3">"Planktonic+Foraminifera"</span><span style="font-family: Courier New;" class="s1">, </span><span style="font-family: Courier New;" class="s3">"Radiolarians"</span><span style="font-family: Courier New;" class="s1">, </span><span style="font-family: Courier New;" class="s3">"Calcareous+Nannoplankton"</span><span style="font-family: Courier New;" class="s1">] <br><a name="l22"><span class="ln">22 </span></a> <br><a name="l23"><span class="ln">23 </span></a>fossilTypes.each() {type -> <br><a name="l24"><span class="ln">24 </span></a> ageRanges.each() {range -> <br><a name="l25"><span class="ln">25 </span></a> </span><span style="font-family: Courier New;" class="s2">def </span><span style="font-family: Courier New;" class="s1">urlxml = </span><span style="font-family: Courier New;" class="s3">"http://services.chronos.org/xqe/public/chronos.neptune.samples.advanced?callback=<br>displayNexus&time_range=$</span><span style="font-family: Courier New;" class="s1">{range}</span><span style="font-family: Courier New;" class="s3">&fossil_group=$</span><span style="font-family: Courier New;" class="s1">{type}</span><span style="font-family: Courier New;" class="s3">&include_synonyms=true&include_dated=true&serializeAs=wrs&"</span><span style="font-family: Courier New;" class="s1"> <br><a name="l26"><span class="ln">26 </span></a> <br><a name="l27"><span class="ln">27 </span></a> </span><span style="font-family: Courier New;" class="s2">def </span><span style="font-family: Courier New;" class="s1">client = </span><span style="font-family: Courier New;" class="s2">new </span><span style="font-family: Courier New;" class="s1">HttpClient() <br><a name="l28"><span class="ln">28 </span></a> </span><span style="font-family: Courier New;" class="s2">def </span><span style="font-family: Courier New;" class="s1">get = </span><span style="font-family: Courier New;" class="s2">new </span><span style="font-family: Courier New;" class="s1">GetMethod(urlxml) <br><a name="l29"><span class="ln">29 </span></a> client.executeMethod(get) <br><a name="l30"><span class="ln">30 </span></a> <br><a name="l31"><span class="ln">31 </span></a> </span><span style="font-family: Courier New;" class="s2">def </span><span style="font-family: Courier New;" class="s1">xmlSlurped = </span><span style="font-family: Courier New;" class="s2">null</span><span style="font-family: Courier New;" class="s1"> <br><a name="l32"><span class="ln">32 </span></a> </span><span style="font-family: Courier New;" class="s2">try </span><span style="font-family: Courier New;" class="s1">{ <br><a name="l33"><span class="ln">33 </span></a> xmlSlurped = </span><span style="font-family: Courier New;" class="s2">new </span><span style="font-family: Courier New;" class="s1">XmlSlurper().parseText(get.getResponseBodyAsString()) <br><a name="l34"><span class="ln">34 </span></a> <br><a name="l35"><span class="ln">35 </span></a> </span><span style="font-family: Courier New;" class="s2">def </span><span style="font-family: Courier New;" class="s1">entries = xmlSlurped.data.currentRow <br><a name="l36"><span class="ln">36 </span></a> </span><span style="font-family: Courier New;" class="s2">def </span><span style="font-family: Courier New;" class="s1">legSiteHole = [] <br><a name="l37"><span class="ln">37 </span></a> </span><span style="font-family: Courier New;" class="s2">def </span><span style="font-family: Courier New;" class="s1">bug = [] <br><a name="l38"><span class="ln">38 </span></a> entries.each {entry -> <br><a name="l39"><span class="ln">39 </span></a> legSiteHole += </span><span style="font-family: Courier New;" class="s3">"$</span><span style="font-family: Courier New;" class="s1">{entry.columnValue[</span><span style="font-family: Courier New;" class="s4">3</span><span style="font-family: Courier New;" class="s1">]}</span><span style="font-family: Courier New;" class="s3">_$</span><span style="font-family: Courier New;" class="s1">{entry.columnValue[</span><span style="font-family: Courier New;" class="s4">4</span><span style="font-family: Courier New;" class="s1">]}</span><span style="font-family: Courier New;" class="s3">_$</span><span style="font-family: Courier New;" class="s1">{entry.columnValue[</span><span style="font-family: Courier New;" class="s4">5</span><span style="font-family: Courier New;" class="s1">]}</span><span style="font-family: Courier New;" class="s3">"</span><span style="font-family: Courier New;" class="s1"> <br><a name="l40"><span class="ln">40 </span></a> bug += </span><span style="font-family: Courier New;" class="s3">"$</span><span style="font-family: Courier New;" class="s1">{entry.columnValue[</span><span style="font-family: Courier New;" class="s4">11</span><span style="font-family: Courier New;" class="s1">]}</span><span style="font-family: Courier New;" class="s3">"</span><span style="font-family: Courier New;" class="s1"> <br><a name="l41"><span class="ln">41 </span></a> } <br><a name="l42"><span class="ln">42 </span></a> <br><a name="l43"><span class="ln">43 </span></a> println </span><span style="font-family: Courier New;" class="s3">"Total tax count is $</span><span style="font-family: Courier New;" class="s1">{bug.size()}</span><span style="font-family: Courier New;" class="s3"> and total LSH count is $</span><span style="font-family: Courier New;" class="s1">{legSiteHole.size()}</span><span style="font-family: Courier New;" class="s3">"</span><span style="font-family: Courier New;" class="s1"> <br><a name="l44"><span class="ln">44 </span></a> println </span><span style="font-family: Courier New;" class="s3">"In range " </span><span style="font-family: Courier New;" class="s1">+ range + </span><span style="font-family: Courier New;" class="s3">" of type " </span><span style="font-family: Courier New;" class="s1">+ type + </span><span style="font-family: Courier New;" class="s3">" found " </span><span style="font-family: Courier New;" class="s1">+ bug.unique().size() + </span><span style="font-family: Courier New;" class="s3">" unique <br>taxa in " </span><span style="font-family: Courier New;" class="s1">+ legSiteHole.unique().size() + </span><span style="font-family: Courier New;" class="s3">" unique leg/site/holes"</span><span style="font-family: Courier New;" class="s1"> <br><a name="l45"><span class="ln">45 </span></a> println range + </span><span style="font-family: Courier New;" class="s3">" " </span><span style="font-family: Courier New;" class="s1">+ bug.unique().size() <br><a name="l46"><span class="ln">46 </span></a> } </span><span style="font-family: Courier New;" class="s2">catch </span><span style="font-family: Courier New;" class="s1">(org.xml.sax.SAXParseException e) { <br><a name="l47"><span class="ln">47 </span></a> println </span><span style="font-family: Courier New;" class="s3">"Exception parsing XML"</span><span style="font-family: Courier New;" class="s1"> <br><a name="l48"><span class="ln">48 </span></a> } <br><a name="l49"><span class="ln">49 </span></a> } <br><a name="l50"><span class="ln">50 </span></a>} <br></span></font></pre>This script uses the Apache Commons HttpClient, but there are many way to do this. The point I am after is not to show a best practice in using Groovy to access services (I'm sure there are many aspects of that code that people could comment on). Rather it is to highlight an issue related to balancing service generality and usefulness.<br><br>Note I did attempt to use both the XML and the character separated values<br />version via Yahoo Pipes and also the defunct Google Mashups Editor. <br />However, the need to have a more programmatic interface to conduct<br />loops, basic array manipulation and such is beyond what these can do<br />easily. While it might be possible, I did not find it easy or intuitive. These resource seem more focused on RSS or ATOM sources and simple filtering and counting. <br><br>Additionally the<br />Kepler work flow application is a bit of over kill and doesn't yet seem to<br />work well with REST or XML-RPC style services as well as I feel it should. A heavy focus on WS* and other more heavy duty scientific data flow operations mean it's lightweight rapid service mashup capacity is limited (IMHO). <br><br><i>Making a resource or service of general "popular" use may make it of generic interest but can limit its utility on its own to address specific scientific efforts.</i><br><br>Expecting generic services to be of use via the implementation of packages like Yahoo Pipes or Kepler runs into implementation aspects of those tools. Aspects which may make use of them sufficiently complex as too discourage users. <br><br>However, expecting people to write code like the example above, even in a language like Groovy that makes calling, using and parsing the data rather easy is also potentially unrealistic. Not everyone likes to write code.<br><br>So, there is a dilemma for service providers of science data like CHRONOS. Make the services too generic and you risk them being of little use to focused research communities. Address the vertical needs of a specific community or tool interface and risk making them of limited utility to others. Additionally addressing the needs of several vertical communities could be taxing from a human resource point of view. <br><br>One does wonder if the creation of a domain specific language (DSL) might have benefit to address this. If there was enough community interest to justify it's creation a service that consumes and process a DSL would allow a sort of "custom command line interface. <br><br>The Sloan Digital Sky Survey SkyServer (Ref: <a title="http://cas.sdss.org/astrodr7/en/" target="_blank" href="http://cas.sdss.org/astrodr7/en/" id="lbzv">http://cas.sdss.org/astrodr7/en/</a> ) addresses this by simply using SQL as that "DSL" language. The page <a title="http://cas.sdss.org/astrodr7/en/tools/search/sql.asp" target="_blank" href="http://cas.sdss.org/astrodr7/en/tools/search/sql.asp" id="zi8x">http://cas.sdss.org/astrodr7/en/tools/search/sql.asp</a> allows one to structure and submit SQL directly to the database. <br><br>However, a more focused DSL might be able to address the needs of special research groups while consuming more generic service end points that could be exposed for other to use in similar approaches to the code above, via the DSL or their own approach. <br><br>Also the development effort in making such a DSL might be able to be spread across multiple efforts making it more appealing from a human resource point of view.<br><br>Just some ramblings on how to balance generic services (resources) vs the need of special communities for more focused and unique services. Regardless if they are REST, XML-RPC or WS* in nature. <br><br><b>Other references:</b><br>Kepler Workflow Application: <a title="http://kepler-project.org/" target="_blank" href="http://kepler-project.org/" id="mmcz">http://kepler-project.org/</a> <br>Google Mashup Editor <a title="http://code.google.com/gme/" target="_blank" href="http://code.google.com/gme/" id="b95g">http://code.google.com/gme/</a> (defunct)<br>XPROC <a title="http://xproc.org/" target="_blank" href="http://xproc.org/" id="rwlu">http://xproc.org/</a> and <a title="http://www.w3.org/TR/xproc/" target="_blank" href="http://www.w3.org/TR/xproc/" id="pp.5">http://www.w3.org/TR/xproc/</a> <br>GRDDL: <a title="http://www.w3.org/2004/01/rdxh/spec" target="_blank" href="http://www.w3.org/2004/01/rdxh/spec" id="kunm">http://www.w3.org/2004/01/rdxh/spec</a><br>DSL in Groovy: <a title="http://docs.codehaus.org/display/GROOVY/Writing+Domain-Specific+Languages" target="_blank" href="http://docs.codehaus.org/display/GROOVY/Writing+Domain-Specific+Languages" id="bpnh">http://docs.codehaus.org/display/GROOVY/Writing+Domain-Specific+Languages</a> <br><br>filshttp://www.blogger.com/profile/04578829160811273052noreply@blogger.com0tag:blogger.com,1999:blog-7651912408580529938.post-17023902724639926262008-12-02T10:52:00.002-06:002008-12-02T10:54:19.514-06:00REST SOA and multi-parameter databases<b>Thoughts on REST and SOA's for database access with multiple parameters</b><br><br>Following that a URI represents a "resource" then it can become an involved process to create a REST compliant approach to RDBM's based resources that allow users to define multiple parameters to request results in a Resource Oriented Architecture (ROA) approach. <br><br>One begins to view "Plain old XML" (POX) defining parameters and constraints passed over HTTP as a REST approach. However, this tends to border on a basic RPC approach. Especially when the endpoint that receives the POX represents a method to run. Thus we are more Services Oriented Architecture (SOA) now than ROA.<br><br>Alternately a URI template approach quickly becomes complex and also very RPC like when we simply define a syntax for the parsing of a URI into what is honestly still a method call. There is also the issue that even something as simple as .../resource/facetA/facetB is different to our caching and general web architecture stack than ../resource/facetB/facetA even if the result (body) is both associative and commutative in operation with respect to these facets. Arguably a URI doesn't need to be this way, but that seems to imply a URI template again and then it seems there is prior knowledge involved server side. <br><br>One could argue that an aspect of the uniform interface approach in REST is that resources are manipulated via model transforms. So a URI represents a resource and we operate on these resources through application of transformations. Obviously this raises visions of resources as XML and transformations being applied via XSL. Whether this is a valid REST/ROA approach remains as an argument I suspect (or does it?). <br><br>However, if the goal of the architecture is to allow a user to generate a result based on arbitrary parameters and constraints and then allow the capacity to pass a representation of these results along to another user as a resource then the results of the initial request must result in the generation of a new resource. This new resource being a representation of the various parameters and constraints requested from the database. <br><br>Defining that as a constraint of the architecture we are looking for then there are perhaps two questions to answer:<br><br><ol><li>If the endpoint we are passing our parameters to is a method then we are RPC and likely should just call ourselves a SOA and move on. <br></li><ul><li>If "yes" to 1 then could the result of this RPC/SOA call generate a resource that could then be passed along on a ROA side of this architecture?</li></ul><li>If we believe a REST approach is useful here can we define a "resource" that provided a transform or set of operations results in a set of resources that represent our "results"</li><ul><li>One has to be careful not to "create" something we call a resource that simply is a method anyway and thus violates a ROA approach anyway. </li></ul><li>Does a REST approach imply requesting resources with individual parameters/facets and then any subset/intersection or other transform is duty of the client to perform. The results of any multi-parameter request is a set of resource links,<br />not the data. The data the client will use (plot, animate, etc.) comes<br />from those resource links. <br></li><ul><li>This puts undue (or not) burden on the client for large data sets</li><li>This makes passing simple representations of resulting data set to other users/machines problematic. <br></li></ul></ol><br><i>However, REST is optimized for large-grain hypermedia data transfer (Ref: <a title="http://www.ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm" target="_blank" href="http://www.ics.uci.edu/%7Efielding/pubs/dissertation/rest_arch_style.htm" id="v6b4">http://www.ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm</a> ) and thus one arrives at this point wondering if this posting is discussing a fine grained approach to data that is not the forte of REST.</i><br><br><i>One has to consider WS* if you are allowing for RPC anyway. </i>If you are going to open the door for RPC, then why not just take on<br />the WS* stack and benefit (and suffer) from the formality of that? One can argue about the conspiracy<br />of tool developers in the evolution of WS* all you want. However, in<br />the end you are attempting to get a goal achieved and if your goal is fine grained parameter access to database over HTTP then perhaps the SOA/WS*/SOAP approach is what you need (even if it's not what you want). <br><br>Clearly though WS* brings a level of complexity that is not welcome in many places. So then, as mentioned at the start of this post, one looks to POX over HTTP via POST as an interesting approach. <br><br>There is nothing that says the result (or an addition by product) of this service call can't include the generation of a resource or that a resource creation could be explicitly requested when passing a representation/resource is desired. The implication being that such a resource is not implicitly generated for each service call. The "Layered System Constraint" allows us to build up more<br />representations (views) into our resources to accommodate architectural<br />approaches needed in our effort. <br><br>A process file representing a workflow to be conducted can be used to pass to others to allow for "gettable" results. Or could be POST'ed back to the server (another server) to generate a "gettable" result. How then is this "workflow document" constructed? JSON ala CouchDB views (Ref: <a title="http://wiki.apache.org/couchdb/Introduction_to_CouchDB_views?action=show&redirect=Views" target="_blank" href="http://wiki.apache.org/couchdb/Introduction_to_CouchDB_views?action=show&redirect=Views" id="inwu">http://wiki.apache.org/couchdb/Introduction_to_CouchDB_views?action=show&redirect=Views</a> ), XProc (Ref: <a title="http://www.w3.org/TR/xproc/" target="_blank" href="http://www.w3.org/TR/xproc/" id="upcf">http://www.w3.org/TR/xproc/</a> ), GRDDL (Ref: <a title="http://www.w3.org/2004/01/rdxh/spec" target="_blank" href="http://www.w3.org/2004/01/rdxh/spec" id="se7c">http://www.w3.org/2004/01/rdxh/spec</a> ) transform links, other? The exact nature of the construction isn't important at this level in the discussion. <br><br>If application state is the purview of the client, then the client is<br />free to use any method to maintain that state. Itself, the involved<br />application server or some other application server. <br><br /><br><br /><br />Implications for HATEOAS (hypertext as the engine of application state)?<br><br /><br /><ul><li>links can be implicate or explicate</li><li>links in mediums other than HTML (examples)</li></ul><br>So then if the issue of multiple parameters is one that is fine grained and best approached by SOA while a resource driven approach is naturally ROA in nature something like the following can be seen as a zeroth order start on database exposure in a hybrid SOA/ROA approach. <br><br><div><table class="zeroBorder" id="r21p" width="100%" border="0" cellpadding="3" cellspacing="0"><tbody><tr valign="top"><td width="33%"><b>ROA</b><br><br>Represented by URI's like:<br><br>.../res/paramter <br>(a large number of resources returned using HATEOAS to navigate the result tree to data)<br><br>.../res/ID<br>(a single resource data represented based on request type)<br><br>Can use the standard CRUD REST mapping (and all its good and bad points) to manipulate these resources and mimetype/request type approaches to alter the serialization of the data.<br><br></td><td width="33%"><b>ROA with rendered views*</b><br><br>Represented by views on the resources like<br><br>.../res/age/10<br>(implying all elements 10Ma or older)<br><br>.../res/depth/345<br>(implying 345 mbsf or deeper)<br><br>../res/fossilegroup/nanno<br>(resources with data from fossil group Nanno)<br><br>../res/taxon/X<br>(resources with data about taxon X)<br><br>.../res/ics2004/earlyjurasic<br>(resource with data from early Jurassic as defined by ICS 2004 timescale)<br><br><br></td><td width="33%"><b>SOA (WS*, POX via POST, etc.)</b><br><br>Standard SOA method calls via SOAP envelopes. <br>(lots of baggage)<br><br>POX over HTTP<br><br>JSON plus REST<br><br>or even just pushing .JS in general over to a server ala Perserver or FeatherDB. <br></td></tr></tbody></table></div>* All these URI examples are based on paleontology/geology approaches associated with the kind of data CHRONOS works with and may be somewhat opaque at first glance. <br><br /><br /><br><b><i>Resource Oriented Architecture</i></b><br><div id="k-6h" style="padding: 1em 0pt; text-align: center;"><img style="width: 357px; height: 240px;" src="http://docs.google.com/File?id=dgtb8v9j_359ghvk78g9_b"><br><div style="text-align: left;">"Classic" REST in a ROA approach. URI point to individual resource which return their data. The "view" of the data may change based on the request type and other elements of the web architecture also are there to address caching (e-tags) and other elements of scaling. Implementation wise this could be <a title="Jersey" target="_blank" href="https://jersey.dev.java.net/" id="p_93">Jersey</a> or <a title="CouchDB" target="_blank" href="http://incubator.apache.org/couchdb/" id="ydk_">CouchDB</a> or many things. <br></div></div><br><b><i>Resource Oriented Architecture with dynamic creation of views</i></b><br><div id="k3i2" style="padding: 1em 0pt; text-align: center;"><img style="width: 452px; height: 412px;" src="http://docs.google.com/File?id=dgtb8v9j_360hqcj5jct_b"><br><div style="text-align: left;">Adding the ability to create dynamic views is a new level. While CouchDB supports views they have to be registered and indexed. Other packages like Perserver (Ref: <a title="http://www.persvr.org/" target="_blank" href="http://www.persvr.org/" id="mn7.">http://www.persvr.org/</a> ) and <a title="FeatherDB" target="_blank" href="http://www.fourspaces.com/blog/2008/4/11/FeatherDB_Java_JSON_Document_database" id="e11w">FeatherDB</a> allow for views to be dynamically created and run via REST calls. The performance and scalability issues of allowing such dynamic view creation would need to be evaluated. Also the views themselves tend to be Javascript based or based on JSON query/path expression syntax. Though this is not always the case and it should be noted that CouchDB, though requiring prior view creation, supports a wide range of languages for views. <br></div></div><b><i>ROA / SOA hybrid</i></b><br><div id="xo48" style="padding: 1em; text-align: center;"><div id="wo-6" style="padding: 1em 0pt; text-align: center;"><a href="http://docs.google.com/File?id=dgtb8v9j_362q5zvhkfr_b" target="_blank"></a><div id="g5ff" style="padding: 1em 0pt; text-align: center;"><a href="http://docs.google.com/File?id=dgtb8v9j_363cpgzx5dq_b" target="_blank"><img style="width: 100%;" src="http://docs.google.com/File?id=dgtb8v9j_363cpgzx5dq_b"></a></div><div style="text-align: left;">If the goal as stated is to allow fine grained multi-parameter access to a database while leveraging off the scaling and ease of use aspects of REST/ROA then a review of various approaches to a ROA/SOA hybrid is beneficial. Obviously there is a high degree of coupling between the service (method) and client in all these cases. <br><br><i>Seq A (One call, all the data returned)</i><br>Classic service call as either XML-RPC, WS*, POX over HTTP (RPC), JSON over HTTP (RPC) or however you need to communicate a request to a method and get your results back. Whether the method is directly exposed or a more document centric approach where an XML or JSON package is processed is not relevant. <br><br><i>Seq B (One call for ID collection then N calls for matching resources)</i><br>Method call made but rather than data a collection of ID's for matching resource are returned. The client then makes further calls for each matching resource.<br><br><i>Seq C (One call for ID collection, One call for collection resource, N calls for matching resources)</i><br>Method call made which results in the creation of a new resource. The ID of that resource if returned to the client which then calls to it to gather the associated resource ID that are called to return the requested data. <br><br><i>Comments on these approaches</i><br>Obviously each version is getting more and more complex and involving more and more network calls and the inherit latency that creates. Seq C does result in a new resource that can retried via REST compliant GET and can then return a HATEOAS compliant collection of resources to be retrieved<br><br>Accessing (opening and closing) N number of resources over the network is also more involved than returning one large document of all the resources combined. The first approach does lend itself well to a scaled out architecture though. One might consider the utility of AtomPub (Ref: http://www.atompub.org/) in this architecture.<br><br>Not mentioned yet but important especially with creating new resource dynamically is the use of http codes (Ref: <a title="http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html" target="_blank" href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html" id="ta3y">http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html</a> ). In particular 201 Created or 303 See Other may be relevant. Obviously using the existing web architecture as much as possible has value. The general shunning of the web architecture by WS* services has not been to its benefit in the authors opinion. <br><br><i><b>Conclusion</b></i><br>The large grained focus of REST makes it hard to accommodate access to database with a large number of parameters and criteria exposed to clients for them to construct queries against. <br><br>SOA based approaches that are more RPC in nature match this need better but introduce strong coupling between client and server and are generally more complex to create, invoke and evolve. <br><br>An architecture than combines ROA and SOA approaches is not impractical<br><br><ul><li>A ROA (REST) approach for easy and highly scalable access to data based on basic parameters and a number of pre-defined "views" to the most common queries</li><li>Service/method calls to support multi-parameter calls to a data source to allow arbitrarily complex requests</li><li>Service/method calls that allow new "views" to be registered when it is felt than they represent a new and potentially popular view to the data or when a passable, REST compliant resource creation is desirable</li></ul><br>How an implementation of this might be constructed is a future topic.<br></div></div></div>take care<br>Doug<br><br>Refs used for this document:<br><a title="http://www.slideshare.net/alan.dean/separating-rest-facts-from-fallacies-presentation/" target="_blank" href="http://www.slideshare.net/alan.dean/separating-rest-facts-from-fallacies-presentation/" id="jcwo">http://www.slideshare.net/alan.dean/separating-rest-facts-from-fallacies-presentation/</a> <br><a title="http://josh-in-antarctica.blogspot.com/2008/11/restful-query-urls-cont.html" target="_blank" href="http://josh-in-antarctica.blogspot.com/2008/11/restful-query-urls-cont.html" id="b9.n">http://josh-in-antarctica.blogspot.com/2008/11/restful-query-urls-cont.html</a> <br><a title="http://www.eherenow.com/soapfight.htm" target="_blank" href="http://www.eherenow.com/soapfight.htm" id="kfnn">http://www.eherenow.com/soapfight.htm</a> <br><a title="http://www.informationweek.com/blog/main/archives/2008/08/rest_vs_soap_ro.html" target="_blank" href="http://www.informationweek.com/blog/main/archives/2008/08/rest_vs_soap_ro.html" id="ou6t">http://www.informationweek.com/blog/main/archives/2008/08/rest_vs_soap_ro.html</a> <br><a title="http://www.25hoursaday.com/weblog/2008/08/24/RESTfulJSONBringingRESTAndRPCCloserTogether.aspx" target="_blank" href="http://www.25hoursaday.com/weblog/2008/08/24/RESTfulJSONBringingRESTAndRPCCloserTogether.aspx" id="ecl0">http://www.25hoursaday.com/weblog/2008/08/24/RESTfulJSONBringingRESTAndRPCCloserTogether.aspx</a> <br><br>filshttp://www.blogger.com/profile/04578829160811273052noreply@blogger.com0tag:blogger.com,1999:blog-7651912408580529938.post-87155271641532457092008-11-18T10:44:00.001-06:002008-11-18T10:45:57.560-06:00REST via URI's and Body RepresentationsI've been thinking about REST URI's and their structure for a while now and been talking with my friend <a title="Josh" target="_blank" href="http://josh-in-antarctica.blogspot.com/" id="m59e">Josh</a> about these. As an exercise to try to codify my own views on this I have written up some of my thoughts on this topic. <br><br><b><i>URI templates:</i></b><br>First order approach to REST is the URI used each day via GET <br> <br>Good:<br><ul><li>Can become book marks (thus IM'd, twitterd, and emailed to ones content)</li><li>Simple and easy to code to in a variety of languages and API's</li><li>Classic resource representation</li></ul>Bad:<br><ul><li>Limited ability to pass multiple parameters</li><li>Limited ability to deal with more than one facet (at least with the example template below, any template could be established, but then everyone calling needs to know it)<br></li><li>Require a template even to pass one parameter</li><li>Can easily start to try and carry to much info with all sorts of verbs and values</li></ul><br>So by example a URI template might look like:<br><br>.../resource/facet (where the facet is some value like ../lithology/sand)<br><br>Then a template could define something like<br><br>.../resource/facet/[match] Given a single value after facet, try to match it (equals or substring?)<br>.../resource/facet/[/min/max] Given two values, assume they are a numerical range to search for<br><br>Of course, the issues already start to build up. In the single value case we could just try to match but if we want a sub-string match then we are assuming we are not in a numerical environment. Or we are requiring the implementation of the service to check for primitive data types which may or may not work. In the second case our min max has to be sure to address numeric primitives (<a title="easy to check" target="_blank" href="http://www.devx.com/tips/Tip/39748?trk=DXRSS_JAVA" id="pgbs">easy to check</a> ) but also we may want to address non-numeric ranges (January/March)<br><br>There is also an interesting use of language that can be done here. Where <i>resource</i> (singular) requires an id to define a single resource. Conversely, a plural <i>resources</i> then would indicate an additional facet (either plural or singular) to carry the template forward.<br><br>So:<br>.../resources/sand (all resources with sand)<br>.../resource/34/sand (sand attribute (facet) of resource 34)<br><br>The use of singular and plural attributes in URI templates seems logical. As a complete aside I have often wondered if a more <a title="Latin style grammar" target="_blank" href="http://en.wikipedia.org/wiki/Latin_grammar" id="yj-3">Latin style grammar</a> would allow more descriptive URI's to be formed since word order is less important and meaning is carried in word itself. Totally impractical though of course. <br><br><b><i>URI templates with representation in the body</i></b><br>So if our template is not enough, exploit the fact that the web architecture doesn't just pass URI's but additional elements in a request/response including headers and a body(representation).<br><br>So now our template (../resource/facet) might get a POST call with a payload of XML or <a title="JSON" target="_blank" href="http://blogs.sun.com/enterprisetechtips/entry/configuring_json_for_restful_web" id="kgid">JSON</a> (pick your flavor I guess, how about <a title="Microformats in REST" target="_blank" href="http://microformats.org/wiki/rest" id="t7ni">Microformats in REST</a> ). That payload is free to define a wide variety of parameters and actions.<br><br>Good:<br><ul><li>Far more flexible in terms of defining what we want to do with a resource facet</li><li>A response payload can define downstream (next) actions to take. Thus giving us a kind of client moderated work flow (ie, 201 created or 303 see other next steps to take)</li></ul>Bad<br><ul><li>These workflows really can't be very complex since we are "waiting around" for the completion with our session. Really only good for events which are "quick". *<br></li><li>The XM or JSON schema/language for the payload has to be agreed on before hand (though this is really true of the URI template too)</li></ul><br>As an aspect of this the service might establish (via the 201 or 303 codes) a new URI that is a GET'able representation of the response of this (complete with etag and everything). The use of status codes (Ref:<br /><a title="http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html" target="_blank" href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html" id="u82_">http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html</a> ) needs to be<br />integral part of any REST approach and REST clients need to realize<br />that if they are going to use the web architecture to get a resource<br />they must look for and deal with the status codes of that architecture.<br /><br><br>However, there are issues with the service provider caching requests and responses:<br><br><ul><li>How long does the service provider keep this? <br></li><li>A service may be called thousands of times by a single person in a sinle session, it doesn't scale (does it?) to keep and etag them all<br></li><li>Just let the web arch do the caching</li></ul><br>Perhaps, one might allow this stored representation of the response to be the URI plus the POST'ed request and then if some does a GET on that URI representing this earlier POST request allow whatever web caching there is to address that scaling Ie:<br><br>POST request to ../resource/facet <br>response could be the actual data with a 201 created URI for future referencing. That new URI, say (../resource/request/[id]) could then in the future be called via GET. All the service provider is storing in that case is the URI plus the request payload, since the results would be rebuilt by the service engine (and then available for near term caching via existing web architecture). Etags (hash sums) could be used to ensure requests are to a specific resource. <br><div id="zvr_" style="padding: 1em; text-align: center;"><div id="qifx" style="padding: 1em; text-align: center;"><img style="width: 640px; height: 454.316px;" src="http://docs.google.com/File?id=dgtb8v9j_353gdkqfjfm_b"><br></div></div>One element I have been working to deal with in REST approaches is the whole "hypermedia as engine of application state". It's a guiding principle and yet sometime hard to understand what exactly it is or if an approach is in compliance with it at times.<br><br>One approach would seem to be to say that all elements of a response must define down stream references as URI's or other (are there other?) hypermedia compliant references. I am not sure if if an img tag or microformat or RDFa embedded content is compliant with this "application state" approach. Also, how exactly does this impact the use use of XML or JSON. While XML could be embedded into XHTML or similar effort done with microformats, does the use of JSON in the response to a REST URI mean that "hypermedia as engine of application state" is not even an option? JSON embedded in XHTML or microformats perhaps might address this but then one argument the JSON proponents make is that they are tossing out the cruft. One could also place the URI into the JSON as a value. (Josh, I think you are looking into this.. I will be curious to read about)<br><br>Indeed it's also interesting to look at <a title="GRDDL" target="_blank" href="http://www.w3.org/TR/2007/NOTE-grddl-scenarios-20070406/" id="y82r">GRDDL</a> and note how it approaches the walking and extraction of information (RDF) from documents (like XHTML) when looking at issues of REST. <br><br>take care<br>Doug<br><br>Ref: <br><a title="http://josh-in-antarctica.blogspot.com/2008/10/restful-query-urls.html" target="_blank" href="http://josh-in-antarctica.blogspot.com/2008/10/restful-query-urls.html" id="uvw1">http://josh-in-antarctica.blogspot.com/2008/10/restful-query-urls.html</a> <br><a title="http://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven" target="_blank" href="http://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven" id="atju">http://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven</a> <br><a title="http://oreilly.com/catalog/9780596529260/" target="_blank" href="http://oreilly.com/catalog/9780596529260/" id="nrwu">http://oreilly.com/catalog/9780596529260/</a> <br><a title="http://www.infoq.com/articles/tilkov-rest-doubts" target="_blank" href="http://www.infoq.com/articles/tilkov-rest-doubts" id="b5ju">http://www.infoq.com/articles/tilkov-rest-doubts</a> <br><a title="http://www.infoq.com/articles/mark-baker-hypermedia" target="_blank" href="http://www.infoq.com/articles/mark-baker-hypermedia" id="iem9">http://www.infoq.com/articles/mark-baker-hypermedia</a> <br><a title="http://microformats.org/wiki/rest/json#Proposals" target="_blank" href="http://microformats.org/wiki/rest/json#Proposals" id="qbe4">http://microformats.org/wiki/rest/json#Proposals</a> <br><a title="http://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven" target="_blank" href="http://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven" id="hlz:">http://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven</a> <br><a title="http://www.w3.org/2004/01/rdxh/spec" target="_blank" href="http://www.w3.org/2004/01/rdxh/spec" id="r9z0">http://www.w3.org/2004/01/rdxh/spec</a> <br><br>* With regard to only good for quick work flows.. one must agree that it is easy to send in an event that may take a long time to run and simply reqister a message to a queue (ATOM feed perhaps) that has the current status and the next event on status update to undertake. This turns the burden of monitoring and moving messages through the queue to the client in this case, but it does allow for a disconnected work flow in compliance with web architectural approaches. <br>filshttp://www.blogger.com/profile/04578829160811273052noreply@blogger.com0tag:blogger.com,1999:blog-7651912408580529938.post-27626608234236969102008-11-06T15:32:00.004-06:002008-11-06T16:06:07.764-06:00JSR-311 and JPA with Intellij 8<a title="Intellij 8 recently came out" href="http://www.jetbrains.com/idea/" id="n48b">IntelliJIDEA 8 recently came out</a> and I wanted to take the time to try out a couple of interesting technologies as a means to test out the new version. Specifically <a title="JSR-311" target="_blank" href="http://jcp.org/en/jsr/detail?id=311" id="sy0j">JSR-311</a> (Via <a title="Jersey" target="_blank" href="https://jersey.dev.java.net/" id="hl.0">Jersey</a> ) and <a title="Java Persistence API" target="_blank" href="http://java.sun.com/javaee/overview/faq/persistence.jsp" id="x2c7">Java Persistence API</a> (JPA) via <a title="Hibernate" target="_blank" href="http://www.hibernate.org/" id="hvmd">Hibernate</a>. I created a simple application using these two technologies. The data store I used in this was a PostgreSQL database with lithological data. Note I actually did this with version 8.0 M1 of IntelliJ (build 8664) and I will go on the assumption nothing changed in this regard to the 8.0 final annouced just this last day or so. <br><br>The first step was to register up the database as a datasource. Under the TOOLS menu is the DATA SOURCE... menu item. Selecting this allow us to register our database as a source through the dialog below:<br><br><div id="z_qe" style="padding: 1em 0pt; text-align: left;"><img style="width: 572px; height: 670px;" src="http://docs.google.com/File?id=dgtb8v9j_336hrtprwht_b"></div><br>Once this is done we can go ahead and create out new project. You wouldn't have to register a data source first, but for sake of a canonical "10 minute demo" the net seems to love it makes life a bit easier as we are about to see.<br><br>Go ahead and start a new project with FILE -> NEW PROJECT. Use the default "Create project from scratch" and name your project. We will be building a "Java Module", the default option when creating a project. Pick the default option for the src directory and when you get the "New Project" window selections options like in figure 2 below:<br><br><div id="a9tx" style="padding: 1em 0pt; text-align: left;"><img style="width: 841px; height: 696px;" src="http://docs.google.com/File?id=dgtb8v9j_337gsnznpgz_b"></div><br>I did NOT select "Hibernate" at this stage, though I did select it under the pull down for the "JavaEE Persistence" option. Also, under "Web Application" -> "WebServices" select the "Jersey" option.<br><br>The project will start to be fleshed out and you will be prompted to import the database schema (assuming you selected the "Import database schema" option). Now select the source you created via the "Data Source Properties" dialog. For me this was "Lith". This database is just a test database for now so it consists of only a few tables with no defined relations at this time. For purposes of this simple demo that should be fine. <br><br>Go ahead and select a datasource and define a package name for the entities to be generated into. <br><br><div id="k6lh" style="padding: 1em 0pt; text-align: left;"><img style="width: 645px; height: 882px;" src="http://docs.google.com/File?id=dgtb8v9j_338jx8cqjg2_b"></div><br><br>You will be prompted that the OR mapping is about to be generated, go ahead and let it start. Depending on your database and your machine this will take a minute or so. <br><br>Once this was generated I was confronted with an Error that the org.hibernate.ejb.HibernatePersistence class/package could not be resolve for its reference in the <i>persistence.xml</i> file that was generated for us by this process. Long story short I went to http://www.hibernate.org/6.html and downloaded the annotations, entitymanager and core (distribution) packages from here. <br><br>Setting up the libraries (as is often the case) was the only real tedious part and in the end the library collection looked like the following figure:<br><i>NOTE: I have to wonder if I would have selected "Hibernate" in the "New Project" dialog above (leaving the import and class generation option unchecked) if Intellij would have imported the Needed Hibernate libraries for me.<br>NOTE 2: Don't forget to add in your database driver too (not that I did that or anything) </i><br><br><div id="vrr_" style="padding: 1em 0pt; text-align: left;"><img style="width: 693px; height: 461px;" src="http://docs.google.com/File?id=dgtb8v9j_339wqrt4tf8_b"></div><br>Parallel to all this JPA/Hibernate stuff Intellij has created a simple JSR-311 (Jersey) class for us with the following default structure:<br><a name="l1"><span class="s0"></span></a><br><pre><a name="l7"><span class="s1"></span></a><a style="font-family: Courier New; color: rgb(0, 0, 0);" name="l8"><span class="s2">package </span><span class="s1">example; <br></span></a><a style="font-family: Courier New; color: rgb(0, 0, 0);" name="l9"> <br></a><a style="font-family: Courier New; color: rgb(0, 0, 0);" name="l10"><span class="s2">import </span><span class="s1">com.sun.net.httpserver.HttpServer; <br></span></a><a style="font-family: Courier New; color: rgb(0, 0, 0);" name="l11"><span class="s2">import </span><span class="s1">com.sun.jersey.api.container.httpserver.HttpServerFactory; <br></span></a><a style="font-family: Courier New; color: rgb(0, 0, 0);" name="l12"><span class="s2">import </span><span class="s1">java.io.IOException; <br></span></a><a style="font-family: Courier New; color: rgb(0, 0, 0);" name="l13"> <br></a><a style="font-family: Courier New; color: rgb(0, 0, 0);" name="l14"><span class="s2">import </span><span class="s1">javax.ws.rs.GET; <br></span></a><a style="font-family: Courier New; color: rgb(0, 0, 0);" name="l15"><span class="s2">import </span><span class="s1">javax.ws.rs.ProduceMime; <br></span></a><a style="font-family: Courier New; color: rgb(0, 0, 0);" name="l16"><span class="s2">import </span><span class="s1">javax.ws.rs.Path; <br></span></a><a style="font-family: Courier New; color: rgb(0, 0, 0);" name="l17"> <br></a><a style="font-family: Courier New; color: rgb(0, 0, 0);" name="l18"><span class="s0">// The Java class will be hosted at the URI path "/helloworld"</span><span class="s1"> <br></span></a><a style="font-family: Courier New; color: rgb(0, 0, 0);" name="l19">@Path(<span class="s3">"/helloworld"</span><span class="s1">) <br></span></a><a style="font-family: Courier New; color: rgb(0, 0, 0);" name="l20"><span class="s2">public class </span><span class="s1">HelloWorld { <br></span></a><a style="font-family: Courier New; color: rgb(0, 0, 0);" name="l21"> <span class="s0">// The Java method will process HTTP GET requests</span><span class="s1"> <br></span></a><a style="font-family: Courier New; color: rgb(0, 0, 0);" name="l22"> @GET <br></a><a style="font-family: Courier New; color: rgb(0, 0, 0);" name="l23"> <span class="s0">// The Java method will produce content identified by the MIME Media type "text/plain"</span><span class="s1"> <br></span></a><a style="font-family: Courier New; color: rgb(0, 0, 0);" name="l24"> @ProduceMime(<span class="s3">"text/plain"</span><span class="s1">) <br></span></a><a style="font-family: Courier New; color: rgb(0, 0, 0);" name="l25"> <span class="s2">public </span><span class="s1">String getClichedMessage() { <br></span></a><a style="font-family: Courier New; color: rgb(0, 0, 0);" name="l26"> <span class="s0">// Return some cliched textual content</span><span class="s1"> <br></span></a><a style="font-family: Courier New; color: rgb(0, 0, 0);" name="l27"> <span class="s2">return </span><span class="s3">"Hello World"</span><span class="s1">; <br></span></a><a style="font-family: Courier New; color: rgb(0, 0, 0);" name="l28"> } <br></a><a style="font-family: Courier New; color: rgb(0, 0, 0);" name="l29"> <br></a><a style="font-family: Courier New; color: rgb(0, 0, 0);" name="l30"> <span class="s2">public static void </span><span class="s1">main(String[] args) </span><span class="s2">throws </span><span class="s1">IOException { <br></span></a><a style="font-family: Courier New; color: rgb(0, 0, 0);" name="l31"> HttpServer server = HttpServerFactory.create(<span class="s3">"http://localhost:9998/"</span><span class="s1">); <br></span></a><a style="font-family: Courier New; color: rgb(0, 0, 0);" name="l32"> server.start(); <br></a><a style="font-family: Courier New; color: rgb(0, 0, 0);" name="l33"> <br></a><a style="font-family: Courier New; color: rgb(0, 0, 0);" name="l34"> System.out.println(<span class="s3">"Server running"</span><span class="s1">); <br></span></a><a style="font-family: Courier New; color: rgb(0, 0, 0);" name="l35"> System.out.println(<span class="s3">"Visit: http://localhost:9998/helloworld"</span><span class="s1">); <br></span></a><a style="font-family: Courier New; color: rgb(0, 0, 0);" name="l36"> System.out.println(<span class="s3">"Hit return to stop..."</span><span class="s1">); <br></span></a><a style="font-family: Courier New; color: rgb(0, 0, 0);" name="l37"> System.in.read(); <br></a><a style="font-family: Courier New; color: rgb(0, 0, 0);" name="l38"> System.out.println(<span class="s3">"Stopping server"</span><span class="s1">); <br></span></a><a style="font-family: Courier New; color: rgb(0, 0, 0);" name="l39"> server.stop(<span class="s4">0</span><span class="s1">); <br></span></a><a style="font-family: Courier New; color: rgb(0, 0, 0);" name="l40"> System.out.println(<span class="s3">"Server stopped"</span><span class="s1">); <br></span></a><a style="font-family: Courier New; color: rgb(0, 0, 0);" name="l41"> } <br></a><a style="font-family: Courier New; color: rgb(0, 0, 0);" name="l42">}</a></pre><br>I wont bother to break down the structure or go into the various annotations used in JSR-311. You can Google up quite a bit of material on all that of far higher quality than I could produce. Starting at the Jersey site (<a title="https://jersey.dev.java.net/" href="https://jersey.dev.java.net/" id="qf-e">https://jersey.dev.java.net/</a> ) is as good a place as any as I think it's likely the most evolved JSR-311 implementation at this time. <br><br>For simplicity we will leave the main() method alone and modify a few other elements in this class. First I changed the class level @Path annotation to:<br><br><b>@Path("/lith);</b><br><br>and also added in a create and close method for the JPA EntityManagerFactory. For fun I modded the getClicedMessage to parse out the URI path sent to it via a:<br><br><b>@Path("location/{latlong}")</b> annotaion along with a <b>@PathParam("latlong") String latlong</b> annotation. The later requires the <i>javax.ws.rs.PathParam</i> import. <br><br>So our final code looks like this (interesting parts in bold):<br><br><span style="font-family: Courier New;">package example;</span><br style="font-family: Courier New;"><br style="font-family: Courier New;"><span style="font-family: Courier New;">import com.sun.jersey.api.container.httpserver.HttpServerFactory;</span><br style="font-family: Courier New;"><span style="font-family: Courier New;">import com.sun.net.httpserver.HttpServer;</span><br style="font-family: Courier New;"><br style="font-family: Courier New;"><span style="font-family: Courier New;">import javax.persistence.EntityManager;</span><br style="font-family: Courier New;"><span style="font-family: Courier New;">import javax.persistence.EntityManagerFactory;</span><br style="font-family: Courier New;"><span style="font-family: Courier New;">import javax.persistence.Persistence;</span><br style="font-family: Courier New;"><span style="font-family: Courier New;">import javax.persistence.Query;</span><br style="font-family: Courier New;"><span style="font-family: Courier New;">import javax.ws.rs.GET;</span><br style="font-family: Courier New;"><span style="font-family: Courier New;">import javax.ws.rs.Path;</span><br style="font-family: Courier New;"><span style="font-family: Courier New;">import javax.ws.rs.PathParam;</span><br style="font-family: Courier New;"><span style="font-family: Courier New;">import javax.ws.rs.ProduceMime;</span><br style="font-family: Courier New;"><span style="font-family: Courier New;">import java.util.List;</span><br style="font-family: Courier New;"><br style="font-family: Courier New;"><span style="font-family: Courier New;">@Path("/lith")</span><br style="font-family: Courier New;"><span style="font-family: Courier New;">public class HelloWorld {</span><br style="font-family: Courier New;"><br style="font-family: Courier New;"><span style="font-family: Courier New;"> <b>private EntityManagerFactory emf = null;</b></span><b><br style="font-family: Courier New;"><br style="font-family: Courier New;"><span style="font-family: Courier New;"> protected void createEMF() {</span><br style="font-family: Courier New;"><span style="font-family: Courier New;"> emf = Persistence.createEntityManagerFactory("NewPeristenceUnit");</span><br style="font-family: Courier New;"><span style="font-family: Courier New;"> }</span><br style="font-family: Courier New;"><br style="font-family: Courier New;"><span style="font-family: Courier New;"> protected void closeEMF() throws Exception {</span><br style="font-family: Courier New;"><span style="font-family: Courier New;"> emf.close();</span><br style="font-family: Courier New;"><span style="font-family: Courier New;"> }</span></b><br style="font-family: Courier New;"><br style="font-family: Courier New;"><span style="font-family: Courier New;"> <b>public String doQuery(String latlong) {</b></span><b><br style="font-family: Courier New;"><span style="font-family: Courier New;"> createEMF();</span><br style="font-family: Courier New;"><span style="font-family: Courier New;"> StringBuffer sb = new StringBuffer();</span><br style="font-family: Courier New;"><span style="font-family: Courier New;"> EntityManager em = emf.createEntityManager();</span><br style="font-family: Courier New;"><span style="font-family: Courier New;"> Query query = em.createQuery("select c."+latlong+" from CmpEntity c");</span><br style="font-family: Courier New;"><span style="font-family: Courier New;"> List<Double> list = query.getResultList();</span><br style="font-family: Courier New;"><span style="font-family: Courier New;"> for (Double c : list) {</span><br style="font-family: Courier New;"><span style="font-family: Courier New;"> sb.append(latlong + ": " + c.toString() + "\n");</span><br style="font-family: Courier New;"><span style="font-family: Courier New;"> }</span><br style="font-family: Courier New;"><span style="font-family: Courier New;"> try {</span><br style="font-family: Courier New;"><span style="font-family: Courier New;"> closeEMF();</span><br style="font-family: Courier New;"><span style="font-family: Courier New;"> } catch (Exception e) {</span><br style="font-family: Courier New;"><span style="font-family: Courier New;"> e.printStackTrace();</span><br style="font-family: Courier New;"><span style="font-family: Courier New;"> }</span><br style="font-family: Courier New;"><span style="font-family: Courier New;"> return sb.toString();</span><br style="font-family: Courier New;"><span style="font-family: Courier New;"> }</span><br style="font-family: Courier New;"><br style="font-family: Courier New;"><span style="font-family: Courier New;"> @GET</span><br style="font-family: Courier New;"><span style="font-family: Courier New;"> @Path("location/{latlong}")</span><br style="font-family: Courier New;"><span style="font-family: Courier New;"> @ProduceMime("text/plain")</span><br style="font-family: Courier New;"><span style="font-family: Courier New;"> public String getClichedMessage(@PathParam("latlong") String latlong) {</span><br style="font-family: Courier New;"><span style="font-family: Courier New;"> return doQuery(latlong);</span><br style="font-family: Courier New;"><span style="font-family: Courier New;"> }</span></b><br style="font-family: Courier New;"><br style="font-family: Courier New;"><span style="font-family: Courier New;"> public static void main(String[] args) throws Exception {</span><br style="font-family: Courier New;"><br style="font-family: Courier New;"><span style="font-family: Courier New;"> HttpServer server = HttpServerFactory.create("http://localhost:9998/");</span><br style="font-family: Courier New;"><span style="font-family: Courier New;"> server.start();</span><br style="font-family: Courier New;"><br style="font-family: Courier New;"><span style="font-family: Courier New;"> System.out.println("Server running");</span><br style="font-family: Courier New;"><span style="font-family: Courier New;"> System.out.println("Visit: http://localhost:9998/helloworld");</span><br style="font-family: Courier New;"><span style="font-family: Courier New;"> System.out.println("Hit return to stop...");</span><br style="font-family: Courier New;"><span style="font-family: Courier New;"> System.in.read();</span><br style="font-family: Courier New;"><span style="font-family: Courier New;"> System.out.println("Stopping server");</span><br style="font-family: Courier New;"><span style="font-family: Courier New;"> server.stop(0);</span><br style="font-family: Courier New;"><span style="font-family: Courier New;"> System.out.println("Server stopped");</span><br style="font-family: Courier New;"><span style="font-family: Courier New;"> }</span><br style="font-family: Courier New;"><span style="font-family: Courier New;">}</span><br><br>A few things to note:<br><ul><li>The List<Double> list = query.getResultList(); is not good(tm) as we have an unchecked assignment of java.util.List to java.util.List<java.lang.Double> Better would be to use something like List<CmpEntity> in this case and alter our query to something like <i>Query query = em.createQuery("select c from CmpEntity c");</i> However, I ran into some issues with return in my entity class being null. Perhaps I needed to allow certain columns to be null, I am not certain of that, but I was more interested in the path than resolving the query. Likely I will resolve this issue as the next step in this experiment. <br></li><li>Parallel to the aboe the point my List<Double> work since I know that the return from the query is a Double</li><li>I altered things a bit in the above example so that I could use both /lith/location/longitude and /lith/location/latitude as calling URI's. This works of course since I know ahead of time what my column names were that I wanted to use for this test and that they were of type Double to address the above two points. This is whay I use the @Path and @PathParam annotations in the getClichedMessage method and strip out and pass along elements of the URI. <br></li><li>Like you would NOT want (ie DO NOT WANT) to use the column names in your mapped data source in your REST URI. You would establish your URI template to whatever degree you wished and then use a more logical approach to generating the structure and content of your services and their replies. This post is talking about the pipeline and is not worried about the very important architectural issue of mapping resources to URI's. Take a look at my friends <a title="Josh's post on that topic" target="_blank" href="http://josh-in-antarctica.blogspot.com/2008/10/restful-query-urls.html" id="gxga">Josh's post on that topic</a> and the nice InfoQ <a title="How to GET a cup of Coffee" href="http://www.infoq.com/articles/webber-rest-workflow" id="c.1z">How to GET a cup of Coffee</a> posting about all that.</li></ul><br>One last important point in all this. I got an error: <b>No identifier specified for entity</b>. When I looked at my generated entity classes all the columns recieved a @Basic and @Column(...) annotation. At least one however needs to have a @Id annotation. Reference the Hibernate FAQ at <a title="http://www.hibernate.org/329.html#A6" href="http://www.hibernate.org/329.html#A6" id="gnrv">http://www.hibernate.org/329.html#A6</a> for more info. I had a unique integer column I could do this to and so I alterd my effected entity classes with @Id @Column(...) on one of the methods. You, however, should not see this as I suspect it was due to the fact I never botherd to set a unique key column in that table on my database (don't tell my DBA). <br><br>That's about it. At this point you should be able to run the project and make calls and get data out. The built in REST client for IntelliJIDEA 8 is quite nice though perhaps not as advanced as the nice <a title="rest-client" href="http://code.google.com/p/rest-client/" id="j04q">rest-client</a> project. In my case I parsed out the URI just for the sake of some fun. With all the elements connected one can move on to more interesting "real world" applications of the JSR-311 and JPA API's used in this simple demo.<br><br><br>filshttp://www.blogger.com/profile/04578829160811273052noreply@blogger.com0tag:blogger.com,1999:blog-7651912408580529938.post-48944437349586643032008-06-19T09:32:00.004-05:002008-06-19T10:53:33.482-05:00Sproutcore on Ubuntu 8.04I was interested in the Sproutcore (ref: <a href="http://www.sproutcore.com/">http://www.sproutcore.com</a>) package that got some attention from the Apple developers conference. (<a href="http://www.appleinsider.com/articles/08/06/16/apples_open_secret_sproutcore_is_cocoa_for_the_web.html">AppleInsider</a> article / <a href="http://twit.tv/mbw94">MacBreak weekly podcast where Sproutcore is talked about</a>)<br /><br />Getting it to work on Ubuntu (verison 8.04 for me) took a few steps I thought I would place here. Of course first you need to make sure you have Ruby gems and Rails installed.<br /><br /><span style="font-family:courier new;">apt-get install rubygems rails</span><br /><br />one items you may miss to install though is the dev package. Be sure to do<br /><br /><span style="font-family:courier new;">apt-get install ruby1.8-dev</span><br /><br />This will show itself when Ruby trys to build native extensions with an error like:<br /><br /><span style="font-style: italic;font-family:courier new;" >extconf.rb:1:in `require': no such file to load -- mkmf (LoadError)</span><br /><br />once you have this done you can do:<br /><br /><span style="font-family:courier new;">gem install sproutcore</span><br /><br />and accept all its questions (it will take a while as several packages need to be installed)<br /><br />At this point you will need to:<br /><br /><span style="font-family:courier new;">cd /var/lib/gems/1.8/gems/sproutcore-0.9.10/bin</span><br /><span style="font-family:courier new;">chmod 755 *</span><br /><br />to make these scripts executable and also add this location to your path with:<br /><br /><span style="font-family:courier new;">export PATH=/var/lib/gems/1.8/gems/sproutcore-0.9.10/bin:$PATH</span><br /><br />or just add them to your shell init scripts like .bashrc<br /><br />At this point you can start down the tutorial examples at: <a href="http://www.sproutcore.com/documentation/hello-world-tutorial/">http://www.sproutcore.com/documentation/hello-world-tutorial/</a><br /><br />take care<br />Doug<br /><br />[update]<br />After talking with my friend <a href="http://josh-in-antarctica.blogspot.com/">Josh</a> I thought I should add in a little note pointing to information related to <a href="http://ajaxian.com/archives/an-interview-with-280-north-on-objective-j-and-cappuccino">Objective-J</a> by 280North of <a href="http://280slides.com/">280Slides</a> development as another data point in some of this Javascript talk.filshttp://www.blogger.com/profile/04578829160811273052noreply@blogger.com0tag:blogger.com,1999:blog-7651912408580529938.post-6837987624661142142008-06-06T11:27:00.001-05:002008-06-06T11:30:11.691-05:005 Groovy / Grails recommendationsSo I was asked about what my top five recommendations would be for Groovy/Grails resources so I thought I would post them here to get feed back and other ideas from the community. I decided to break it up into five categories.<br id="azbk0"><br id="azbk2"><b id="emzr0">1) Web sites</b><br id="azbk3">Of course the top two would have to be the main <a title="Groovy" target="_blank" href="http://groovy.codehaus.org/" id="my4g">Groovy</a> and <a title="Grails" target="_blank" href="http://grails.org/" id="onv3">Grails</a> sites themselves. Not just because they are the home of the respective projects but because both truly are good resources with plenty of examples, documentations and links to mailing lists and other resources. Others not associated with elements already to be placed in following categories might be the <a title="Spring" target="_blank" href="http://www.springframework.org/" id="nej4">Spring</a> and <a title="Hibernate" target="_blank" href="http://www.hibernate.org/" id="niia">Hibernate</a> sites. <br id="azbk4"><br id="azbk5"><b id="emzr1">2) Podcasts</b><br id="azbk6"><a title="Sven Haige" target="_blank" href="http://hansamann.wordpress.com/" id="h3if">Sven Haige</a> started the <a title="Grails podcast" target="_blank" href="http://grails.org/Grails+Podcast" id="m2l2">Grails podcast</a> and was recently joined by <a title="Glen Smith" target="_blank" href="http://blogs.bytecode.com.au/glen/" id="zd6:">Glen Smith</a>. They do a wonderful job with the podcast and it's a great resource for the community. <br id="tn5b0"><a title="The Java Posse" target="_blank" href="http://javaposse.com/" id="z82v">The Java Posse</a> is high water mark in technical podcasts and I also enjoy <a title="Software Engineering Radio" target="_blank" href="http://www.se-radio.net/" id="kpcv">Software Engineering Radio</a>. <br id="a2900"><br id="azbk8"><b id="emzr2">3) RSS feeds</b><br id="xp620">There are a lot of blogs, rss feeds, etc. that a person can track down. The following three aggregate such feeds and provide a good starting point for locating feeds you resonate with:<br id="ugsh0"><a title="http://groovyblogs.org" target="_blank" href="http://groovyblogs.org" id="p8cu">http://groovyblogs.org</a> <a title="http://groovy.dzone.com/" target="_blank" href="http://groovy.dzone.com/" id="dw6b">http://groovy.dzone.com/</a> <a title="http://www.groovyongrails.com/" target="_blank" href="http://www.groovyongrails.com/" id="nlcq">http://www.groovyongrails.com/</a> <br id="xp621"><br id="xp622"><b id="emzr3">4) Software</b><br id="xp623">Focusing on IDE's the first I would have to recommend is <a title="IntelliJ IDEA" target="_blank" href="http://www.jetbrains.com/idea/index.html" id="b92e">IntelliJ IDEA</a> as its Grails/Groovy support is top notch. It's commercial, but well worth the cost and I believe the best of the those out there for Groovy Grails work period. <br id="osho0"><br id="osho1">Following this I would (personally) would go with<a title="Netbeans 6.1" target="_blank" href="http://www.netbeans.org/" id="onzy">Netbeans 6.1</a> as it also now has Grails support. However, you need to use <b id="yxpm0"><i id="yxpm1">6.1 development builds</i></b>. The plugin home page is at: <a title="http://plugins.netbeans.org/PluginPortal/faces/PluginDetailPage.jsp?pluginid=6265" target="_blank" href="http://plugins.netbeans.org/PluginPortal/faces/PluginDetailPage.jsp?pluginid=6265" id="nt:b">http://plugins.netbeans.org/PluginPortal/faces/PluginDetailPage.jsp?pluginid=6265</a> <br id="osho2"><br id="osho3">Eclipse also has support and in fact was the first to do so if I recall correctly. However, I have not heard much about it and suspect its support has not been a high priority. Many love <a title="Textmate" target="_blank" href="http://macromates.com/" id="a6q1">Textmate</a> (for good reason) and its various bundles and while on the non-IDE side of software one could likely do a lot with <a title="Jedit" target="_blank" href="http://www.jedit.org/" id="uslq">Jedit</a> as well (I have). <br id="m3y10"><br id="m3y11">Both <a title="Teamcity" target="_blank" href="http://www.jetbrains.com/teamcity/index.html" id="ggu_">Teamcity</a> and <a title="Hudson" target="_blank" href="https://hudson.dev.java.net/" id="otfp">Hudson</a> provide build environments and code coverage (by way of plugins for Hudson) for Grails projects. Hudson requires a little work for setting up Grails builds but there are some nice blog posts on such efforts. Again the IntelliJ produced Teamcity shines in its Grails support right out of the box. <br id="xp624"><br id="xp625"><b id="vxop0">5) Books</b><br id="xp626"><i id="u7_n0"><a title="Groovy in Action" target="_blank" href="http://www.manning.com/koenig/" id="qavy">Groovy in Action</a></i> is a wonderful resource to have as is <i id="u7_n1"><a title="The Definitive Guide to Grails" target="_blank" href="http://www.apress.com/book/view/1590597583" id="iesa">The Definitive Guide to Grails</a></i>. There are several new Groovy books coming out and more likely to follow as the Groovy/Grails community continues to grow. <br id="xp627"><br id="xp628">Would love to hear other ideas/comments.<br id="xp6210">Doug<br id="xp6211"><br id="co2w0">filshttp://www.blogger.com/profile/04578829160811273052noreply@blogger.com5tag:blogger.com,1999:blog-7651912408580529938.post-70876428646769838972008-05-20T12:35:00.001-05:002008-05-20T12:38:07.616-05:00Fkey template for IntelliJI've come to really appreciate IntelliJ IDEA. It's a wonderful IDE and so glad I discovered it as a by product of working with Grails and Groovy. As I use it more and more I wanted to start to exploit more of its features and also to begin to get the key patterns to memory so I can work a bit faster. Failing to find an fkey overlay for IntelliJ I made one. I include a link to the SVG so anyone can modify/improve it as they wish. If you do improve it, please email me updates so I can include them here. <br id="azzi0"><br id="azzi1"><a title="SVG Fkey template for IntelliJ IDEA" target="_blank" href="http://s3.amazonaws.com/fils/fkeyLetter.svg" id="eto:">SVG Fkey template for IntelliJ IDEA</a> (I would highly recommend the <a title="InkScape" target="_blank" href="http://www.inkscape.org/" id="n:il">InkScape</a> SVG editor, if you want to edit this document)<br id="csum"><br id="t2c0">I am also linking to the <a title="document page with the offical IntelliJ PDF key maps" target="_blank" href="http://www.jetbrains.com/idea/documentation/documentation.html" id="i12j">document page with the official IntelliJ PDF key maps</a> documents which list keyboard shortcuts. This is a nice document I think should be more prominently noted at their web site. You can add in your own keyboard <a title="short cuts and a blog posting by Ted Young shows an example" target="_blank" href="http://tedyoung.blogsome.com/2008/02/21/intellij-idea-tip-quick-lists/" id="mx:7">short cuts and there is a blog posting by Ted Young that shows an example</a> of how to do that.<br id="vdda0"><br id="vdda1">I recently noticed the <span id="mwwi0"><b>Shortcut Key List plugin</b></span> ( <a title="http://plugins.intellij.net/plugin/?id=2391" href="http://plugins.intellij.net/plugin/?id=2391" id="ldmx">http://plugins.intellij.net/plugin/?id=2391</a> ) which displays a simple box with short cuts in it. <br id="y5lp"><br id="vdda2">Also there is <span id="mwwi1"><b>Key Promoter</b></span> (<a title="http://plugins.intellij.net/plugin/?id=1003" href="http://plugins.intellij.net/plugin/?id=1003" id="w40c">http://plugins.intellij.net/plugin/?id=1003</a> ) which can look for repetitive actions and ask if you want to assign a key sequence. I have started to fill in opening in the template above with some of the common things I do. <br id="vdda3"><br id="q8xt">I'd also like to mention a new keyboard that I am totally taken with (as is my 3 month old son). It is based on the old IBM Model M keyboards. You can learn more about these keyboards at <a title="http://www.clickykeyboards.com/" target="_blank" href="http://www.clickykeyboards.com/" id="mmsz">http://www.clickykeyboards.com/</a> which re-sells old Model M keyboards and also the Wikipedia entry (Ref: <a title="http://en.wikipedia.org/wiki/Model_M_Keyboard" target="_blank" href="http://en.wikipedia.org/wiki/Model_M_Keyboard" id="o6lv">http://en.wikipedia.org/wiki/Model_M_Keyboard</a> ). These are wonderful, if not a little noisy, keyboards that many of you may recall from your first experiences with computers. <br id="c.ee"><br id="k4vc">A company called <a title="Unicomp" target="_blank" href="http://pckeyboards.stores.yahoo.net/index.html" id="o_hn">Unicomp</a> bought the rights to the buckling spring technology and makes <span id="pa1x0"><i>brand new USB based versions of the Model M style keyboard</i></span> called the <a title="Customizer" target="_blank" href="http://pckeyboards.stores.yahoo.net/customizer.html" id="tbdq">Customizer</a> which is what I picked up and is pictured below. If you don't know what buckling spring keyboards are give them a look. In a time when keyboards are getting far too complex for their own good, I'll take a keyboard that just does one thing and does it very well.<br id="a2rd0"><br id="a2rd1">take care<br id="a2rd2">Doug<br id="k5yh"> <div id="vgwj" style="padding: 1em 0pt; text-align: center;"><img id="id-2" style="width: 800px; height: 365px;" src="http://docs.google.com/File?id=dgtb8v9j_2926vmgw8dn_b"></div>filshttp://www.blogger.com/profile/04578829160811273052noreply@blogger.com0tag:blogger.com,1999:blog-7651912408580529938.post-54996262804772291702008-04-28T12:16:00.001-05:002008-04-28T12:17:27.704-05:00Using IUI in Grails So the first package I used in reviewing phone based UI's from Grails was <a title="CIUI" target="_blank" href="http://clientside.cnet.com/cnet-js-standards/ciui-cnet-iphone-ui/" id="zc3o">CIUI</a>. The other popular one that I found was <a title="IUI" target="_blank" href="http://code.google.com/p/iui/" id="kvtk">IUI</a>. This one seems a bit more advanced or at least feature rich than CIUI. I should point out it will be interesting to see how move advanced <a title="iPhone" href="http://ajaxian.com/archives/iphone-webkit-goodness-3d-css-transforms-and-ontouch-events" id="f:4e">iPhone</a> and HTML 5.0 features are going to play out in all this. That aside, implementing IUI was only marginally harder than CIUI. Again we download and place the CSS and JS files into the css and js directories located in [project]/web-app. In the case of IUI I did have to edit the CSS file to have it correctly locate the images folder (where I placed all the images that come with IUI). You could make a separate directory for these images as a sub-directory of images and alter the CSS file as you see fit in that case. Note, this package didn't render well for me outside of the webkit engine. I did a post on <a title="Webkit javascript performance on Ubuntu" target="_blank" href="http://douglasfils.blogspot.com/2008/04/this-weekend-i-had-small-pet-project-i.html" id="uqo-">Webkit javascript performance on Ubuntu</a> that also links to resources for compiling this package on Linux (Mac and Windows builds are available at the <a title="webkit site" target="_blank" href="http://webkit.org" id="l10o">webkit site</a> )<br id="f3yk"><br id="eg4r">For the case of IUI I used the samples that came with the package as a guide to for all this. My GSP files looks like:<br id="sppf"><br id="u:.4"><pre id="wknv"><a id="ha1m" name="l1"><span id="m-2k" class="s0"><%@ </span><span id="ynp9" class="s2">page </span><span id="sx_q" class="s3">contentType</span><span id="jb0-" class="s1">="</span><span id="s37f" class="s4">text/html;charset=UTF-8</span><span id="m.zw" class="s1">" </span><span id="zprx" class="s0">%></span><span id="lp8j" class="s5"> <br id="obb6"></span></a><a id="qhal" name="l2"><span id="aacg" class="s6"><</span><span id="bu_u" class="s7">html</span><span id="isu6" class="s6">></span><span id="qdbq" class="s5"> <br id="sy30"></span></a><a id="zg40" name="l3"><span id="b99x" class="s6"><</span><span id="gc4z" class="s7">meta </span><span id="rakt" class="s8">name=</span><span id="eiuy" class="s9">"layout" </span><span id="u8he" class="s8">content=</span><span id="hmzd" class="s9">"iuiLayout"</span><span id="wke7" class="s6">/></span><span id="c0p:" class="s5"> <br id="brcq"></span></a><a id="hrpz" name="l4"> <br id="b836"></a><a id="quw9" name="l5"><span id="qw5i" class="s6"><</span><span id="qdj0" class="s7">head</span><span id="adux" class="s6">><</span><span id="n-z6" class="s7">title</span><span id="horf" class="s6">></span><span id="t.3q" class="s5">IUI test</span><span id="w6f8" class="s6"></</span><span id="y1e-" class="s7">title</span><span id="d2g-" class="s6">></span><span id="qbjm" class="s5"> <br id="i4-b"></span></a><a id="p66u" name="l6"> <span id="xs6c" class="s6"><</span><span id="q_0r" class="s7">meta </span><span id="gnis" class="s8">name=</span><span id="snql" class="s9">"viewport" </span><span id="y4d1" class="s8">content=</span><span id="floc" class="s9">"width=320; initial-scale=1.0; maximum-scale=1.0; user-scalable=0;"</span><span id="qsr5" class="s6">></span><span id="b-bv" class="s5"> <br id="hq2:"></span></a><a id="rdpm" name="l7"><span id="gv.v" class="s6"></</span><span id="v8ut" class="s7">head</span><span id="lodq" class="s6">></span><span id="ch-h" class="s5"> <br id="o0tr"></span></a><a id="s0qh" name="l8"> <br id="lhtk"></a><a id="o:sj" name="l9"><span id="x_s9" class="s6"><</span><span id="nlrt" class="s7">body </span><span id="m-bl" class="s8">onclick=</span><span id="y4q8" class="s9">"</span><span id="zq_o" class="s5">console.log(</span><span id="al65" class="s10">'Hello'</span><span id="mn01" class="s5">, event.target);</span><span id="kpqc" class="s9">"</span><span id="g:7n" class="s6">></span><span id="sju-" class="s5"> <br id="g_d:"></span></a><a id="z0ec" name="l10"><span id="dabg" class="s6"><</span><span id="gmrm" class="s7">div </span><span id="p.s4" class="s8">class=</span><span id="h4m:" class="s9">"toolbar"</span><span id="dr4d" class="s6">></span><span id="jli1" class="s5"> <br id="nz67"></span></a><a id="l:om" name="l11"> <span id="g7lh" class="s6"><</span><span id="j20q" class="s7">h1 </span><span id="ww33" class="s8">id=</span><span id="k:zd" class="s9">"pageTitle"</span><span id="gxhy" class="s6">></</span><span id="levk" class="s7">h1</span><span id="ohfm" class="s6">></span><span id="din:" class="s5"> <br id="xqwo"></span></a><a id="pj8m" name="l12"> <span id="wsp." class="s6"><</span><span id="pi9a" class="s7">a </span><span id="mctc" class="s8">id=</span><span id="w4vs" class="s9">"backButton" </span><span id="fuet" class="s8">class=</span><span id="qmkq" class="s9">"button" </span><span id="y92f" class="s8">href=</span><span id="ldf0" class="s9">"#"</span><span id="h2oy" class="s6">></</span><span id="evbf" class="s7">a</span><span id="e-se" class="s6">></span><span id="cc2i" class="s5"> <br id="bd.m"></span></a><a id="os6j" name="l13"> <span id="qh2m" class="s6"><</span><span id="m_nz" class="s7">a </span><span id="z586" class="s8">class=</span><span id="b41v" class="s9">"button" </span><span id="whf-" class="s8">href=</span><span id="dihv" class="s9">"</span><span id="wkay" class="s11">${</span><span id="azy6" class="s12">createLink(controller: </span><span id="lwyy" class="s13">'iui'</span><span id="g6ah" class="s12">, action: </span><span id="txoo" class="s13">'search'</span><span id="y095" class="s12">)</span><span id="gow8" class="s11">}</span><span id="t-ti" class="s9">"</span><span id="yx9a" class="s6">></span><span id="q1vo" class="s5">Search</span><span id="d29o" class="s6"></</span><span id="jm2v" class="s7">a</span><span id="teiv" class="s6">></span><span id="qt9-" class="s5"> <br id="qdqh"></span></a><a id="dw:3" name="l14"><span id="j.ih" class="s6"></</span><span id="goav" class="s7">div</span><span id="cc2." class="s6">></span><span id="rl3b" class="s5"> <br id="ewlr"></span></a><a id="j51n" name="l15"> <br id="ydd-"></a><a id="xxan" name="l16"><span id="hkvy" class="s6"><</span><span id="w8xt" class="s7">ul </span><span id="lw7s" class="s8">id=</span><span id="op0-" class="s9">"home" </span><span id="nyxi" class="s8">title=</span><span id="mx5h" class="s9">"Home" </span><span id="ykv9" class="s8">selected=</span><span id="nb4f" class="s9">"true"</span><span id="cq3b" class="s6">></span><span id="x6j." class="s5"> <br id="iti3"></span></a><a id="xtrm" name="l17"> <span id="ybbq" class="s6"><</span><span id="yr_q" class="s7">li</span><span id="ydm9" class="s6">><</span><span id="lpf8" class="s7">a </span><span id="fz2c" class="s8">href=</span><span id="k3on" class="s9">"</span><span id="q:sx" class="s11">${</span><span id="fxo9" class="s12">createLink(controller: </span><span id="v0_-" class="s13">'iui'</span><span id="k7ht" class="s12">, action: </span><span id="h2w1" class="s13">'category1'</span><span id="nh2e" class="s12">)</span><span id="e95l" class="s11">}</span><span id="x5r6" class="s9">"</span><span id="nlcv" class="s6">></span><span id="k:0k" class="s5">Category 1</span><span id="d_1i" class="s6"></</span><span id="nuxt" class="s7">a</span><span id="c2b5" class="s6">></</span><span id="a5vq" class="s7">li</span><span id="af5a" class="s6">></span><span id="n_tm" class="s5"> <br id="ns55"></span></a><a id="rgmj" name="l18"> <span id="s.mw" class="s6"><</span><span id="ibvm" class="s7">li</span><span id="u_nl" class="s6">><</span><span id="udse" class="s7">a </span><span id="mmp4" class="s8">href=</span><span id="h7ok" class="s9">"</span><span id="rqm2" class="s11">${</span><span id="w-8:" class="s12">createLink(controller: </span><span id="js3v" class="s13">'iui'</span><span id="nwun" class="s12">, action: </span><span id="qpmq" class="s13">'category2'</span><span id="b841" class="s12">)</span><span id="ycis" class="s11">}</span><span id="zv1:" class="s9">"</span><span id="r33o" class="s6">></span><span id="d_-r" class="s5">Category 2</span><span id="etuc" class="s6"></</span><span id="jaew" class="s7">a</span><span id="rumm" class="s6">></</span><span id="pd.h" class="s7">li</span><span id="x5y:" class="s6">></span><span id="ehqw" class="s5"> <br id="ybv0"></span></a><a id="w4rk" name="l19"> <span id="lknz" class="s6"><</span><span id="ac55" class="s7">li</span><span id="r-52" class="s6">><</span><span id="w9.7" class="s7">a </span><span id="j.7e" class="s8">href=</span><span id="e4rw" class="s9">"</span><span id="ig62" class="s11">${</span><span id="uh-p" class="s12">createLink(controller: </span><span id="kn59" class="s13">'iui'</span><span id="hghk" class="s12">, action: </span><span id="bf5h" class="s13">'stats'</span><span id="qev-" class="s12">)</span><span id="muoh" class="s11">}</span><span id="gma9" class="s9">"</span><span id="n-vz" class="s6">></span><span id="axo_" class="s5">Stats</span><span id="lqjo" class="s6"></</span><span id="qa0j" class="s7">a</span><span id="d0ep" class="s6">></</span><span id="ck12" class="s7">li</span><span id="lpzq" class="s6">></span><span id="bnqk" class="s5"> <br id="re_k"></span></a><a id="wm2o" name="l20"> <span id="fv6-" class="s6"><</span><span id="yv.t" class="s7">li</span><span id="j.-2" class="s6">><</span><span id="gzja" class="s7">a </span><span id="rmtz" class="s8">href=</span><span id="ci:2" class="s9">"</span><span id="e-ja" class="s11">${</span><span id="yjj4" class="s12">createLink(controller: </span><span id="o6lh" class="s13">'iui'</span><span id="o7-w" class="s12">, action: </span><span id="v:_0" class="s13">'settings'</span><span id="r2.j" class="s12">)</span><span id="lfn_" class="s11">}</span><span id="o7s4" class="s9">"</span><span id="m:3c" class="s6">></span><span id="m6dz" class="s5">Settings</span><span id="t243" class="s6"></</span><span id="jnz0" class="s7">a</span><span id="lzqt" class="s6">></</span><span id="wdlj" class="s7">li</span><span id="cur5" class="s6">></span><span id="ea2g" class="s5"> <br id="my2m"></span></a><a id="z_8n" name="l21"> <span id="mkkh" class="s6"><</span><span id="guq4" class="s7">li</span><span id="d2kz" class="s6">><</span><span id="t905" class="s7">a </span><span id="l834" class="s8">href=</span><span id="c7ot" class="s9">"</span><span id="jkpo" class="s11">${</span><span id="hw25" class="s12">createLink(controller: </span><span id="xla2" class="s13">'iui'</span><span id="fcnw" class="s12">, action: </span><span id="xfks" class="s13">'about'</span><span id="e-j6" class="s12">)</span><span id="qq-3" class="s11">}</span><span id="t05b" class="s9">"</span><span id="p9rl" class="s6">></span><span id="i5kv" class="s5">About</span><span id="mjtp" class="s6"></</span><span id="w65:" class="s7">a</span><span id="hi85" class="s6">></</span><span id="xiuh" class="s7">li</span><span id="p4xb" class="s6">></span><span id="kda5" class="s5"> <br id="pb9b"></span></a><a id="h4rw" name="l22"> <br id="a.4n"></a><a id="b:e2" name="l23"><span id="p6vw" class="s6"></</span><span id="advx" class="s7">ul</span><span id="mdrp" class="s6">></span> </a><a id="f_h." name="l24"><br id="k:tw"></a><a id="ygn9" name="l25"><span id="e_zh" class="s6"></</span><span id="fggj" class="s7">body</span><span id="xo68" class="s6">></span><span id="smml" class="s5"> <br id="hx5t"></span></a><a id="xj7." name="l26"><span id="k7.j" class="s6"></</span><span id="cin9" class="s7">html</span><span id="qhn1" class="s6">></span></a></pre><br id="g0mo"><div id="en5b"><table id="v:jp" border="0" cellpadding="3" cellspacing="0" width="100%"><tbody id="a:8u"><tr id="kq_7"><td id="pftz" width="50%"><img id="gb4u" style="width: 357px; height: 572px;" src="http://docs.google.com/File?id=dgtb8v9j_284frs9xrf6_b"></td><td id="g7:j" width="50%"><div id="jvpq" style="padding: 1em 0pt; text-align: left;"><img id="mtrz" style="width: 357px; height: 572px;" src="http://docs.google.com/File?id=dgtb8v9j_285fvxxm8ft_b"></div></td></tr></tbody></table></div><br id="ft48">Here I just linked into my <a title="IuiController.groovy" target="_blank" href="Doc?id=dgtb8v9j_286fpzx3bf3" id="iav0">IuiController.groovy</a> file to various methods to demonstrate some of the different features IUI has. The only interesting points in this are that it does demonstrate how to use the <a title="render method" target="_blank" href="http://grails.org/Controller+Dynamic+Methods" id="w7f0">render method</a> for cases like this where you need to include attributes and such. One interesting note was that the render method refused to provide a node named "form" in the XML return. I assume this is perhaps related to a keyword issue or some such. However, MarkupBuilder has no issues so where this does NOT work:<br id="z469"><br id="qsyl"><pre id="ql:l"><a id="z2:a" name="l81"> <span id="xy1i" class="s4">// shows a form but doesn't work due to keyword issue with "form'?</span><span id="k.j_" class="s1"> <br id="ukln"></span></a><a id="juji" name="l82"> <span id="pds9" class="s0">def </span><span id="jkmn" class="s1">searchDoesNotWork = { <br id="umez"></span></a><a id="wxuu" name="l83"> render(contentType: <span id="dl6a" class="s2">"text/xhtml"</span><span id="odkw" class="s1">) { <br id="c0c9"></span></a><a id="h.s4" name="l84"> <span id="rorj" class="s2">"form"</span><span id="pc28" class="s1">(id: </span><span id="guwm" class="s2">"searchForm"</span><span id="x8.0" class="s1">, </span><span id="w68x" class="s0">class</span><span id="e48c" class="s1">: </span><span id="sh.f" class="s2">"dialog"</span><span id="e-4n" class="s1">, action: </span><span id="x9dc" class="s2">"foo"</span><span id="g1ne" class="s1">) { <br id="yr4o"></span></a><a id="w4wk" name="l85"> fieldset { <br id="kz1l"></a><a id="tm0c" name="l86"> h1(<span id="gfpo" class="s2">"Search the records"</span><span id="pim." class="s1">) <br id="v7dm"></span></a><a id="bcsd" name="l87"> a(<span id="ld4v" class="s0">class</span><span id="t07x" class="s1">: </span><span id="st8j" class="s2">"button leftButton"</span><span id="wtra" class="s1">, type: </span><span id="rywy" class="s2">"cancel"</span><span id="jboo" class="s1">, </span><span id="sn::" class="s2">"Cancel"</span><span id="vven" class="s1">) <br id="q20p"></span></a><a id="qvmz" name="l88"> a(<span id="fire" class="s0">class</span><span id="pll9" class="s1">: </span><span id="zs31" class="s2">"button blueButton"</span><span id="ej-e" class="s1">, type: </span><span id="yadg" class="s2">"submit"</span><span id="n7xa" class="s1">, </span><span id="g4tu" class="s2">"Search"</span><span id="r6zp" class="s1">) <br id="okiu"></span></a><a id="amhw" name="l89"> label(<span id="e17b" class="s2">"First Name"</span><span id="qcyl" class="s1">) <br id="rkp5"></span></a><a id="kp:2" name="l90"> input(id: <span id="yvjr" class="s2">"fname"</span><span id="udgn" class="s1">, type: </span><span id="x9gz" class="s2">"text"</span><span id="acg5" class="s1">, name: </span><span id="ax6v" class="s2">"fname"</span><span id="umk1" class="s1">) <br id="glqp"></span></a><a id="e:3z" name="l91"> label(<span id="t7p4" class="s2">"Last Name"</span><span id="dleg" class="s1">) <br id="upgf"></span></a><a id="c-um" name="l92"> input(id: <span id="rz:e" class="s2">"lname"</span><span id="imml" class="s1">, type: </span><span id="vg.d" class="s2">"text"</span><span id="fzlc" class="s1">, name: </span><span id="s_ur" class="s2">"lname"</span><span id="iru_" class="s1">) <br id="erw."></span></a><a id="y240" name="l93"> } <br id="y3xm"></a><a id="id04" name="l94"> } <br id="plih"></a><a id="m-z6" name="l95"> } <br id="jftv"></a><a id="wabq" name="l96"> } <br id="abvr"></a></pre>this method will:<br id="uwq3"><pre id="cw3h"><a id="d98:" name="l100"> <span id="xyyq" class="s0">def </span><span id="edxx" class="s1">search = { <br id="xwv."></span></a><a id="j_oy" name="l101"> <span id="pylf" class="s0">def </span><span id="jrn6" class="s1">mywriter = </span><span id="mzvo" class="s0">new </span><span id="ojhu" class="s1">StringWriter() <br id="rwq4"></span></a><a id="w-t_" name="l102"> <span id="g4bf" class="s0">def </span><span id="mwzi" class="s1">xml = </span><span id="hd9d" class="s0">new </span><span id="esm7" class="s1">MarkupBuilder(mywriter) <br id="d8la"></span></a><a id="tmmh" name="l103"> <br id="pdsf"></a><a id="ccnu" name="l104"> <span id="cqyo" class="s4">// had to do this since the render method seemed to not want to return form (key word?)</span><span id="r75t" class="s1"> <br id="o8yh"></span></a><a id="mf2y" name="l105"> <span id="ycjs" class="s4">// this is really how they should dbe done anyway... but I am too lazy to change all the</span><span id="yoi:" class="s1"> <br id="ibse"></span></a><a id="path" name="l106"> <span id="m__3" class="s4">// examples</span><span id="k158" class="s1"> <br id="o6lht"></span></a><a id="bj:y" name="l107"> <br id="i:u_"></a><a id="g9hg" name="l108"> xml.<span id="ynud" class="s2">"form"</span><span id="vxom" class="s1">(id: </span><span id="l5mp" class="s2">"searchForm"</span><span id="ql65" class="s1">, </span><span id="tmhk" class="s0">class</span><span id="clz3" class="s1">: </span><span id="ublx" class="s2">"dialog"</span><span id="o311" class="s1">, action: </span><span id="xggu" class="s2">"$</span><span id="hyoi" class="s1">{createLink(controller: </span><span id="tewj" class="s2">'iui'</span><span id="wrt1" class="s1">, action: </span><span id="wc7m" class="s2">'doSearch'</span><span id="lplc" class="s1">)}</span><span id="ogb3" class="s2">"</span><span id="vo6b" class="s1">) { <br id="kev7"></span></a><a id="ljgn" name="l109"> fieldset { <br id="vh8d"></a><a id="umfq" name="l110"> h1(<span id="fmad" class="s2">"Search the records"</span><span id="gjnq" class="s1">) <br id="jihy"></span></a><a id="ivmx" name="l111"> a(<span id="ft60" class="s0">class</span><span id="riw2" class="s1">: </span><span id="ve-e" class="s2">"button leftButton"</span><span id="d0p:" class="s1">, type: </span><span id="tx53" class="s2">"cancel"</span><span id="w4mz" class="s1">, </span><span id="vwrz" class="s2">"Cancel"</span><span id="beg5" class="s1">) <br id="j5yh"></span></a><a id="vcpa" name="l112"> a(<span id="khxj" class="s0">class</span><span id="vk26" class="s1">: </span><span id="r7i:" class="s2">"button blueButton"</span><span id="n166" class="s1">, type: </span><span id="rwcq" class="s2">"submit"</span><span id="r.bh" class="s1">, </span><span id="pkl1" class="s2">"Search"</span><span id="iu41" class="s1">) <br id="zzh5"></span></a><a id="h_el" name="l113"> label(<span id="w1hr" class="s2">"First Name"</span><span id="ndgv" class="s1">) <br id="wbzy"></span></a><a id="mud5" name="l114"> input(id: <span id="vesa" class="s2">"fname"</span><span id="ugd0" class="s1">, type: </span><span id="z4cb" class="s2">"text"</span><span id="qb9t" class="s1">, name: </span><span id="h6iw" class="s2">"fname"</span><span id="x7u_" class="s1">) <br id="pkuo"></span></a><a id="zdha" name="l115"> label(<span id="qsty" class="s2">"Last Name"</span><span id="asa9" class="s1">) <br id="gilo"></span></a><a id="a4iq" name="l116"> input(id: <span id="g_:r" class="s2">"lname"</span><span id="tk_a" class="s1">, type: </span><span id="ltbf" class="s2">"text"</span><span id="nuyc" class="s1">, name: </span><span id="z51a" class="s2">"lname"</span><span id="byd5" class="s1">) <br id="e3ns"></span></a><a id="nvpm" name="l117"> } <br id="yyv."></a><a id="s:7x" name="l118"> } <br id="j9.."></a><a id="us5n" name="l119"> render mywriter.toString() <br id="j7g5"></a><a id="wy5q" name="l120"> } <br id="n.20"></a></pre><br id="r4rw">In point of fact <span id="z7x6"><b id="ln9d">it's likely better form in most cases to use the MarkupBuilder</b></span> as you can incorporate it into code logic easier. Though for simple cases there seems to be nothing wrong with render. As with the CIUI example I'll just post up the whole controller file for people to look at (<a title="IUI Controller" target="_blank" href="http://s3.amazonaws.com/fils/IuiController.groovy.html" id="s.ku">IUI Controller</a> ). <br id="e_pp"><br id="lbwp0">Note, I did modify the iui.js file line:<br id="lbwp1"><br id="lbwp2"><span id="c2nz0"><i> req.open(method || "GET", href, true);</i></span><br id="lbwp3"><br id="lbwp4">to <br id="lbwp5"><br id="lbwp6"><span id="c2nz1"><i> req.open(method || "POST", href, true);</i></span><br id="lbwp7"><br id="lbwp8"><span id="xpsh0"><i>in order to get the search call to work to my controller</i></span>. For some reason the URL encoding version would not send back the variables. I may contact the IUI people about this and see if I can resolve why this occured. <br id="lbwp9"><br id="ga-g0">I was not able to wire up the toggle button to a controller method.. I got the error:<br id="ga-g1"><br id="ga-g2">console message: http://localhost:8080/phoneUIGarden/iui#_Settings @35: Can't find variable: settingCallBack<br id="q4wp0"><br id="vjpr0">Likely this is something simple (a guess) but I didn't bother to track it down since this was just a "for fun exploration" project. <br id="ga-g3"><br id="qprm">It's easy to see how this could all be extended into a secured area of a site via <a title="Acegi" target="_blank" href="http://grails.org/AcegiSecurity+Plugin" id="d5jv">Acegi</a> or <a title="jsecurity" target="_blank" href="http://grails.codehaus.org/JSecurity+Plugin" id="exsq">jsecurity</a> and perhaps even attached to <a title="webflow" target="_blank" href="http://grails.org/WebFlow" id="sxcr">webflow</a> . All fun stuff if I was paid to do that. This is by no means and in depth review, it's just my 4am feeding hacking to see what is involved in using IUI and CIUI. I thought I would share since I haven't seen much on using these in the Grails framework. <br id="vg8p"><br id="hfh3">It's obvious that <br id="tpcx">A) it's simple <br id="wlru">B) Groovy and Grails are a wonderful language/framework environment to do this in. <br id="au_v">C) Someone with more time for this could flesh it all out rather quickly<br id="yb-.0"><br id="ok2:">enjoy...<br id="ncdn">Doug<br id="tmuj"><br id="q_d:"><br id="snwf0">filshttp://www.blogger.com/profile/04578829160811273052noreply@blogger.com1tag:blogger.com,1999:blog-7651912408580529938.post-15596626399803540682008-04-28T12:14:00.001-05:002008-04-28T12:24:21.777-05:00Using CIUI in Grails So this past weekend I needed a fun yet mindless task one morning during at 4 am. I have been looking at both <a title="CIUI" target="_blank" href="http://clientside.cnet.com/cnet-js-standards/ciui-cnet-iphone-ui/" id="u35g">CIUI</a> and <a title="IUI" target="_blank" href="http://code.google.com/p/iui/" id="moxk">IUI</a> and knew they would lend themselves well to being implemented in Grails what with it's oh so easy REST/AJAX style call to the controller layer. In fact this was ever so easy and so I thought I would mention it here so people would know of these nice packages and add these as tools to their Grails toolbox. If you are doing this from Linux (Ubuntu in my case) like I am, you may want to use the webkit engine (Ref: <a title="Webkit on Ubuntu performance" target="_blank" href="http://douglasfils.blogspot.com/2008/04/this-weekend-i-had-small-pet-project-i.html" id="fs:x">Webkit on Ubuntu performance</a> ). Especially for the IUI package since it seems to render best in Webkit even over the latest FF3 engine. <br id="csk8"><br id="j-32">Starting with <a title="CIUI" target="_blank" href="http://clientside.cnet.com/cnet-js-standards/ciui-cnet-iphone-ui/" id="gfdz">CIUI</a> the approach is easy. Place the CiUI.css in your [project]/web-app/css and the CiUI.js into the [project]/web-app/js directories. You may need to scan the CSS file and change image paths and such if you are using images from an other than default location. <br id="lzb2"><br id="ldva">I created a simple controller CiuiController.groovy and an associated GSP page index.gsp. Into the GSP page be sure to place:<br id="xvq9"><br id="s48x"><span id="v0_z"><b id="otmb"><link rel="stylesheet" href="${createLinkTo(dir:'css',file:'CiUI.css')}" /></b></span><br style="font-weight: bold;" id="bbsl"><span id="km22"><b id="gonx"><g:javascript library="CiUI"/></b></span><br id="bt6l"><br id="ec9q">to load the CiUI css and js files. Obviously you can alter the directory paths and such to keep your application file structure in whatever order you wish.<br id="k-y9"><br id="ekmp">My whole GSP pages looks like:<br id="d:ge"><pre id="ymdn"><a id="ngpd" name="l1"><span id="tqeh" class="s0"><</span><span id="c13o" class="s1">html</span><span id="u6_n" class="s0">><</span><span id="ki9y" class="s1">head</span><span id="th72" class="s0">></span><span id="u_te" class="s2"> <br id="rc6s"></span></a><a id="fl45" name="l2"> <span id="h5y8" class="s0"><</span><span id="efjc" class="s1">title</span><span id="lqws" class="s0">></span><span id="x9vc" class="s2">Grails and CiUI Demo</span><span id="yrcg" class="s0"></</span><span id="lbyj" class="s1">title</span><span id="t.b8" class="s0">></span><span id="sx_k" class="s2"> <br id="a-.l"></span></a><a id="dwow" name="l3"> <span id="yy5h" class="s0"><</span><span id="efzt" class="s1">meta </span><span id="k0if" class="s3">name=</span><span id="ixkw" class="s4">"viewport" </span><span id="j-d4" class="s3">content=</span><span id="n2n7" class="s4">"width=320"</span><span id="r-0v" class="s0">/></span><span id="q2y9" class="s2"> <br id="y3pm"></span></a><a id="ywfq" name="l4"> <span id="nzn2" class="s0"><</span><span id="f853" class="s1">meta </span><span id="ca5e" class="s3">name=</span><span id="qiwa" class="s4">"viewport" </span><span id="p.rn" class="s3">content=</span><span id="cqzj" class="s4">"initial-scale=1.0, user-scalable=no"</span><span id="b6:0" class="s0">/></span><span id="fsw-" class="s2"> <br id="pvmq"></span></a><a id="c2lz" name="l5"> <span id="myes" class="s0"><</span><span id="i9la" class="s1">link </span><span id="ljb." class="s3">rel=</span><span id="yri6" class="s4">"stylesheet" </span><span id="adp4" class="s3">href=</span><span id="xjj-" class="s4">"</span><span id="h.6i" class="s5">${</span><span id="xzhn" class="s6">createLinkTo(dir: </span><span id="s5cb" class="s7">'css'</span><span id="f89o" class="s6">, file: </span><span id="eso4" class="s7">'CiUI.css'</span><span id="g0.p" class="s6">)</span><span id="g.by" class="s5">}</span><span id="n:80" class="s4">"</span><span id="kvwe" class="s0">/></span><span id="k3ai" class="s2"> <br id="ac0-"></span></a><a id="bz0c" name="l6"> <span id="c-56" class="s8"><</span><span id="c6qz" class="s9">g:javascript </span><span id="t4mg" class="s11">library</span><span id="bo4h" class="s10">="</span><span id="u:8c" class="s12">CiUI</span><span id="meqs" class="s10">"</span><span id="rx:x" class="s8">/></span><span id="lq7n" class="s2"> <br id="s5d6"></span></a><a id="xk42" name="l7"><span id="uxkl" class="s0"></</span><span id="m99y" class="s1">head</span><span id="gsu4" class="s0">></span><span id="beo1" class="s2"> <br id="r8gq"></span></a><a id="f:w4" name="l8"> <br id="opm9"></a><a id="p3-8" name="l9"><span id="qj-a" class="s0"><</span><span id="ru.q" class="s1">body</span><span id="pdzg" class="s0">></span><span id="nsv3" class="s2"> <br id="m:ta"></span></a><a id="pi-2" name="l10"><span id="n_g0" class="s0"><</span><span id="g.tj" class="s1">div </span><span id="khhm" class="s3">id=</span><span id="f08l" class="s4">"iphone_header"</span><span id="xtye" class="s0">></span><span id="fucg" class="s2"> <br id="mpc5"></span></a><a id="cx78" name="l11"> <span id="kz9y" class="s0"><</span><span id="qxhb" class="s1">div </span><span id="vc2d" class="s3">id=</span><span id="dqxt" class="s4">"iphone_backbutton"</span><span id="wzd-" class="s0">></span><span id="b5lu" class="s2"> <br id="ov9d"></span></a><a id="v6xy" name="l12"> <span id="hj30" class="s0"><</span><span id="qx.l" class="s1">img </span><span id="ut1z" class="s3">src=</span><span id="ly3n" class="s4">"images/back-button-tip.png" </span><span id="mgvr" class="s3">border=</span><span id="d9so" class="s4">"0" </span><span id="ojq." class="s3">align=</span><span id="t0wo" class="s4">"left"</span><span id="vwjw" class="s0">/></span><span id="kyy7" class="s2"> <br id="n85."></span></a><a id="ozjn" name="l13"> <span id="xn4p" class="s0"><</span><span id="ne-x" class="s1">a </span><span id="ouna" class="s3">id=</span><span id="v9:d" class="s4">"iphone_backbutton_text" </span><span id="at69" class="s3">href=</span><span id="uyyl" class="s4">"#" </span><span id="f.4l" class="s3">class=</span><span id="slrc" class="s4">"go_back"</span><span id="v.cm" class="s0">></span><span id="xtsm" class="s2">Back</span><span id="a.4h" class="s0"></</span><span id="uhxt" class="s1">a</span><span id="jfxc" class="s0">></span><span id="ilno" class="s2"> <br id="kbfj"></span></a><a id="v0x7" name="l14"> <span id="c6wx" class="s0"></</span><span id="yish" class="s1">div</span><span id="eq_e" class="s0">></span><span id="yg70" class="s2"> <br id="f:zl"></span></a><a id="gf.y" name="l15"> <span id="mog2" class="s0"><</span><span id="k95a" class="s1">div </span><span id="jy40" class="s3">id=</span><span id="wh7s" class="s4">"iphone_title"</span><span id="uvag" class="s0">></</span><span id="em0t" class="s1">div</span><span id="kvbe" class="s0">></span><span id="fu-7" class="s2"> <br id="ozja"></span></a><a id="ape5" name="l16"><span id="e7wj" class="s0"></</span><span id="g2qw" class="s1">div</span><span id="a8:3" class="s0">></span><span id="m56p" class="s2"> <br id="zau0"></span></a><a id="sm8v" name="l17"> <br id="yh_7"></a><a id="drpy" name="l18"><span id="zn6y" class="s0"><</span><span id="iurr" class="s1">div </span><span id="z87h" class="s3">id=</span><span id="b8_u" class="s4">"iphone_body" </span><span id="fetc" class="s3">style=</span><span id="tqk9" class="s4">"</span><span id="g3h-" class="s13">clear</span><span id="oq8t" class="s2">:</span><span id="r3ja" class="s14">both</span><span id="p0.c" class="s2">;</span><span id="w.ip" class="s4">"</span><span id="dc-d" class="s0">></span><span id="qfg:" class="s2"> <br id="tl2a"></span></a><a id="qt1c" name="l19"> <span id="wrnu" class="s0"><</span><span id="pjd0" class="s1">ul </span><span id="dv1z" class="s3">class=</span><span id="yxj4" class="s4">"menu"</span><span id="cegy" class="s0">></span><span id="o:j." class="s2"> <br id="trzh"></span></a><a id="g_r5" name="l20"> <span id="d-xt" class="s0"><</span><span id="ckqm" class="s1">li</span><span id="v_:7" class="s0">><</span><span id="rfvv" class="s1">a </span><span id="zq0j" class="s3">href=</span><span id="yk2e" class="s4">"</span><span id="v7ls" class="s5">${</span><span id="hmsz" class="s6">createLink(controller: </span><span id="p2y5" class="s7">'ciui'</span><span id="dze-" class="s6">, action: </span><span id="rtpr" class="s7">'page'</span><span id="fre-" class="s6">, id: </span><span id="bm8q" class="s7">'1'</span><span id="erp:" class="s6">)</span><span id="tv3x" class="s5">}</span><span id="vtuf" class="s4">" </span><span id="ccbv" class="s3">class=</span><span id="evld" class="s4">"go_forward" </span><span id="sznl" class="s3">title=</span><span id="ihih" class="s4">"Feeds"</span><span id="ypvs" class="s0">></span><span id="n164" class="s2"> <br id="orwk"></span></a><a id="o.e5" name="l21"> Groovy and Grails Feeds <br id="gq:l"></a><a id="tf-b" name="l22"> <span id="jnvk" class="s0"></</span><span id="l:tp" class="s1">a</span><span id="ehve" class="s0">></span><span id="u6.o" class="s2"> <br id="ba:o"></span></a><a id="m9y." name="l23"> <span id="p2mc" class="s0"></</span><span id="uosl" class="s1">li</span><span id="b-:o" class="s0">></span><span id="vq.3" class="s2"> <br id="pgs."></span></a><a id="dv39" name="l24"> <span id="jjfi" class="s0"></</span><span id="e8ur" class="s1">ul</span><span id="b-y1" class="s0">></span><span id="z2_b" class="s2"> <br id="gk2m"></span></a><a id="bws9" name="l25"><span id="qc0m" class="s0"></</span><span id="fqcq" class="s1">div</span><span id="pajw" class="s0">></span><span id="zuy1" class="s2"> <br id="e0fk"></span></a><a id="l1wv" name="l26"> <br id="ncgw"></a><a id="ky4j" name="l27"><span id="fe4k" class="s0"><</span><span id="uh_q" class="s1">div </span><span id="uhl9" class="s3">id=</span><span id="yz0e" class="s4">"iphone_footer"</span><span id="mxax" class="s0">></span><span id="gb:t" class="s2">My footer for CiUI demo</span><span id="vtg:" class="s0"></</span><span id="u0ct" class="s1">div</span><span id="y9va" class="s0">></span><span id="jg0d" class="s2"> <br id="psk1"></span></a><a id="fxro" name="l28"> <br id="bll7"></a><a id="qzb." name="l29"><span id="eieo" class="s0"><</span><span id="q11i" class="s1">div </span><span id="l6bq" class="s3">id=</span><span id="a6eu" class="s4">"iphone_loading_page"</span><span id="dg:l" class="s0">></span><span id="pg-c" class="s2"> <br id="nnhc"></span></a><a id="ke26" name="l30"> <span id="sam0" class="s0"><</span><span id="d49f" class="s1">div </span><span id="mhv4" class="s3">id=</span><span id="b1o1" class="s4">'loading' </span><span id="wr9r" class="s3">class=</span><span id="se0-" class="s4">"info_msg"</span><span id="d49-" class="s0">></span><span id="oq6s" class="s2"> <br id="zva-"></span></a><a id="gcy0" name="l31"> <span id="b1ps" class="s0"><</span><span id="gosu" class="s1">img </span><span id="hr1i" class="s3">src=</span><span id="sn1f" class="s4">"images/loading.gif"</span><span id="m2bm" class="s0">/><</span><span id="ltri" class="s1">br</span><span id="chd7" class="s0">/></span><span id="rtcx" class="s2"> <br id="d8:5"></span></a><a id="b:-o" name="l32"> loading... <br id="w670"></a><a id="x9v:" name="l33"><span id="rtfo" class="s0"></</span><span id="ztt_" class="s1">div</span><span id="d:uy" class="s0">></span><span id="axhj" class="s2"> <br id="fiu6"></span></a><a id="x4yj" name="l34"><span id="x.w1" class="s0"></</span><span id="q1tz" class="s1">div</span><span id="m7m1" class="s0">></span><span id="f6r3" class="s2"> <br id="u:n7"></span></a><a id="drw3" name="l35"><span id="o26u" class="s0"></</span><span id="yq-h" class="s1">body</span><span id="ytj8" class="s0">></span><span id="yviv" class="s2"> <br id="mrz6"></span></a><a id="r2va" name="l36"><span id="wr4v" class="s0"></</span><span id="as7c" class="s1">html</span><span id="k98d" class="s0">></span><span id="zjxw" class="s2"> <br id="zrxe"><br id="mlwc"><br id="c_92"><div id="sssb" style="padding: 1em 0pt; text-align: left;"><img id="zrmi" style="width: 357px; height: 572px;" src="http://docs.google.com/File?id=dgtb8v9j_283dkzz7zcx_b"></div><br id="noe0"></span></a></pre><br id="o4a.">Next it's just a few simple method calls in the controller. I decided to have a little fun and used the Groovy XML features to pull down RSS feeds from <a title="Groovy Blogs" target="_blank" href="http://www.groovyblogs.org" id="dalh">Groovy Blogs</a>, <a title="Groovy on Grails" target="_blank" href="http://feeds.feedburner.com/groovyongrails" id="fkpt"></a><a title="Groovy on Grails" target="_blank" href="http://www.groovyongrails.com/" id="ymcj">Groovy on Grails</a> and <a title="Groovy Zone" target="_blank" href="http://feeds.dzone.com/zones/groovy" id="jjtv"></a><a title="Groovy Zone" target="_blank" href="http://groovy.dzone.com/" id="yyc0">Groovy Zone</a>. There are really only a couple of comments:<br id="us_-"><br id="g-3t">1) This whole effort is made very easy by use of the Grails "render" call. It means that we can do things like:<br id="jsan"><br id="rdhl"><pre id="oo9x"><a id="qrv6" name="l29"> <span id="pkz5" class="s0">def </span><span id="wjrz" class="s1">page = { <br id="w-pj"></span></a><a id="fpjs" name="l30"> <br id="grd1"></a><a id="txms" name="l31"> <span id="nmc2" class="s3">// you would make this more dynamic in a real app</span><span id="wj.u" class="s1"> <br id="osp1"></span></a><a id="jejf" name="l32"> <span id="o:en" class="s0">def </span><span id="cowg" class="s1">feedMap = [</span><span id="ghq1" class="s2">'Groovy Blogs'</span><span id="luwk" class="s1">: </span><span id="hhwz" class="s2">'http://feeds.feedburner.com/groovyblogs'</span><span id="w_3q" class="s1">, </span><span id="fadi" class="s2">'Grroovy on Grails'</span><span id="unnt" class="s1">: </span><span id="refi" class="s2">'http://feeds.feedburner.com/groovyongrails'</span><span id="e1j2" class="s1">, </span><span id="y_ty" class="s2">'Groovy Zone'</span><span id="mism" class="s1">: </span><span id="fp4l" class="s2">'http://feeds.dzone.com/zones/groovy'</span><span id="zufi" class="s1">] <br id="h7pk"></span></a><a id="gt8h" name="l33"> <br id="nc4l"></a><a id="hkkr" name="l34"> render(contentType: <span id="xmnm" class="s2">"text/xhtml"</span><span id="k9l4" class="s1">) { <br id="tohb"></span></a><a id="xzo2" name="l35"> <br id="mjcf"></a><a id="mbv9" name="l36"> <span id="afkj" class="s3">// If there are some menu items to show.. show them</span><span id="ym70" class="s1"> <br id="gaqf"></span></a><a id="bv5t" name="l37"> ul(<span id="f-wn" class="s0">class</span><span id="h1ba" class="s1">: </span><span id="wujc" class="s2">"menu"</span><span id="q5vc" class="s1">) { <br id="xvwk"></span></a><a id="mwc5" name="l38"> <span id="qpxg" class="s0">for </span><span id="mfaj" class="s1">(i </span><span id="nmp1" class="s0">in </span><span id="r9hp" class="s1">feedMap.keySet()) { <br id="x:nr"></span></a><a id="q0aa" name="l39"> li { <br id="tn25"></a><a id="rthj" name="l40"> a(<span id="z8r7" class="s0">class</span><span id="fjcs" class="s1">: </span><span id="hsfs" class="s2">"go_forward"</span><span id="qin5" class="s1">, title: </span><span id="yk1f" class="s2">"$</span><span id="ruix" class="s1">{i}</span><span id="opl9" class="s2">"</span><span id="w9o6" class="s1">, href: </span><span id="ybpr" class="s2">"$</span><span id="ir:x" class="s1">{createLink(controller: </span><span id="pyr0" class="s2">'ciui'</span><span id="kqjr" class="s1">, action: </span><span id="zk7o" class="s2">'getFeed'</span><span id="dt7-" class="s1">)}</span><span id="wk63" class="s2">/?url=" </span><span id="h5we" class="s1">+ feedMap[i]) { <br id="wfs8"></span></a><a id="r14p" name="l41"> div(<span id="ffxz" class="s2">"$</span><span id="gixp" class="s1">{i}</span><span id="j5j7" class="s2">"</span><span id="b4ua" class="s1">) <br id="noy4"></span></a><a id="idn_" name="l42"> } <br id="arpm"></a><a id="yfia" name="l43"> } <br id="l5y5"></a><a id="s1mi" name="l44"> } <br id="ztc2"></a><a id="j-8c" name="l45"> } <br id="seuc"></a><a id="r:6n" name="l46"> } <br id="fokl"></a><a id="prj:" name="l47"> <br id="t0gn"></a><a id="lf78" name="l48"> } <br id="na1l"></a><a id="xs3y" name="l49"> <br id="hj6b"></a></pre>Check out more on the render method at <a title="http://grails.org/Controller+Dynamic+Methods" target="_blank" href="http://grails.org/Controller+Dynamic+Methods" id="z4uy">http://grails.org/Controller+Dynamic+Methods</a>. For sake of a short blog entry I have just placed the whole controller file on-line (Ref: <a title="CIUI Controller" target="_blank" href="http://s3.amazonaws.com/fils/CiuiController.groovy.html" id="a3o3">CIUI Controller</a> ) It's all just a simple zero'th order pass at using CiUI. There are many extensions that one could do with this (more with templates, webflow, etc.). I didn't implement the search interface or anything fancy here as this is just a review to see what is involved (practically nothing) in using CIUI in Grails. I will likely go back and add something like that in and see if I can call back with parameters to my controller. <br id="nw4-"><br id="x0ci">Note however that when generating the mark up in the controller it's likely a bit more "proper" to do something like the following <a title="MarkupBuilder" target="_blank" href="http://groovy.codehaus.org/Creating+XML+using+Groovy%27s+MarkupBuilder" id="a90o">MarkupBuilder</a> example. This is actually an example from the IUI test I did and not used in this CIUI example, however the principle is the same. <br id="lrax"><br id="tcbk"><pre id="end1"><a id="ebk9" name="l100"> <span id="vdso" class="s0">def </span><span id="b87r" class="s1">search = { <br id="gjz1"></span></a><a id="q3bc" name="l101"> <span id="m6yh" class="s0">def </span><span id="fevz" class="s1">mywriter = </span><span id="ey62" class="s0">new </span><span id="ooru" class="s1">StringWriter() <br id="i16m"></span></a><a id="c.o2" name="l102"> <span id="dzte" class="s0">def </span><span id="pcpj" class="s1">xml = </span><span id="opxy" class="s0">new </span><span id="bgu8" class="s1">MarkupBuilder(mywriter) </span></a><a id="oey8" name="l103"></a><a id="a8o4" name="l104"></a><a id="j.dz" name="l106"><span id="a203" class="s1"><br id="vo8l"></span></a><a id="xexr" name="l107"> <br id="gxrv"></a><a id="scla" name="l108"> xml.<span id="or-c" class="s2">"form"</span><span id="cgl6" class="s1">(id: </span><span id="qdp7" class="s2">"searchForm"</span><span id="fwu_" class="s1">, </span><span id="a:3v" class="s0">class</span><span id="xtxr" class="s1">: </span><span id="ilqk" class="s2">"dialog"</span><span id="ttzi" class="s1">, action: </span><span id="hcts" class="s2">"$</span><span id="h3ez" class="s1">{createLink(controller: </span><span id="qc8q" class="s2">'iui'</span><span id="duax" class="s1">, action: </span><span id="zskf" class="s2">'doSearch'</span><span id="g:7a" class="s1">)}</span><span id="b9sl" class="s2">"</span><span id="vafd" class="s1">) { <br id="dqgl"></span></a><a id="k47s" name="l109"> fieldset { <br id="d6.f"></a><a id="m2-0" name="l110"> h1(<span id="ah-g" class="s2">"Search the records"</span><span id="azjn" class="s1">) <br id="uyb4"></span></a><a id="l-b8" name="l111"> a(<span id="r6lr" class="s0">class</span><span id="c_6n" class="s1">: </span><span id="a3g5" class="s2">"button leftButton"</span><span id="phyg" class="s1">, type: </span><span id="w7m2" class="s2">"cancel"</span><span id="r.ii" class="s1">, </span><span id="bfzj" class="s2">"Cancel"</span><span id="n4m." class="s1">) <br id="gsnw"></span></a><a id="qeb7" name="l112"> a(<span id="jd7j" class="s0">class</span><span id="vrve" class="s1">: </span><span id="n:8c" class="s2">"button blueButton"</span><span id="z1kh" class="s1">, type: </span><span id="aju-" class="s2">"submit"</span><span id="w1.y" class="s1">, </span><span id="iev3" class="s2">"Search"</span><span id="j_yc" class="s1">) <br id="qeuk"></span></a><a id="a-79" name="l113"> label(<span id="fdte" class="s2">"First Name"</span><span id="zt_2" class="s1">) <br id="tj10"></span></a><a id="cm0z" name="l114"> input(id: <span id="umrx" class="s2">"fname"</span><span id="c2wl" class="s1">, type: </span><span id="cogn" class="s2">"text"</span><span id="z8l:" class="s1">, name: </span><span id="d__l" class="s2">"fname"</span><span id="qze:" class="s1">) <br id="fgo_"></span></a><a id="sfbb" name="l115"> label(<span id="lruc" class="s2">"Last Name"</span><span id="h3tx" class="s1">) <br id="vla5"></span></a><a id="p7vy" name="l116"> input(id: <span id="ztjv" class="s2">"lname"</span><span id="oc_8" class="s1">, type: </span><span id="hivp" class="s2">"text"</span><span id="f:t_" class="s1">, name: </span><span id="o1ld" class="s2">"lname"</span><span id="cq5s" class="s1">) <br id="kqw3"></span></a><a id="dqnd" name="l117"> } <br id="a-gq"></a><a id="c_qx" name="l118"> } <br id="t-ad"></a><a id="qdg:" name="l119"> render mywriter.toString() <br id="e-1t"></a><a id="q2l2" name="l120"> } <br id="h5-d"></a></pre><br id="axvf">Here we used the Groovy MarkupBuilder to generate our XML outside the render method and then just pass it along. This means we can push this logic down into a service or at least have a bit better logic to our controller code designs. <br id="wgjr"><br id="niuq"><a title="This youTube video shows what it looks like in action:" target="_blank" href="http://www.youtube.com/watch?v=VN-hxJ7GNA0" id="njz0">This youTube video shows what it looks like in action</a> <br id="dq1l"><br id="frzf">Implementing these JS/CSS packages inside Grails is trivial. As the iPhone and Google Android phones become more and more a mechanism for people to obtain information from the web the ability for developers easy represent data in appropriate formats will be more important. Grails is a wonderfull platform for addressing that. <br id="wf57"><br id="u4j8">enjoy<br id="m6wv">Doug<br id="szmj"><br id="y.ja0">filshttp://www.blogger.com/profile/04578829160811273052noreply@blogger.com0tag:blogger.com,1999:blog-7651912408580529938.post-47146786624531895302008-04-25T10:25:00.005-05:002008-04-28T15:31:43.107-05:00xrandr with Ubuntu 8.04 and external monitors on laptopsSo I installed Ubuntu 8.04 onto my Dell Inspiron 700m. I was impressed with everything it is doing well right out of the box (display, power management, wireless, etc.).<br /><br />However, one item I was really looking forward to was xrandr and the ability to dynamically plug in external monitors or projection units. In trying this I failed to get my external monitor to dynamically configure via xrandr. However, the solution to this was simple.<br /><br />The default xorg.conf was:<br /><br /><span style="font-family:courier new;">Section "Screen" </span><br /><span style="font-family:courier new;"> Identifier "Default Screen"</span><br /><span style="font-family:courier new;"> Monitor "Configured Monitor"</span><br /><span style="font-family:courier new;"> Device "Configured Video Device"</span><br /><span style="font-family:courier new;">EndSection</span><br /><br />I modified this by adding in the subsection display:<br /><br /><span style="font-family:courier new;">Section "Screen" </span><br /><span style="font-family:courier new;"> Identifier "Default Screen"</span><br /><span style="font-family:courier new;"> Monitor "Configured Monitor"</span><br /><span style="font-family:courier new;"> Device "Configured Video Device"</span><br /><span style="font-family:courier new;"> </span><span style="font-weight: bold;font-family:courier new;" > SubSection "Display"</span><br /><span style="font-weight: bold;font-family:courier new;" > Virtual 2560 1024</span><br /><span style="font-weight: bold;font-family:courier new;" > EndSubSection</span><br /><span style="font-family:courier new;"> EndSection</span><br /><br />Next restart X (logout and in, or cntrl-alt backsapce). Note that for me the entry was 2560 1024 since my internal display is 1280x800 and the external is 1280x1024 so 1280 + 1280 = 2560 and 1024 > 800 so use 1024. You will need to do the math for your case.<br /><br />At this point the "Screen Resolution" application works correctly to set up the displays. Readers may also be <span class="blsp-spelling-corrected" id="SPELLING_ERROR_0">interested</span> in the article at <a href="http://www.thinkwiki.org/wiki/Sample_Fn-F7_script">http://www.thinkwiki.org/wiki/Sample_Fn-F7_scrip</a>t which shows a nice shell script that can be connected to key combinations for all this.<br /><br />take care<br />Doug<br /><br />UPDATE April 28th:<br /><br />Just a follow up to this posting. It turns out that in several cases (and low end laptop graphics like mine among them) that doing this will<br /><br /><span style="font-style: italic;">disable DVI and thus not allow you to run compiz</span><br /><br />For me nice graphics vs dual monitor is a no contest. I'll take the dynamic xrandr over compiz any day.filshttp://www.blogger.com/profile/04578829160811273052noreply@blogger.com4tag:blogger.com,1999:blog-7651912408580529938.post-58640114263706868512008-04-22T08:41:00.001-05:002008-04-22T08:51:42.112-05:00Sorting multidimensional arrays in GroovyI didn't find this solution as quickly as I thought I would so I thought I would post about it here to provide some search engine fodder.<br id="d:r3"><br id="ussy">If you are dealing with multidimensional arrays in Groovy (ref: <a title="http://groovy.codehaus.org/JN1025-Arrays" target="_blank" href="http://groovy.codehaus.org/JN1025-Arrays" id="sdhs">http://groovy.codehaus.org/JN1025-Arrays</a> ) and want to sort them you can expand on the sorting concepts for collections in Groovy (ref: <a title="http://groovy.codehaus.org/JN1015-Collections" target="_blank" href="http://groovy.codehaus.org/JN1015-Collections" id="i686">http://groovy.codehaus.org/JN1015-Collections</a> ). <br id="hq:b"><br id="e86v">If you look in the last reference you will find the example:<br id="n.q3"> <pre id="a85v" style="border: 1px dashed rgb(153, 153, 153); padding: 5px; overflow: auto; font-family: Andale Mono,Lucida Console,Monaco,fixed,monospace; color: rgb(0, 0, 0); background-color: rgb(238, 238, 238); font-size: 12px; line-height: 14px; width: 100%;">def mc= [<br id="w:.l"> compare: {a,b-> a.equals(b)? 0: Math.abs(a)<Math.abs(b)? -1: 1 }<br id="y_is">] as Comparator<br id="wj1h"></pre> We can modify this for a multidimensional array. So some excerpts from my code:<br id="zmhn"><pre id="a85v" style="border: 1px dashed rgb(153, 153, 153); padding: 5px; overflow: auto; font-family: Andale Mono,Lucida Console,Monaco,fixed,monospace; color: rgb(0, 0, 0); background-color: rgb(238, 238, 238); font-size: 12px; line-height: 14px; width: 100%;">println "Pre sort: ${uniqueAges}"<br id="uoh-">def mc = [<br id="s92g"> compare: {a, b -> a[0].equals(b[0]) ? 0 : a[0] < b[0] ? -1 : 1 }<br id="a2lu">] as Comparator<br id="j:_n">println "Post sort: ${uniqueAges.sort(mc)}"<br id="n-v1"></pre> results in<br id="eqoi"><pre id="a85v" style="border: 1px dashed rgb(153, 153, 153); padding: 5px; overflow: auto; font-family: Andale Mono,Lucida Console,Monaco,fixed,monospace; color: rgb(0, 0, 0); background-color: rgb(238, 238, 238); font-size: 12px; line-height: 14px; width: 100%;">Pre sort: [[11.2, 5.32], [1.77, 0.01], [3.58, 1.77], [5.32, 1.77], [5.32, 3.58]]<br id="ru9s">Post sort: [[1.77, 0.01], [3.58, 1.77], [5.32, 1.77], [5.32, 3.58], [11.2, 5.32]]</pre>or <br id="tp9d"><pre id="a85v" style="border: 1px dashed rgb(153, 153, 153); padding: 5px; overflow: auto; font-family: Andale Mono,Lucida Console,Monaco,fixed,monospace; color: rgb(0, 0, 0); background-color: rgb(238, 238, 238); font-size: 12px; line-height: 14px; width: 100%;"> println "Pre sort: ${ageZoneList}"<br id="vtc8"> def mc2 = [<br id="ng6a"> compare: {a, b -> a[0][0].equals(b[0][0]) ? 0 : a[0][0] < b[0][0] ? -1 : 1 }<br id="m8xm"> ] as Comparator<br id="l3xy"> println "Post sort: ${ageZoneList.sort(mc2)}"</pre> results in<br id="c7if"><pre id="a85v" style="border: 1px dashed rgb(153, 153, 153); padding: 5px; overflow: auto; font-family: Andale Mono,Lucida Console,Monaco,fixed,monospace; color: rgb(0, 0, 0); background-color: rgb(238, 238, 238); font-size: 12px; line-height: 14px; width: 100%;">Pre sort: [[[5.32, 11.2], [163.5, 189.62, "Late Miocene"]], [[0.01, 1.77], [0.05, 105.58, "Pleistocene"]], [[1.77, 3.58], [114.82, 123.68, "Late Pliocene"]], [[1.77, 5.32], [103.13, 163.53, "Pliocene"]], [[3.58, 5.32], [137.16, 156.19, "Early Pliocene"]]]<br id="n1pw">Post sort: [[[0.01, 1.77], [0.05, 105.58, "Pleistocene"]], [[1.77, 3.58], [114.82, 123.68, "Late Pliocene"]], [[1.77, 5.32], [103.13, 163.53, "Pliocene"]], [[3.58, 5.32], [137.16, 156.19, "Early Pliocene"]], [[5.32, 11.2], [163.5, 189.62, "Late Miocene"]]]<br id="h:or"><code id="wwja"><br id="ix9:"></code></pre> <br id="mna9">Note that in Groovy multidimensional arrays need not have the same exact length at each level as noted in the Array reference above. This does mean that you need to take care with respect to iterating your indexes if your array is triangular. This is not likely the most efficient search for a large multidimensional array, but for simple sorts on smaller examples its working well for me. I would appreciate references for quicker sorts if people have them.<br id="y8iz"><br id="clw6">take care<br id="mtwc">Doug<br id="t.y8"><br id="a75b"> filshttp://www.blogger.com/profile/04578829160811273052noreply@blogger.com0tag:blogger.com,1999:blog-7651912408580529938.post-70910289178399764712008-04-21T11:06:00.001-05:002008-04-21T11:11:00.128-05:00Spaces in REST URLsSo I need to deal with spaces in REST URL's. Something like:<br id="oicz"><br id="nlkf"><span id="glao" style="font-family: Courier New;"> .../timescale/Late Jurassic</span><br id="z:y8"><br id="m38k">I'd rather not allow these types of URL's (since I don't think the spec even allows them) and was thinking about the programming and also community practices issues this raises. If a person wants a space they likely should use "%20", however this just doesn't look good and I believe gets confusing. <br id="r_1r"><br id="hkdt">I could simply allow the community to create such URL's even though I believe them wrong and then attempt to resolve them in my Grails application with:<br id="pi9j"><br id="u8i3"><span id="z6lm" style="font-family: Courier New;">someString.replaceAll("%20", " ")</span><br id="s_11"><br id="hjpy">since firefox and I suspect other browsers will convert the spaces to "%20" for the user. <br id="qen2"><br id="pirh">This seems a poor approach though since if someone puts two spaces into the URL or somehow a tab gets in things get ugly fast. A person just has far too many bad issues to deal with.<br id="nadn"><br id="jhnc">Forcing the community to use camel case (here I will use lower camel case but one could use upper camel case) would require a URL like:<br id="e63q"><br id="s9:q"><span id="r_jz" style="font-family: Courier New;">.../timescale/lateJurasic</span><br id="dptn"><br id="qrfi">where I could split the parameter in Groovy with:<br id="vn4a"><br id="jp1o"><span id="s2q5" style="font-family: Courier New;">someString.replaceAll("([A-Z])", " <font id="wwed" face="Wingdings">$</font>1").trim()</span><br id="ie.7"><br id="xcn_">so "lateJurassic" becomes "late Jurassic" which is what is in my database (actually "Late Jurassic" but I can case insensitive search) since that is the "proper" form of the name.<br id="gu4d"><br id="w4la">This puts the burden of creating a camel case representation of "Late Jurassic" on the client end of the application (ie, make this someone else's problem, which is always a good thing). <br id="vmr_"><br id="cm_y">I suspect this is just a case where <span id="g9-g"><i>the best resource identifier in the database is not always the best resource identifier for the REST URL representation</i></span>. It would seem then that apply a best practice approach one would use a camel case representation. This might be a situation where using upper camel case is better yet since the proper cases on the name is "Late Jurassic" resulting in a URL:<br id="fjb4"><br id="fy-b"><span id="gbe0" style="font-family: Courier New;"> .../timescale/LateJurassic </span><br id="om83"><br id="wlzu">That is still fine since the .trim() call will remove the leading space still in the above code. I'd love to hear other thoughts on such mappings.<br id="cu7g"><br id="stab">take care<br id="epr3">Doug<br id="mffz"><br id="xc2c"> filshttp://www.blogger.com/profile/04578829160811273052noreply@blogger.com3tag:blogger.com,1999:blog-7651912408580529938.post-30080419940619972752008-04-14T09:19:00.001-05:002008-04-14T09:21:56.159-05:00Javascript comparison of WebKit and Firefox 3 Beta 5 on UbuntuThis weekend I had a small pet project I did that gave me a need for a webkit based browser on Linux (Ubuntu 7.04). While I could have used Konqueror I was more interested in a later source base and so downloaded the WebKit r31738 source from <a title="http://www.webkit.org" target="_blank" href="http://www.webkit.org" id="jxo-">http://www.webkit.org</a>. Actually, after a couple simple apt-get install for the development library this source package will compile just fine on a rather stock Ubuntu 7.04 and runs well (Ref: <a title="http://live.gnome.org/WebKitGtk" target="_blank" href="http://live.gnome.org/WebKitGtk" id="nf2z">http://live.gnome.org/WebKitGtk</a> ). The default UI shell for the webkit core is ultra basic but all I need for testing against this rendering engine. One can use webkit as the engine for Epiphany (<a title="http://live.gnome.org/Epiphany/WebKit" target="_blank" href="http://live.gnome.org/Epiphany/WebKit" id="cdu.">http://live.gnome.org/Epiphany/WebKit</a> ) if you want a nicer wrapper. <br id="ofq_"><br id="kjhd">I was curious to try this new engine (more specifically it's javascript aspect) against my current favorite browser Firefox in the form of Firefox 3 Beta 5 (<a title="http://www.mozilla.com/en-US/firefox/all-beta.html" target="_blank" href="http://www.mozilla.com/en-US/firefox/all-beta.html" id="sw41">http://www.mozilla.com/en-US/firefox/all-beta.html</a> ). A tried both the <a title="Dromaeo" target="_blank" href="http://dromaeo.com/" id="w1-o">Dromaeo</a> test suite (still in early release, review notes at: <a title="http://wiki.mozilla.org/Dromaeo" target="_blank" href="http://wiki.mozilla.org/Dromaeo" id="i-vw">http://wiki.mozilla.org/Dromaeo</a> ) and Sunspider from: <a title="http://webkit.org/perf/sunspider-0.9/sunspider.html" target="_blank" href="http://webkit.org/perf/sunspider-0.9/sunspider.html" id="riif">http://webkit.org/perf/sunspider-0.9/sunspider.html</a>. My platform is rather paltry Athlon 64 3200+ and 2 gigs of memory running the afore mentioned Ubuntu 7.04. <br id="hxew"><br id="oscq">Firefox 3 Beta 5:<br id="mkdh"><a title="http://dromaeo.com/?id=5552" target="_blank" href="http://dromaeo.com/?id=5552" id="njcl">http://dromaeo.com/?id=5552</a> (3282.80ms)<br id="ak_:"><a title="http://dromaeo.com/?id=5560" target="_blank" href="http://dromaeo.com/?id=5560" id="g8ic">http://dromaeo.com/?id=5560</a> (3287.60ms)<br id="j1cf"><a title="SunSpider: 6794.0ms" target="_blank" href="http://webkit.org/perf/sunspider-0.9/sunspider-results.html?%7B%223d-cube%22:%5B277,274,283,284,282%5D,%223d-morph%22:%5B241,269,272,268,268%5D,%223d-raytrace%22:%5B229,255,237,238,252%5D,%22access-binary-trees%22:%5B162,178,169,167,181%5D,%22access-fannkuch%22:%5B366,359,364,362,366%5D,%22access-nbody%22:%5B292,290,267,293,288%5D,%22access-nsieve%22:%5B135,165,168,161,158%5D,%22bitops-3bit-bits-in-byte%22:%5B168,174,159,165,165%5D,%22bitops-bits-in-byte%22:%5B192,182,176,179,183%5D,%22bitops-bitwise-and%22:%5B150,139,151,150,139%5D,%22bitops-nsieve-bits%22:%5B239,238,225,224,231%5D,%22controlflow-recursive%22:%5B162,166,134,147,156%5D,%22crypto-aes%22:%5B170,146,162,169,146%5D,%22crypto-md5%22:%5B176,164,172,171,161%5D,%22crypto-sha1%22:%5B163,151,164,150,153%5D,%22date-format-tofte%22:%5B368,366,363,364,377%5D,%22date-format-xparb%22:%5B228,234,247,241,225%5D,%22math-cordic%22:%5B292,305,292,306,309%5D,%22math-partial-sums%22:%5B255,270,238,247,273%5D,%22math-spectral-norm%22:%5B217,211,213,201,202%5D,%22regexp-dna%22:%5B546,537,531,537,538%5D,%22string-base64%22:%5B202,203,190,188,206%5D,%22string-fasta%22:%5B465,445,431,445,469%5D,%22string-tagcloud%22:%5B376,352,356,352,364%5D,%22string-unpack-code%22:%5B495,489,483,497,500%5D,%22string-validate-input%22:%5B261,249,257,268,262%5D%7D" id="bcs1">SunSpider: 6794.0ms</a> <br id="a5qv"><br id="md4l">WebKit <br id="pg7x"><a title="http://dromaeo.com/?id=5556" target="_blank" href="http://dromaeo.com/?id=5556" id="kyy5">http://dromaeo.com/?id=5556</a> (3260.60ms)<br id="k3xp"><a title="http://dromaeo.com/?id=5563" target="_blank" href="http://dromaeo.com/?id=5563" id="vlpq">http://dromaeo.com/?id=5563</a> (3225.40ms)<br id="yeyw"><a title="SunSpider: 5794.8ms" target="_blank" href="http://webkit.org/perf/sunspider-0.9/sunspider-results.html?%7B%223d-cube%22:%5B260,265,261,276,264%5D,%223d-morph%22:%5B240,238,239,237,239%5D,%223d-raytrace%22:%5B232,227,230,231,228%5D,%22access-binary-trees%22:%5B114,115,117,115,114%5D,%22access-fannkuch%22:%5B443,437,451,443,447%5D,%22access-nbody%22:%5B262,255,258,256,261%5D,%22access-nsieve%22:%5B112,112,112,112,113%5D,%22bitops-3bit-bits-in-byte%22:%5B95,96,95,98,96%5D,%22bitops-bits-in-byte%22:%5B157,159,161,162,159%5D,%22bitops-bitwise-and%22:%5B236,237,240,240,240%5D,%22bitops-nsieve-bits%22:%5B168,168,171,167,172%5D,%22controlflow-recursive%22:%5B95,94,94,93,93%5D,%22crypto-aes%22:%5B136,139,136,138,141%5D,%22crypto-md5%22:%5B101,100,100,101,99%5D,%22crypto-sha1%22:%5B106,105,106,105,104%5D,%22date-format-tofte%22:%5B277,284,283,288,284%5D,%22date-format-xparb%22:%5B402,402,406,404,406%5D,%22math-cordic%22:%5B271,268,289,278,282%5D,%22math-partial-sums%22:%5B279,269,274,273,274%5D,%22math-spectral-norm%22:%5B157,159,157,160,162%5D,%22regexp-dna%22:%5B345,342,343,342,345%5D,%22string-base64%22:%5B204,206,206,206,205%5D,%22string-fasta%22:%5B280,279,276,275,279%5D,%22string-tagcloud%22:%5B292,292,298,294,300%5D,%22string-unpack-code%22:%5B265,263,265,265,266%5D,%22string-validate-input%22:%5B246,247,247,246,248%5D%7D" id="vywk">SunSpider: 5794.8ms</a> <br id="qhu4"><br id="i:8q"><br id="nf5a">In the Dromaeo test there is for all practical purposes no difference between the two javascript engines. Sunspider showed a 1.17 factor favor toward the Webkit engine. There has been a lot of recent new traffic over acid3 testing and other performance metrics (<a title="http://ajaxian.com/archives/where-is-firefox-on-acid-3-here" target="_blank" href="http://ajaxian.com/archives/where-is-firefox-on-acid-3-here" id="zh-9">http://ajaxian.com/archives/where-is-firefox-on-acid-3-here</a> ). The Firefox ecosystem around its amazing set of add-ones (<a title="https://addons.mozilla.org/en-US/firefox/" target="_blank" href="https://addons.mozilla.org/en-US/firefox/" id="q0f4">https://addons.mozilla.org/en-US/firefox/</a> ) is without question a key factor for me. The Mozilla foundations strong effort on standards, highly open and extensible platform win over a few percentage points from a Javascript benchmark. The acid3's focus on DOM scripting is important to me and so I would like to see that arrive in the Gecko engine though some of the comments of Mike Shaver (<a title="http://shaver.off.net/diary/2008/03/27/the-missed-opportunity-of-acid-3/" target="_blank" href="http://shaver.off.net/diary/2008/03/27/the-missed-opportunity-of-acid-3/" id="piof">http://shaver.off.net/diary/2008/03/27/the-missed-opportunity-of-acid-3/</a> ) and Rob Sayre (<a title="http://blog.mozilla.com/rob-sayre/2008/03/26/acid3-is-basically-worthless/" target="_blank" href="http://blog.mozilla.com/rob-sayre/2008/03/26/acid3-is-basically-worthless/" id="eg5v">http://blog.mozilla.com/rob-sayre/2008/03/26/acid3-is-basically-worthless/</a> ) regarding this do make sense to me. <br id="jpl8"><br id="u8y8">I'm a loyal Firefox user and nothing I have seen from the Webkit camp has yet to give me any pause, though I am glad they are there. We need competition and pressure to drive development forward. It's always good to compare and I am still in a situation where as a developer I need to have various engines around for testing. I also played around with the new Next-Generation Java Plug-in (<a title="https://jdk6.dev.java.net/plugin2/" target="_blank" href="https://jdk6.dev.java.net/plugin2/" id="rv-j">https://jdk6.dev.java.net/plugin2/</a> ) and was very impressed with it as well. Especially the easy javascript to applet communication the new plugin delivers. <br id="s6lm"><br id="xxwu">take care<br id="swwh">Doug<br id="f1-l"><br id="oms6">filshttp://www.blogger.com/profile/04578829160811273052noreply@blogger.com0tag:blogger.com,1999:blog-7651912408580529938.post-57273411296581050162008-03-26T14:09:00.001-05:002008-03-26T14:40:56.936-05:00Grails datasource in resource.xml <br id="hnkg">So I have the need for a couple extra datasources in my grails application. This turned out to take a bit more research than I first thought it would, though likely just due to my lack of much heavy duty spring experience so far. Basically I just want access to another database resource that is NOT my main datasource used by the hibernate ORM. My domain classes for this application are separate from my need to access this other datasource which I would do just via SQL calls. <br id="cy:s"><br id="oxa:">A quick review of the Nabble list gave me a couple of leads:<br id="pbac"><a title="http://www.nabble.com/dataSource-on-ApplicationBootStrap-td11441836.html#a11441836" href="http://www.nabble.com/dataSource-on-ApplicationBootStrap-td11441836.html#a11441836" id="d7jk">http://www.nabble.com/dataSource-on-ApplicationBootStrap-td11441836.html#a11441836</a> <br id="vr4p"><a title="http://www.nabble.com/multiply-datasources--td10038844.html#a10038844" href="http://www.nabble.com/multiply-datasources--td10038844.html#a10038844" id="j-b-">http://www.nabble.com/multiply-datasources--td10038844.html#a10038844</a> <br id="ai04"> <br id="w21o">I created the following entry in resource.xml located in GRAILS_APP/web-app/WEB-INF/spring directory. Create this path and file if it doesn't already exist. The full XML document is like:<br id="h3ri"><br id="n.q3"> <pre id="a85v" style="border: 1px dashed rgb(153, 153, 153); padding: 5px; overflow: auto; font-family: Andale Mono,Lucida Console,Monaco,fixed,monospace; color: rgb(0, 0, 0); background-color: rgb(238, 238, 238); font-size: 12px; line-height: 14px; width: 100%;"><code id="wwja"><?xml version="1.0" encoding="UTF-8"?><br id="ojmz"><beans xmlns="http://www.springframework.org/schema/beans"<br id="bvxu"> xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"<br id="v8:."> xsi:schemaLocation="<br id="q5uj">http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd"><br id="o8ux"><br id="d355"> <bean id="dataSourceJanus" class="org.springframework.jdbc.datasource.SingleConnectionDataSource"><br id="rex2"> <property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"/><br id="ho_5"> <property name="url" value="'jdbc:oracle:thin:@xxx.xxx.xxx.xxx:1521:janware"/><br id="wipn"> <property name="username" value="NAME"/><br id="gzg3"> <property name="password" value="PASSWORD"/><br id="wpaa"> </bean><br id="xv28"></beans></code><br id="t89q"></pre> <br id="w76t">though you may only need the bean entry if the file already exists of course. Thanks Alex (again) for finding my insanely stupid typo in this file. I stared for hours (sad to say) at it. <br id="xp:l"><br id="e06r">The only tricky part is that you can not directly reference the new datasource ( here called dataSourceJanus in my example) in say your controller class. Rather you will need to implement ApplicationContextAware. So you will have something like:<br id="xfu9"><br id="n.q3"> <pre id="a85v" style="border: 1px dashed rgb(153, 153, 153); padding: 5px; overflow: auto; font-family: Andale Mono,Lucida Console,Monaco,fixed,monospace; color: rgb(0, 0, 0); background-color: rgb(238, 238, 238); font-size: 12px; line-height: 14px; width: 100%;"><code id="wwja">// Needed for datasource <br id="m04-">import org.springframework.jdbc.core.JdbcTemplate<br id="vw.m">import groovy.sql.Sql<br id="lx56"><br id="extj"><br id="ha7p">// Spring<br id="wuhx">import org.springframework.beans.BeansException<br id="szlz">import org.springframework.context.ApplicationContext<br id="vd9q">import org.springframework.context.ApplicationContextAware<br id="j3.s">import org.codehaus.groovy.grails.commons.GrailsApplication<br id="p0ts"><br id="rs3t">class FacetController implements ApplicationContextAware {<br id="qq53"> <br id="bzt."> GrailsApplication grailsApplication<br id="g7-3"> ApplicationContext appCtx<br id="r624"><br id="a0-v"> def void setApplicationContext(ApplicationContext arg0) throws BeansException {<br id="hhkd"> appCtx = arg0;<br id="se13"> }<br id="k4cx"><br id="oz20">...<br id="t89q"></code></pre> <br id="vnik">This will get you your application context object. Now it's rahter straight forward to use:<br id="sbns"><br id="l8wr"><pre id="a85v" style="border: 1px dashed rgb(153, 153, 153); padding: 5px; overflow: auto; font-family: Andale Mono,Lucida Console,Monaco,fixed,monospace; color: rgb(0, 0, 0); background-color: rgb(238, 238, 238); font-size: 12px; line-height: 14px; width: 100%;"><code id="wwja">def showFacets = {<br id="dym9"> def jt = new JdbcTemplate(<span id="ewt."><b id="phvn">appCtx.dataSourceJanus</b></span>)<br id="ldkz"> def sqlString = "select latitude_degrees, longitude_degrees from hole where leg like '101' and site like '631' and hole like 'A'"<br id="qmrp"></code><code id="wwja"> response.contentType = "text/plain"</code><br id="fkmq"><code id="wwja"> jt.queryForList(sqlString).each { <br id="jct1"> response.outputStream << "${it.latitude_degrees} ${it.longitude_degrees}"<br id="frcp"> }<br id="o9pm">}</code></pre><br id="odmr">That is just a dead simple method to call the datasource (bold) and writes directly to the output stream. Not the org.springframework.jdbc.core.JdbcTemplate import in the previous code sample along with the other imports used for the application context. Obviously you will likely return to a view or render a JSON serialization or some such in your method rather than write directly to the outputStream. <br id="px:p"><br id="c5hm">If you place def jt = new JdbcTemplate(appCtx.dataSourceJanus) outside the scope of the method you get a null reference at start up. So it seems this should be scoped inside the method that uses it. However, this logic might be better moved into a service class anyway depending on how much you will use it. <br id="q0vh"><br id="l:_s">Some additional good reading on this includes:<br id="wvrg">Datasources: <a title="http://docs.codehaus.org/display/GRAILS/DataSources+New" href="http://docs.codehaus.org/display/GRAILS/DataSources+New" id="s4ii">http://docs.codehaus.org/display/GRAILS/DataSources+New</a> <br id="hdjd"> Using JNDI resources instead: <a title="http://docs.codehaus.org/display/GRAILS/JNDI+Data+Sources" href="http://docs.codehaus.org/display/GRAILS/JNDI+Data+Sources" id="qxeh">http://docs.codehaus.org/display/GRAILS/JNDI+Data+Sources</a> <br id="m4h4"> Spring Bean Builder: <a title="http://grails.org/Spring+Bean+Builder" href="http://grails.org/Spring+Bean+Builder" id="bfep">http://grails.org/Spring+Bean+Builder</a> <br id="l0me"> Application Context: <a title="http://grails.org/Services" href="http://grails.org/Services" id="c05z">http://grails.org/Services</a> <br id="qbl4"><br id="fkn3">If you review and follow some of the thread in the Nabble site, you will see that <span id="v.gv"><b id="hwol">seems</b></span> one could in fact use dynamic methods with this new datasource if you do a few extra steps. I have not done this but it appears to involve making some entries in the hibernate.cfg.xml file. Reference <a title="http://www.nabble.com/How-to-get-multi-dataSource-in-grails--td12291409.html#a12291409" href="http://www.nabble.com/How-to-get-multi-dataSource-in-grails--td12291409.html#a12291409" id="l2.7">http://www.nabble.com/How-to-get-multi-dataSource-in-grails--td12291409.html#a12291409</a> for some comments I found on trying to do this. There does seem to be some issues I am hearing about having/using dynamic methods on multiple datasource though, so I am not sure how this all plays out. <br id="tuft"><br id="h395">For my needs however, just having an SQL resource that I could use the very nice <a title="Groovy SQL" href="http://groovy.codehaus.org/Database+features" id="fn4s">Groovy SQL</a> capacity with. <br id="wfq4"><br id="w2rc">enjoy<br id="u9ab">Doug<br id="x1jj"><br id="jzom">filshttp://www.blogger.com/profile/04578829160811273052noreply@blogger.com11tag:blogger.com,1999:blog-7651912408580529938.post-81597619865863832972007-12-06T12:04:00.000-06:002007-12-07T13:57:27.161-06:00Flot plotting in Grails:I came across the FLOT package (Ref: <a title="http://code.google.com/p/flot/" target="_blank" href="http://code.google.com/p/flot/" id="ps3n">http://code.google.com/p/flot/</a> ) while reading Ajaxian (Ref: <a title="http://ajaxian.com/archives/plotting-in-jquery" target="_blank" href="http://ajaxian.com/archives/plotting-in-jquery" id="ys.8">http://ajaxian.com/archives/plotting-in-jquery</a> ). Flot uses the jQuerry package (Ref: <a title="http://jquery.com/" target="_blank" href="http://jquery.com/" id="upf7">http://jquery.com/</a> ) to do client side plotting. I wanted to see how this works when dropped into Grails. As expected the results are quite nice.<br /><div id="xa77" style="padding: 1em 0pt; text-align: left;"><img style="width: 711px; height: 547px;" src="http://docs.google.com/File?id=dgtb8v9j_225dk9stmgc" /></div><br />The set up is rather easy. Obtain the jquery.flot-0.1.js and jquery-1.2.1.min.js files (your versions will obviously change with the march of time) and place these in your web-app/js directory of your grails project.<br />You will need to reference these in your GSP page HEAD tag with something like:<br /><br /> <pre style="border: 1px dashed rgb(153, 153, 153); padding: 5px; overflow: auto; font-family: Andale Mono,Lucida Console,Monaco,fixed,monospace; color: rgb(0, 0, 0); background-color: rgb(238, 238, 238); font-size: 12px; line-height: 14px; width: 100%;"><code> <g:javascript library="jquery-1.2.1.min"/><br /><g:javascript library="jquery.flot-0.1"/></code><br /></pre> <br />At this point your are basically done. You can review the URL's at the top of this page for better information straight from the source. Basically, you will need to place into the BODY tag of your GSP something like the following:<br /><br /> <pre style="border: 1px dashed rgb(153, 153, 153); padding: 5px; overflow: auto; font-family: Andale Mono,Lucida Console,Monaco,fixed,monospace; color: rgb(0, 0, 0); background-color: rgb(238, 238, 238); font-size: 12px; line-height: 14px; width: 100%;"><code> <div id="placeholder2" style="margin-left:100px;width:600px;height:300px;"></div><br /><br /><script id="source2" language="javascript" type="text/javascript"><br /> $(function () {<br /> var d1 = [];<br /> for (var i = 0; i < 14; i += 0.5)<br /> d1.push([i, Math.sin(i)]);<br /><br /> var d2 = ${array};<br /><br /> $.plot($("#placeholder2"), [ d1, d2 ]);<br /> });<br /></script></code><br /></pre> Here is the only interesting aspect of all this. The line <b><i>var d2 = ${array}</i></b> is referencing a list from the controller:<br /><br /> <pre style="border: 1px dashed rgb(153, 153, 153); padding: 5px; overflow: auto; font-family: Andale Mono,Lucida Console,Monaco,fixed,monospace; color: rgb(0, 0, 0); background-color: rgb(238, 238, 238); font-size: 12px; line-height: 14px; width: 100%;"><code> def jflotPlot = {<br /> def array = [[4, 6], [5, 7], [9, 2]]<br /> [array: array]<br />}</code><br /></pre> <br />Likely your controller will get more interesting data. ;) This is just a really quick and dirty copy paste job of the examples from the above references into a Grails app. Flot can already at version 0.1 do more types and plots and zooming and other very slick features (<a title="see the demos at the flot site" target="_blank" href="http://people.iola.dk/olau/flot/examples/" id="u36r">see the demos at the flot site</a> ). Integration with Grails is quick and easy.<br /><br />enjoy<br />Dougfilshttp://www.blogger.com/profile/04578829160811273052noreply@blogger.com3tag:blogger.com,1999:blog-7651912408580529938.post-31014967602339312352007-11-14T10:39:00.000-06:002007-11-16T07:32:01.519-06:00Grails + ROME + geoRSS<b>Grails + ROME + geoRSS</b><br /><br />If you are looking to do some RSS/ATOM feeds in Grails you likely are looking at either at the examples that Glen Smith has done (ref: <a href="http://blogs.bytecode.com.au/glen/2006/12/22/1166781151213.html" id="pr-0" title="http://blogs.bytecode.com.au/glen/2006/12/22/1166781151213.html">http://blogs.bytecode.com.au/glen/2006/12/22/1166781151213.html</a> ) or to work with the Feeds plugins (ref: <a href="http://grails.org/Feeds+Plugin" id="yt2j" title="http://grails.org/Feeds+Plugin">http://grails.org/Feeds+Plugin</a> ) by Marc Palmer. Both are excellent places to start.<br /><br />What I want to talk about there is adding geospatial data to a RSS feed in the Grails framework by building off this work. The above efforts leverage off the ROME package from <a href="http://rome.dev.java.net/" id="b_4r" title="http://rome.dev.java.net">http://rome.dev.java.net</a>. For geospatial elements there is the GeoRSS Module for ROME by Marc Wick (ref: <a href="http://georss.geonames.org/" id="x8ge" title="http://georss.geonames.org">http://georss.geonames.org</a> ).<br /><br />Adding this ability to a Grails project is rather easy. Obviously you need to have the Rome and GeoRSS jar files from these referenced sites in your project lib directory. From there you can do something like the following. Please note these examples are based on the work of these previously mentioned individuals and the credit is theirs. I just glued things together.<br /><br />So likely you will end up with something like this in your controller:<br /><br /> <pre style="border: 1px dashed rgb(153, 153, 153); padding: 5px; overflow: auto; font-family: Andale Mono,Lucida Console,Monaco,fixed,monospace; color: rgb(0, 0, 0); background-color: rgb(238, 238, 238); font-size: 12px; line-height: 14px; width: 100%;"><code> // some import for this.. (watch me polute my namespace) ;)<br />import com.sun.syndication.feed.synd.*;<br />import com.sun.syndication.io.SyndFeedOutput;<br />import com.sun.syndication.feed.module.georss.*;<br />import com.sun.syndication.feed.module.georss.geometries.*;<br /><br /><br /> def supportedFormats = ["rss_0.90", "rss_0.91", "rss_0.92", "rss_0.93", "rss_0.94", "rss_1.0", "rss_2.0", "atom_0.3"]<br /><br /> def rss = {<br /> render(text: getFeed("atom_0.3"), contentType: "text/xml", encoding: "UTF-8")<br /> }<br /><br /> def all = {<br /> def format = params.id<br /> if (supportedFormats.contains(format)) {<br /> render(text: getFeed(format), contentType: "text/xml", encoding: "UTF-8")<br /> } else {<br /> response.sendError(response.SC_FORBIDDEN);<br /> }<br /> }<br /><br /> def getFeed(feedType) {<br /><br /> def ageModels = AgeModel.list()<br /> def jdb = Sql.newInstance('[ WE ARE MAKING SQL CALLS SO THE CONNECT STRING GOES HERE. GORM WOULD BE NICER, BUT NOT AN OPTION FOR ME')<br /> <br /> def entries = []<br /> ageModels.each {ageModel -><br /> List functionData = jdb.rows("select latitude_degrees, longitude_degrees from hole where leg like ${ageModel.leg} and site like ${ageModel.site} and hole like '${ageModel.hole}'")<br /> def locationString = functionData[0][1] + ", " + functionData[0][0]<br /><br /> def desc = new SyndContentImpl(type: "text/plain", value: "Entry for leg " + ageModel.leg + " locate at " + locationString + ageModel.rating + " " + ageModel.status);<br /> def entry = new SyndEntryImpl(title: ageModel.leg + "_" +ageModel.site + ageModel.hole + " - " + ageModel.user,<br /> link: 'http://localhost:8080/janusAmp/rest/lsh/' + ageModel.leg + "/" + ageModel.site+ "/"+ageModel.hole,<br /> publishedDate: ageModel.date, description: desc);<br /><br /><b> // Select the style of encoding you want to do.<br /> // def geoRSSModule = new W3CGeoModuleImpl()<br /> // def geoRSSModule = new GMLModuleImpl()<br /> def geoRSSModule = new SimpleModuleImpl()<br /> geoRSSModule.setPosition(new Position(functionData[0][0], functionData[0][1]));<br /> entry.getModules().add(geoRSSModule);<br /> entries.add(entry);<br /></b> }<br /><br /> SyndFeed feed = new SyndFeedImpl(feedType: feedType, title: 'Recently added age models',<br /> link: 'http://www.chronos.org', description: 'List of age models from Janus data',<br /> entries: entries);<br /><br /> StringWriter writer = new StringWriter();<br /> SyndFeedOutput output = new SyndFeedOutput();<br /> output.output(feed, writer);<br /> writer.close();<br /><br /> return writer.toString();<br /> }<br /><br /></code> </pre> The only thing different than what Glen, shows in his blog is the addition of the bold lines to add in the geoRss element. The result... geospatially enabled RSS feeds in the Grails framework (and anywhere else for Groovy and Java of course)<br /><br />A few things to note:<br />a) While Google Earth and Google Maps should read this output I had some issues with the resulting files. Running things through the validator at: <a title="http://cite.opengeospatial.org/test_engine/georss_validator/" href="http://cite.opengeospatial.org/test_engine/georss_validator/" id="hma9">http://cite.opengeospatial.org/test_engine/georss_validator/</a> one gets some errors related to the namespaces. I noticed that the examples for RSS feeds at <a title="http://georss.org/" href="http://georss.org/" id="kvsv">http://georss.org/</a> even had some issues. While the ATOM version validates ok the RSS based feed errors out in the same way that my generated feeds do. Likely this is an issue with ROME and if I track down the issue I will follow up on it. Until the feeds validate these feeds do not appear to work in Google Earth or Maps.<br />b) However, you can generate the rss version 2 feed with the SimpleModuleImpl() from the georss jar file and that will translate correctly with the rss to kml style sheet found at <a title="http://www.kovacevic.nl/hacks/kml/georss2kml.xsl" href="http://www.kovacevic.nl/hacks/kml/georss2kml.xsl" id="d:ic">http://www.kovacevic.nl/hacks/kml/georss2kml.xsl</a> (ref: <a title="http://www.kovacevic.nl/blog" href="http://www.kovacevic.nl/blog" id="sua:">http://www.kovacevic.nl/blog</a> ).<br /><br /><b>RSS to KML via XSL in Grails:</b><br /><br />So you can drop into your controller something like:<br /> <pre style="border: 1px dashed rgb(153, 153, 153); padding: 5px; overflow: auto; font-family: Andale Mono,Lucida Console,Monaco,fixed,monospace; color: rgb(0, 0, 0); background-color: rgb(238, 238, 238); font-size: 12px; line-height: 14px; width: 100%;"><code> def kml = {<br /> def xslPath = getAppPathService.serviceMethod().toString() + "georss2kml.xsl"<br /> def kmlxsl = new File(xslPath).getText().toString()<br /> def rssxml = getFeed("rss_2.0")<br /><br /> def mywriter = new StringWriter()<br /><br /> def factory = TransformerFactory.newInstance()<br /> def transformer = factory.newTransformer(new StreamSource(new StringReader(kmlxsl)))<br /> transformer.transform(new StreamSource(new StringReader(rssxml)), new StreamResult(mywriter))<br /><br /> render(text: mywriter.toString(), contentType: "text/xml", encoding: "UTF-8")<br /> }<br /></code> </pre> you make also need the following in your code for XSL support: <pre>// for the XSLT transform<br />import javax.xml.transform.TransformerFactory<br />import javax.xml.transform.stream.StreamResult<br />import javax.xml.transform.stream.StreamSource<br /></pre> <br />into your controller where AppPathService is (not mine.. but I don't have the reference for this.. bad me)<br /><br /><pre style="border: 1px dashed rgb(153, 153, 153); padding: 5px; overflow: auto; font-family: Andale Mono,Lucida Console,Monaco,fixed,monospace; color: rgb(0, 0, 0); background-color: rgb(238, 238, 238); font-size: 12px; line-height: 14px; width: 100%;"><code> import org.codehaus.groovy.grails.commons.*<br />import org.apache.commons.logging.*<br /><br />import org.springframework.beans.BeansException<br />import org.springframework.context.ApplicationContext<br />import org.springframework.context.ApplicationContextAware<br /><br />class GetAppPathService implements ApplicationContextAware {<br /><br /> GrailsApplication grailsApplication<br /> ApplicationContext appCtx<br /><br /> boolean transactional = true<br /><br /> def void setApplicationContext(ApplicationContext arg0) throws BeansException {<br /> appCtx = arg0;<br /> }<br /> def serviceMethod() {<br /> String applicationPath = "${appCtx?.getServletContext()?.getRealPath("/")}"<br /> return applicationPath<br /> }<br />}<br /> </code> </pre> What the KML does is load in the XSL file (it's looking at the root of the response which will be your web-apps directory. You can move and alter this as you wish. Of course you could simple place the XSL in the controller too like at <a title="http://groovy.codehaus.org/Processing+XML+with+XSLT" href="http://groovy.codehaus.org/Processing+XML+with+XSLT" id="aejw">http://groovy.codehaus.org/Processing+XML+with+XSLT</a> which is what kml method is based on. I like this this way a bit better. At this point you should be generating KML from this and Google Earth and Google Maps should load and monitor this URL just fine.<br /><br />I do want the get the GeoRSS feed vaildating though and will work on that effort as noted.<br /><br />enjoy<br />Dougfilshttp://www.blogger.com/profile/04578829160811273052noreply@blogger.com2tag:blogger.com,1999:blog-7651912408580529938.post-6727098849524263552007-11-12T14:16:00.001-06:002007-11-12T14:23:43.235-06:00Checking REST and RSS style feedsContinuing to do work with REST in <a href="http://www.grails.org/">Grails</a> as well as RSS feeds. One issue is testing some of these URL's. In my last post I spoke about a groovy client using Apache's http client package which is quite nice for doing testing with. I also ran across this Firefox extension <a href="http://www.xucia.com/#RestTest">(RestTest)</a> which is quite handy.<br /><br />I also found this to a nice tool for testing RSS feeds with since it's not hard to get into a situation these days where your browser will either try and show you the RSS styled up or even redirect you to Google Reader or some other RSS reader. You can put the RSS/ATOM URL into this extensions interface and simply retrieve the XML of your feed for inspection during coding.<br /><br />On the topic of RSS/ATOM and Grails check out the <a href="http://grails.org/Feeds+Plugin">Feeds Plugin</a> at the Grails site.<br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhD4FXzsR_p-xr7Cd-2Ej5z4knrITC8yUq1aIx-Zk9FLaE5kS61NdyLSwzoMDMsTA5To20eIhMI5cr6sv6DqeZawMPBJ4REgfLqaViVWrvwAUy5vUZt3OA8-Gt-Q4tu37VC-ig3WnfLbPzt/s1600-h/Screenshot-RESTTest+-+HTTP+Tester.png"><img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhD4FXzsR_p-xr7Cd-2Ej5z4knrITC8yUq1aIx-Zk9FLaE5kS61NdyLSwzoMDMsTA5To20eIhMI5cr6sv6DqeZawMPBJ4REgfLqaViVWrvwAUy5vUZt3OA8-Gt-Q4tu37VC-ig3WnfLbPzt/s320/Screenshot-RESTTest+-+HTTP+Tester.png" alt="" id="BLOGGER_PHOTO_ID_5132051988468866322" border="0" /></a>filshttp://www.blogger.com/profile/04578829160811273052noreply@blogger.com0tag:blogger.com,1999:blog-7651912408580529938.post-49810611642422983542007-10-23T14:12:00.000-05:002007-10-23T14:25:09.702-05:00Groovy REST client with Apache httpclient (setting accept request-header value)Been doing quite a bit with REST style services in <a href="http://www.grails.org/">Grails</a>. In particular trying to follow closes REST best practices approaches. One of these approaches is to inspect the HTTP Accept header values. (Ref: <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html">http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html</a>).<br /><br />While creating these services a person really needs a simple client that they can alter the accept request-header field with. I created one using the Apache httpclient package (<a href="http://jakarta.apache.org/httpcomponents/httpclient-3.x/">http://jakarta.apache.org/httpcomponents/httpclient-3.x/</a>).<br /><br />Simply download and place the commons-httpclient-*.jar file into your groovy lib directory (or somewhere you class loader will find it) and generate a <a href="http://groovy.codehaus.org/">Groovy</a> program like:<br /><br /><blockquote><span style="font-size:85%;"><br /><span style="font-family:courier new;">import org.apache.commons.httpclient.HttpClient;<br /></span><span style="font-family:courier new;">import org.apache.commons.httpclient.UsernamePasswordCredentials;<br /></span><span style="font-family:courier new;">import org.apache.commons.httpclient.methods.GetMethod;<br /><br /></span><span style="font-family:courier new;">def url = "URL TO SERVICE"</span><br /><span style="font-family:courier new;">client = new HttpClient()</span><br /><span style="font-family:courier new;">get = new GetMethod(url)</span><br /><span style="font-family:courier new;">get.setRequestHeader("Accept", "text/xml")</span><br /><span style="font-family:courier new;">client.executeMethod(get)</span><br /><br /><span style="font-family:courier new;">println get.getResponseBodyAsString().toString()</span></span></blockquote><span style="font-family:courier new;"></span><span style="font-family:courier new;"></span><br /><br />It's a simple client and one can alter the ACCEPT header value easily to check and validate REST services response to various settings of accept request-header.filshttp://www.blogger.com/profile/04578829160811273052noreply@blogger.com6tag:blogger.com,1999:blog-7651912408580529938.post-45180299585303660892007-09-17T21:42:00.000-05:002007-09-18T08:49:46.639-05:00Using CHRONOS Timescale Converter Service CallsThe timescale converter web application accessible through the CHRONOS portal (Ref: <a href="http://portal.chronos.org/gridsphere/gridsphere?cid=tools_tsconvert">http://portal.chronos.org/gridsphere/gridsphere?cid=tools_tsconvert</a>) uses publicly available web service calls to the timescale service described by the WSDL document located at <a href="http://services.chronos.org:9090/axis/timescales.jws?wsdl">http://services.chronos.org:9090/axis/timescales.jws?wsdl</a>. These services are available for use on any platform.<br /><br />Simple clients to these end points can be created in any modern programming language. Examples using Python and Groovy, two popular dynamic languages, are given below. Examples using Ruby or C# would be quite similar.<br /><br />Example using Python<br /><br /><div style="margin-left: 40px;"> <span style="font-family:Courier New;"><b>#!/usr/bin/python</b></span> <span style="font-family:Courier New;"><b><br />#</b></span> <br /><span style="font-family:Courier New;"><b>import re, string</b></span> <br /><span style="font-family:Courier New;"><b>from SOAPpy import SOAPProxy</b></span> <br /><br /><span style="font-family:Courier New;"><b>server = SOAPProxy("http://services.chronos.org:9090/axis/timescales.jws")</b></span> <span style="font-family:Courier New;"><b><br />time = 20.0</b></span> <span style="font-family:Courier New;"><b><br />while time < 40.0:</b></span> <span style="font-family:Courier New;"><b><br /> result = server.convertTime("GTS 2004", "Berggren 95",time)</b></span> <span style="font-family:Courier New;"><b> <br /> print time , result</b></span> <span style="font-family:Courier New;"><b> <br /> time = time + 0.1</b></span><br /></div><br /><br />Example using Groovy<br /> <br /><div style="margin-left: 40px;"><b>import groovy.net.soap.SoapClient</b> <b><br /><br />def proxy = new SoapClient("http://services.chronos.org:9090/axis/timescales.jws?wsdl")</b> <b><br /><br />def serviceClosure = {</b><br /> <b>time -> return proxy.convertTime("GTS 2004", "Berggren 95", time)</b><br /> <b>}</b> <br /><br /><b>for (float f = 20;f< 40.0; f=f+0.1) {<br /></b> <b> println serviceClosure(f)<br />}<br /></b></div><br />A slightly more advanced example of a real-world application of these services is the implementation of the service in a Java-based web application server. In this case, a user would use e.g. the Xfire (http://xfire.codehaus.org/Client+and+Server+Stub+Generation+from+WSDL) or Apache Axis (http://ws.apache.org/axis/java/client-side-axis.html) packages to generate a library from the WSDL file. The resulting library could then be easily called from various locations in the application with only a few lines of code.<br /><br />The first step would involve the creation of the stub classes in a WSDL-to-Java process. We will use the Apache Axis package in this example but the process is similar for Xfire or C# style environments. The initial stub classes are generated through a call like:<br /><br /><div style="margin-left: 40px;"><b>java -cp axis.jar:commons-logging-1.0.4.jar:commons-discovery-0.2.jar:axis-ant.jar:log4j-1.2.8.jar:wsdl4j-1.5.1.jar:jaxrpc.jar:saaj.jar org.apache.axis.wsdl.WSDL2Java -o . -d Session -p org.chronos.ws http://services.chronos.org:9090/axis/timescales.jws?wsdl </b><br /></div><br />The result of this call is a set of Java source files that would then be compiled:<br /><br /><div style="margin-left: 40px;"><b>javac -classpath axis.jar:commons-logging-1.0.4.jar:commons-discovery-0.2.jar:axis-ant.jar:log4j-1.2.8.jar:wsdl4j-1.5.1.jar:jaxrpc.jar:saaj.jar org/chronos/ws/*.java</b><br /></div><br />and the resulting class files collected into a jar file:<br /><br /><div style="margin-left: 40px;"><b>jar -cvf timescale.jar org/chronos/ws/*.class</b><br /></div><br />The resulting jar file can then be used to greatly simplify the creation of clients in Java or any other Java byte code compatible language like Jruby, Jpython or Groovy.<br /><br />An example of Java client that gets the color scheme for the Geological Time Scale is:<br /><br /><div style="margin-left: 40px;"><b>public class wsClient {</b> <b><br /> public static void main(String [] args) throws Exception {<br /><br /></b> <b>// Make a service</b><br /> <b>org.chronos.ws.TimescalesService service = new org.chronos.ws.TimescalesServiceLocator();</b><br /><br /> <b>// Now use the service to get a stub to the service</b> <b> org.chronos.ws.Timescales_PortType ts = service.gettimescales();</b> <b><br /><br /> // Make the actual call</b> <b><br /> System.out.println("call " + ts.getColorScales());</b> <b><br /> }</b> <b><br />}</b><br /></div><br />A Groovy client that uses the jar file (here using the batch convert method for time conversion) is:<br /><div style="margin-left: 40px;"><b>import java.text.DecimalFormat</b> <b><br /><br />// Make a service</b><br /><b>def org.chronos.ws.TimescalesService service = new org.chronos.ws.TimescalesServiceLocator();</b> <b><br /><br />// Now use the service to get a stub to the service</b><br /><b>def org.chronos.ws.Timescales_PortType ts = service.gettimescales();</b> <b><br /><br />// Make the actual call</b> <b><br />def batchResults = (ts.convertTimeBatch("GTS 2004", "Berggren 95", 0.toDouble(), 60.toDouble(), 1.toDouble()));</b> <br /><br /><b>DecimalFormat df2 = new DecimalFormat( "#,###,###,##0.000" );</b> <b><br /><br />// Closure for 2 place formating</b> <b><br />def decf2 = { </b> <b><br /> value -> return new Double(df2.format(value)).doubleValue();</b> <b><br />}<br /><br /></b> <b>for (item in batchResults) {</b><br /> <b>println decf2(item)</b><br /><b>}</b> </div><br /> <br />These examples illustrate the process involved in the creation of clients using only the WSDL URL. Once created, jar libraries like those at the end of the process can be dropped into application server class paths and used in frameworks like Grails (<a href="http://www.gridsphere.org/">http://www.grails.org</a>), Seam ( <a href="http://www.jboss.com/products/seam">http://www.jboss.com/products/seam</a>) or JSR-168 portal environments like Gridsphere (<a href="http://www.gridsphere.org/">http://www.gridsphere.org</a>). Any application or tool with network access can invoke these services in a similar manner.<br /><br />All CHRONOS services a similar pattern and can be utilized in web-based or stand-alone clients that have access to the network.filshttp://www.blogger.com/profile/04578829160811273052noreply@blogger.com6tag:blogger.com,1999:blog-7651912408580529938.post-18655961057860987372007-09-04T13:57:00.000-05:002007-09-04T14:12:29.257-05:00Grails SQL date formating libBeen working with Grails more and created a little tag lib I thought I would post up for others. The date that comes out of the domains is a rather ugly SQL date style so I create a tag lib with the following code:<br /><br /><pre><span style="font-family:courier new;"> def formatSqlDate = { attrs -> <br /> def String startdatetime = "${attrs['targetDate']}"<br /> def DateFormat odf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss.S");<br /> def DateFormat df = new SimpleDateFormat( "MMM d, ''yy" );<br /> out << df.format(odf.parse(startdatetime)) <br /> }<br /></span></pre>Then I can simple reference it with:<br /><br /><code><span><span style="font-family:courier new;"><g:formatsqldate targetdate="${domain.dateObject}"> </span></span></code><br /><br />A person can mode the date format string all they want and also could put in various cases or a flag to select a format style if they wished.filshttp://www.blogger.com/profile/04578829160811273052noreply@blogger.com4tag:blogger.com,1999:blog-7651912408580529938.post-77253554443535061092007-07-27T10:06:00.000-05:002007-07-27T10:51:08.191-05:00Terminal shell in eclipse<a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg48qtDW9LHUuCf8SydW3ccY_RlYqUm7Kn4GFAQdmV2q-sv_64ao5KJ6jejOOJX_kVmd42p_7C6WEYd9Q5cAYe-8f-pv9ywXwwyx3lBwF6Yx9BwDwCM_AORGyQM44Q9Xxbe7I9JSeqOd1qG/s1600-h/Screenshot-Show+View+-1.png"><img style="margin: 0pt 0pt 10px 10px; float: right; cursor: pointer;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg48qtDW9LHUuCf8SydW3ccY_RlYqUm7Kn4GFAQdmV2q-sv_64ao5KJ6jejOOJX_kVmd42p_7C6WEYd9Q5cAYe-8f-pv9ywXwwyx3lBwF6Yx9BwDwCM_AORGyQM44Q9Xxbe7I9JSeqOd1qG/s320/Screenshot-Show+View+-1.png" alt="" id="BLOGGER_PHOTO_ID_5091899028762019362" border="0" /></a><br />The state of working with Grails in Eclipse is not the best for me. Using the external tool call has been a pain and I have been using an external terminal shell. Today I went looking for a terminal shell solution built into Eclipse and found the <a href="http://www.eclipse.org/dsdp/tm/">Target Management Project</a>.<br /><br />You can install the components for this via the Eclipse update manager with the information from: <a href="http://europa-mirror1.eclipse.org/dsdp/tm/updates/">http://europa-mirror1.eclipse.org/dsdp/tm/updates/</a>.<br /><br />Once this is done use the menu sequence Window -> Show_View -> Other and select the Remote Systems folder. From there I added "Remote System Details" (not really needed) and "Remote Shell". Using the triangle menu for this latter element I selected the default local connection.<br /><br />After all this though.. failure.. it exec's each command out to the shell and as such there is no way to re-attach and kill a process easily. If you run grails run-app it's disconnected to one has to ps its PID and kill it. (not optimal).<br /><br />After some looking I found sshView (<a href="http://www.eclipse-plugins.info/eclipse/plugin_details.jsp?id=1187">http://www.eclipse-plugins.info/eclipse/plugin_details.jsp?id=1187</a>). This installed but gave some very strange behavior and never was able to get it to successfully work. You can access it in the same sequence as above and look for sshview.<br /><br />In the end there is <a href="http://pluginbox.sourceforge.net/plugins.html">easyShell</a>. However, all this does is launch the shell I was using when I started this journey. Yes, you can configure it to open up in the directory the file is in and you can get to rather easily through the contextual menu. In the end not really worth the time and effort and still no good built into the IDE view shell solutions that I can find at least.filshttp://www.blogger.com/profile/04578829160811273052noreply@blogger.com1tag:blogger.com,1999:blog-7651912408580529938.post-4856032206254332882007-07-19T15:44:00.000-05:002007-07-19T15:51:52.311-05:00Using Grails GSP to provide alternating colors in a tableUsing Grails GSP to provide alternating colors in a table<br /><br />I saw something related to this a while ago on the net but was not able to find it again. So I just came up with my own approach.<br /><br />I wanted a table that had alternating rows highlighted. There are many ways to approach this. This solution is what I am going to use since it all falls on the view side of things and as such is rather easy to code. It is based on the code examples from <a href="http://grails.org/GSP+Tag+-+set">http://grails.org/GSP+Tag+-+set</a><br /><span style="font-size:78%;"><br /><span style="font-family:Arial Narrow;"> <table width="50%" align="right" border="0" cellpaddinng="0" cellspacing="0"></span> <span style="font-family:Arial Narrow;"> </span><span style="font-weight: bold;font-family:Arial Narrow;" ><br /> <g:def var="counter" value="${1}" /></span> <span style="font-family:Arial Narrow;"><br /> <g:each in="${lastElement}"></span> <span style="font-family:Arial Narrow;"><br /> <tr></span> <span style="font-family:Arial Narrow;"><br /> <td style="background:</span><span style="font-weight: bold;font-family:Arial Narrow;" >${counter % 2 == 0 ? 'white' : 'grey'</span><span style="font-family:Arial Narrow;">"></span> <span style="font-family:Arial Narrow;"><br /> <g:showElementResults id='${it.id}'/></span> <span style="font-family:Arial Narrow;"><br /> </td></span> <span style="font-family:Arial Narrow;"><br /> </tr></span> <span style="font-family:Arial Narrow;"> </span><span style="font-weight: bold;font-family:Arial Narrow;" ><br /><g:set var="counter" value="${counter + 1}" /></span> <span style="font-family:Arial Narrow;"><br /> </g:each></span> <span style="font-family:Arial Narrow;"><br /> </table></span></span><br /><br />It is the "<span style="font-size:85%;">${counter % 2 == 0 ? 'white' : 'grey'}</span>" that does all the work. The only other interesting elements are in bold. Simply change the two colors to get the banding effect you want. A more advanced version of this could set a style name or some other CSS element to improve the approach. I am sure there are several ways to do this. This seems to work for me. The "<g:showElementResults id='${it.id}'/>" code is not relevant here. It's just a taglib I use to create the formatted text of the cell based on an id and the rest is the just the <g:each> tag boilerplate.filshttp://www.blogger.com/profile/04578829160811273052noreply@blogger.com0tag:blogger.com,1999:blog-7651912408580529938.post-74399139919754242262007-07-12T10:20:00.000-05:002007-07-12T10:27:24.787-05:00Keyboard shortcut for grails external tool in Eclipse<a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiu9QGmon-ZiSqwte9kHj9jrIBSMlYuerIDe3SS_fADCFnbmt-UrU3kogXYHW9QelrfBk411PWyEYvA7k5o1u_NCCLTAtiphMGlK5xT5SWeDHnLUuARoMuxJPMMpK2SNml75ijZ8gG9Yixw/s1600-h/Screenshot-Preferences+.png"><img style="margin: 0pt 10px 10px 0pt; float: left; cursor: pointer;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiu9QGmon-ZiSqwte9kHj9jrIBSMlYuerIDe3SS_fADCFnbmt-UrU3kogXYHW9QelrfBk411PWyEYvA7k5o1u_NCCLTAtiphMGlK5xT5SWeDHnLUuARoMuxJPMMpK2SNml75ijZ8gG9Yixw/s320/Screenshot-Preferences+.png" alt="" id="BLOGGER_PHOTO_ID_5086331047822737122" border="0" /></a><br />Somehow I think this should have been much easier to do. Since it didn't seem to be I am placing it here.<br /><br />Using Grails in Eclipse (<a href="http://grails.codehaus.org/IDE+Integration">IDE integration information here</a>), I wanted to make a shortcut for the external tools command which is used so much in this set up.<br /><br />To do this you need to go to preferences (under the window menu), and select Keys -> Modify (Tab) -> Run/Debug (Category pull down) -> Run Last Launched External Tool (Name pull down) then assign the key in the key sequence and be sure to click the "Add" button.<br /><br />Once all that is done you have a keyboard shortcut for the external tool.<br /><br />(Please.. I'd like to see<a href="http://blogs.sun.com/geertjan/entry/grails_in_netbeans_ide"> netbeans and grails integration</a>)filshttp://www.blogger.com/profile/04578829160811273052noreply@blogger.com2tag:blogger.com,1999:blog-7651912408580529938.post-60235148150706907222007-06-26T10:14:00.000-05:002007-06-26T10:16:59.764-05:00Perm gen size and GrailsI've been continuing to work with the <a href="http://www.grails.org">Grails</a> framework and found an interesting issue. When working with the Tomcat application server I run into several issues related to out of memory errors with Grails applications. <br /><br />Attempts to resolve this with -Xmx512m or some other setting for the memory heap size failed to work. This seems to be due to the large number of elements (controllers, domains, etc) in my grails app that load classes. I believe this is related to issues of lots of use of reflection (not sure). It does appear that this memory is alive during the entire life of the application (not garbage collected). After installing several plug-ins and increasing the size of the application quite a bit this occurred. It also seems to be partly related to the creation of an additional "Context Path" for an application in Tomcat. <br /><br />I found some reference to Tomcat 6 having better "perm size" management on the net vs Tomcat 5 series. Though nothing of a definitive nature. I saw it seems related since it removing the extra "Context Path" for this grails application seemed to resolve things. Perhaps it is an issue with grails applications in multiple Context Path's but the perm gen size increase did resolve it cleanly. <br /><br />My current options line for catalina.sh:<br /><span style="font-style: italic;font-size:85%;" ><span style="font-family: courier new;"> JAVA_OPTS='-Xmx512m -XX:MaxPermSize=256m -server -Djava.awt.headless=true'</span></span>filshttp://www.blogger.com/profile/04578829160811273052noreply@blogger.com2