2009年11月18日水曜日

[Grails]ignore the excludePathPatterns parameter when using the Compress Plugin

In Grails environment, Compress Plugin ignore the excludePathPatterns option and filter doesn't work fine.
  • References
  • Description
    The Compress Plugin ignores the excludePathPatterns parameter even though I set the excludePathPatterns parameter in grails-app/conf/Config.groovy as follows, the Compress filter tries to compress for the css or pdf files. the css and pdf files should be exclude and disable the compression process for these files.
    (If you need to use the excludePathPatterns, the value of includePathPattern has to be set to null or empty.)
    includePathPatterns = []
    excludePathPatterns = [".*\\.css",".*\\.gif",".*\\.ico",".*\\.jpg",".*\\.swf",".*\\.pdf"]
  • Solution(Workaround)
    Change the plugin's source code as follows in CompressGrailsPlugin.groovy and recompile or rebuild the application. Please check the exclamation mark carefully.
    line and source code
    55 // excludePathPatterns configuration
    56 'init-param' {
    57 'param-name'("excludePathPatterns")
    58 //changed the source code from line:59 to line:60
    59 //if (compress && !compress["excludePathPatterns"] && compress["excludePathPatterns"].isEmpty()) {
    60 if (compress && compress["excludePathPatterns"] && !compress["excludePathPatterns"].isEmpty()) {
    61 'param-value'(compress["excludePathPatterns"].join(","))
    62 } else {
    63 'param-value'(".*\\.gif, .*\\.ico, .*\\.jpg, .*\\.swf")
    64 }
    65 }

[Grails]IOException occurs when calling redirect() in case of using Compress plugin

IOException occurs when calling redirect() in case of using Compress plugin in Grails environment.
  • References

  • Description
    IOException occurs and outputs if you call redirect() in case of using Compress plugin(pjl Compression filter) under the Jetty as the servlet container. If you set the debug level of the log4j option to WARN or DEBUG for the org.mortbay java package in Config.groovy, the message "java.io.IOException: Closed" outputs into the stdout or debug file.
    (Maybe this problem will not occur if you use the Tomcat or other servlet container., but i don't know the detail of this in case of using the Tomcat or other servlet container.)

  • Error Messages
    Following is a error stack when calling redirect() method.
    java.io.IOException: Closed
    at org.mortbay.jetty.AbstractGenerator$Output.write(AbstractGenerator.java:627)
    at org.mortbay.jetty.AbstractGenerator$Output.write(AbstractGenerator.java:577)
    at java.util.zip.GZIPOutputStream.finish(GZIPOutputStream.java:91)
    at com.planetj.servlet.filter.compression.CompressingStreamFactory$GZIPCompressingStreamFactory$1.finish(CompressingStreamFactory.java:369)
    at com.planetj.servlet.filter.compression.ThresholdOutputStream.close(ThresholdOutputStream.java:131)
    at com.planetj.servlet.filter.compression.CompressingServletOutputStream.close(CompressingServletOutputStream.java:92)
    at com.opensymphony.module.sitemesh.filter.RoutableServletOutputStream.close(RoutableServletOutputStream.java:46)
    at org.mortbay.jetty.servlet.Dispatcher.forward(Dispatcher.java:343)
    at org.mortbay.jetty.servlet.Dispatcher.forward(Dispatcher.java:126)
    at org.codehaus.groovy.grails.web.util.WebUtils.forwardRequestForUrlMappingInfo(WebUtils.java:293)
    at org.codehaus.groovy.grails.web.util.WebUtils.forwardRequestForUrlMappingInfo(WebUtils.java:269)
    at org.codehaus.groovy.grails.web.util.WebUtils.forwardRequestForUrlMappingInfo(WebUtils.java:261)
    at org.codehaus.groovy.grails.web.mapping.filter.UrlMappingsFilter.doFilterInternal(UrlMappingsFilter.java:181)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:76)
    ......
  • Cause
    This problem occures because the pjl Compression Filter tries to call the close() method for the OutputStream object which is already closed at the end of the response. When closing the output stream, flush() method is called before closing in org.mortbay.jetty.AbstractGenerator$Output.write object. When calling the org.mortbay.jetty.AbstractGenerator$Output.write method, System checks whether the Output object is already closed or not. If the object is already closed, JVM raises the IOException.

  • Solution(workaround)
    Before calling the redirect() method, response.getWriter().write("") calls as follows.
     def test={
    response.getWriter().write("");
    return redirect(url:"http://example.com");
    }

2009年10月23日金曜日

[Google Map V3]How to use the OverlayView's fromDivPixelToLatLng and fromLatLngToDivPixel functions(how to convert the google.maps.Point to LatLng)

How to use the OverlayView's fromDivPixelToLatLng(converter method from pixel in div to LatLng object) and fromLatLngToDivPixel(converter method from LatLng object to pixel in div field) functions.
(how to convert the google.maps.Point to LatLng) on Google Map Version 3.
This is my sample program. This program works fine in my test environment.Please let me know if I'm misunderstanding.

References
Examples
 var map=null;
var projectionHelper=null;

function initialize(){
var mapElem = document.getElementById('map');
var gmapOpts={
zoom:10
,center:new google.maps.LatLng(37.421843,-122.084026)
,mapTypeId:google.maps.MapTypeId.ROADMAP
,mapTypeControl:true
,scaleControl:true
,navigationControl:false
,draggable:true
,scrollwheel:false
}

//initialize the google map object
map=new google.maps.Map(mapElem,gmapOpts);
//initialize the OverlayView object
projectionHelper=new ProjectionHelper(map);
}

//converter from google.maps.Point object to google.maps.LatLng object
function fromPixelToLatLng(point){
if(point==null || !(point instanceof google.maps.Point)){
return null;
}
return projectionHelper.getProjection().fromDivPixelToLatLng(point);
}

//converter from google.maps.LatLng object to google.maps.Point object
function fromLatLngToPixel(latLng){
if(latLng==null || !(latLng instanceof google.maps.LatLng)){
return null;
}
return projectionHelper.getProjection().fromLatLngToDivPixel(latLng);
}

//calculate the array of the polygon's path and draw polygon ogject into the google map
//
//***** Attention *****
//This method should be called from the ProjectionHelper.prototype.draw()
//because the fromLatLngToDivPixel and fromDivPixelToLatLng functions won't work fine.
//*********************
function drawPolygons(){
//google hq's location
var googleHqLatLng=new google.maps.LatLng(37.421843,-122.084026);
//conversion the google.map.LatLng object to the google.map.Point object.
var googleHqPoint=fromLatLngToPixel(googleHqLatLng);
....
//culculates the pixel X-Y coordinates to draw the circle around the google headquarter.
var radius=5000;
var plotPoints=40;
var plotPointsPath=new Array();
for(var i=0;i < plotPoints+1;i++){
//calculate the pixel X-Y coordinates on the google map object to draw the circle
var theta=2.0*Math.PI/plotPoints*i;
var x=radius*Math.cos(theta)+googleHqPoint.x;
var y=radius*Math.sin(theta)+googleHqPoint.y;
//after calculating the pixcel x and y, convert the google.maps.Point(x,y) to the LatLng object.
var latLng=fromPixelToLatLng(new google.maps.Point(x,y));
//set the LatLng object into the Array object
plotPointsPath.push(latLng);
}

var polygonOpts={
paths:paths
}
var polygon=new google.maps.Polygon(polygonOpts);
polygon.setMap(map);
}

//initialize the OverlayView
//helper functions
function ProjectionHelper(overlayMap) {
google.maps.OverlayView.call(this);
this.set_map(overlayMap);
}

ProjectionHelper.prototype = new google.maps.OverlayView();
ProjectionHelper.prototype.draw = function () {
if (!this.ready) {
this.ready = true;

//call the draw function here.
drawPolygons();
}
}

2009年7月24日金曜日

[Grails]check list when java.lang.OutOfMemoryError: Java heap space in grails application

check list when java.lang.OutOfMemoryError: Java heap space in grails application

References
Tools
Check list
  • Check the setup for the second level hibernate cache
    Open grails-app/conf/DataSource.groovy and check the setup of the second-level and query cache. If you don't need to use the hibernate query cache, both cache.use_second_level_cache and cache.use_query_cache are set to false. And then, you need to comment out the cache.provider_class settings if you don't need to use second level cache.(In our case, if the cache provider setting is not commented out, grails caches the domain object into the EhCache internally.)
         hibernate {
    cache.use_second_level_cache=false
    cache.use_query_cache=false
    //cache.provider_class='org.hibernate.cache.EhCacheProvider'
    }
    If you use the Multiple Datasources plugin, please check hibernate cache settings for the other database sessions in the datasources file.

  • Check the history of the java heap usage and Garbage Collector
    Check whether the memory setting for the java virtual machine is enough or not.
    1. run the grails application with followng java options.
      export JAVA_OPTS=-verbose:gc -XX:+PrintClassHistogram -XX:+PrintGCDetails -Xloggc:/tmp/jvm_loggc.log -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tmp
    2. After appearing the OutOfMemory error, view the /tmp/jvm_loggc.log.
      If you use the GCViewer(http://www.tagtraum.com/gcviewer.html), you can check the history of running the garbage collector and usage of the java heap size easily.

  • Check and analyze the heap dump file.
    After occuring the OutOfMemory Exception, Java VM outputs the HeapDump file under the HeapDumpPath directory, the file name is java_pidXXXXX.hprof. You can check and analyze the usage of the java heap for each java objects and view the description when the OutOfMemory error occures.
    If you use the Memory Analyzer(http://www.eclipse.org/mat/) tool, you can view the information about the Heap Memory Dump file visually and easily.
    Memory Analyzer reports the Leak Suspect Report, you can see the output of the "Leak Suspect Report" analyzed by the Memory Analyzer. Memory analyzer reports the overview of the memory usage and problem suspect.

    Check the memory leak in your program based on the output of the Memory Leak Suspect Report. If you cache the objects pointer in your program, garbage collector never clear and garbage the heap memory.
Problems
  • Memory usage of the org.hibernate.engine.StatefulPersistenceContext" object
    If the Problem Suspect in Leak Suspect Report says that the memory usage of the org.hibernate.engine.StatefulPersistenceContext" object is accumulated, this object depends on the Hibernate's First-level cache, and maybe we can't turn off the hibernate first-level cache functionality in grails environment.(In standalone hibernate env, we can find the StatelessSession functionality, but I don't know how to use this in grails.)
    In case of this, I can find the 2 solutions to avoid increasing the memory usage of the hibernate first level cache.
    1. Set the session's AutoClear flag to true.
      When starting the application, this flag is set to true, cached objects in session are cleared after the end of the transaction. But the cached objects aren't always cleard out.
          Session session=sessionFactory.getCurrentSession();
      if((session instanceof SessionImpl)){
      ((SessionImpl)session).setAutoClear(true);
      }
    2. Call the clear() method of the session
      We often call the clear() method of the session object in our program and release the cached objects in first-level cache manually.
           Session session=sessionFactory.getCurrentSession();
      session.clear();

2009年7月3日金曜日

[Java]how to use the gzipped http response by using the apache http-client ver.3.x

how to use the gzipped http response by using the apache http-client ver.3.x

  • References
  • Example
    //set url
    String url="http://hc.apache.org/httpclient-3.x/";

    //initialize apache HttpClient object and HttpMethod
    HttpClient httpClient = new HttpClient();
    HttpMethod httpMethod = new GetMethod(url);
    //set followRedirects function to true if you need.
    httpMethod.setFollowRedirects(true);
    //set Accept-Encoding request header
    httpMethod.setRequestHeader("Accept-Encoding","gzip");

    try{
    //access to the url and get response status
    int status = httpClient.executeMethod(http_method);

    //check response status. if the value of response status is set to 200, get body stream
    if(status == 200){
    //check the response header "Content-Encoding". the value of Content-Encoding header contains the
    //"gzip" value, this means the response stream is gzipped.
    Header contentEncodingHeader=httpMethod.getResponseHeader("Content-Encoding");
    String contentEncoding = contentEncodingHeader!=null ? contentEncodingHeader.getValue() : "";
    String contentEncodingLowerCase=contentEncoding.toLowerCase();
    boolean isGzipped=(contentEncodingLowerCase.indexOf("gzip")>=0);

    //get response stream
    InputStream stream=httpMethod.getResponseBodyAsStream();

    InputStream bodyStream=null;
    ByteArrayOutputStream outStream=null;
    try{
    //if the response stream is gzipped, derived stream is converted into the GZIPInputStream.
    //the response stream is not gzipped, set the stream without conversion
    bodyStream = isGzipped ? new GZIPInputStream(stream) : stream;

    //change the response from InputStream to Byte Array.
    outStream=new ByteArrayOutputStream();
    byte[] buffer = new byte[4096];
    int length;
    while((length=bodyStream.read(buffer))>0){
    outStream.write(buffer,0,length);
    }

    //get the response charset.
    String charset=httpMethod.getResponseCharSet();
    //convert the response byte array to the String object.
    String body=new String(outStream.toByteArray(),charset);

    //Instead of converting the InputStream to Byte array,
    //we can convert the inputstream to String by using the InputStreamReader and BufferedReader directly.
    //But we can't read the responsed InputStream twice.
    //for example.
    //InputStreamReader bodyReader=new InputStreamReader(bodyStream,charset);
    //BufferedReader bodyBufferedReader=new BufferedReader(bodyReader);
    //String line=bodyBufferedReader.readLine();
    //while(line!=null){
    // bodyBuffer.append(line);
    // line=bodyBufferedReader.readLine();
    //}
    //String body=bodyBuffer.toString();
    }catch(Exception e1){
    throw e1;
    }finally{
    //close ByteArrayOutputStream
    if(outStream!=null){
    try{
    outStream.close();
    }catch(Exception ignore){}
    }

    //close InputStream
    if(bodyStream!=null){
    try{
    bodyStream.close();
    }catch(Exception ignore){}
    }
    if(stream!=null){
    try{
    stream.close();
    }catch(Exception ignore){}
    }
    }
    }
    }catch(Exception e0){
    System.out.println("Error, "+e0);
    }finally{
    httpMethod.releaseConnection();
    }


2009年5月12日火曜日

[Java>Rome]Rome plugin module for twitter search RSS

Rome plugin module for twitter search RSS(Beta version 0.1)
  • References

  • Description
    the rss output of the twitter search is including the original elements like the twitter:source and twitter:lang tags. These tags are extended by the Twitter Search, so we need to use new plugin module to get the value of these tags by using the rome rss library.
  • Setups
    • Dwonload Jar file
      http://groups.google.com/group/taapps-sourcecode-libraries/web/tskr-twitter-rss-0.1-b1.jar
    • Put the downloaded jar file into the directory
    • Restart the application

  • Sample Program
    //import
    import com.sun.syndication.fetcher.FeedFetcher;
    import com.sun.syndication.fetcher.FetcherException;
    import com.sun.syndication.fetcher.impl.HttpURLFeedFetcher;
    import com.sun.syndication.feed.synd.SyndEntry;
    import com.sun.syndication.feed.synd.SyndFeed;
    import com.sun.syndication.io.FeedException;
    import java.io.IOException;
    import java.net.MalformedURLException;
    import java.net.URL;
    import java.util.List;

    //import rome module
    import com.sun.syndication.feed.module.Module;
    //import plugin new module
    import jp.tskr.feed.module.twitter.Twitter;

    public class TwSample{
    public static void main(String[] args){
    FeedFetcher feedFetcher=new HttpURLFeedFetcher();
    try{
    String urlStr="http://search.twitter.com/search.atom?q=iphone";
    URL feedUrl=new URL(urlStr);
    SyndFeed feedFetch=feedFetcher.retrieveFeed(feedUrl);

    for(SyndEntry entry : (List) feedFetch.getEntries()){
    System.out.format("\tUpdate:[%s] URL:[%s] Title:[%s]\n",entry.getPublishedDate(),entry.getLink(),entry.getTitle());

    //calling twitter search module
    Module module=entry.getModule(Twitter.URI);
    //checking the module object whether the object is instanceof Twitter class or not
    if(module instanceof Twitter){
    Twitter twModule=(Twitter)module;
    //get the value in the twitter search rss field
    String source=twModule.getSource();
    //get the value in the twitter search rss field
    String lang=twModule.getLang();
    }
    }
    }catch(Exception e){
    //.....
    }
    }
    }

2009年4月28日火曜日

[rome-fetcher]how to get rss data from the basic authenticated web site

Steps to get rss data from the basic authenticated web site like the twitter.

  1. Create a new class implemented the CredentialSupplier interface. this class stores the username and password for the basic authentication and returns the Credentials object of the apache http client. following code is a sample class implemented the CredentialSupplier interface
    import com.sun.syndication.fetcher.impl.HttpClientFeedFetcher.CredentialSupplier;
    import org.apache.commons.httpclient.UsernamePasswordCredentials;
    import org.apache.commons.httpclient.Credentials;
    public class AuthCredentialSupplier implements CredentialSupplier{
    private String username=null;
    public void setUsername(String username){
    this.username=username;
    }
    public String getUsername(){
    return this.username;
    }

    private String password=null;
    public void setPassword(String password){
    this.password=password;
    }
    public String getPassword(){
    return this.password;
    }

    public AuthCredentialSupplier(){
    }
    public AuthCredentialSupplier(String username,String password){
    setUsername(username);
    setPassword(password);
    }
    public Credentials getCredentials(String realm, String host){
    String username=getUsername();
    String password=getPassword();
    return new UsernamePasswordCredentials(username,password);
    }
    }
  2. Access to the rss url and get rss data from the basic authenticated web site. When accessing to the rss url, we need to use the HttpClientFeedFetcher instead of HttpURLFeedFetcher.
     //sample program
    //rss url
    String url="http://twitter.com/statuses/friends_timeline.atom";

    //username and password
    String username="username";
    String password="password";

    try{
    //create and initialize CredentialSupplier Object
    AuthCredentialSupplier authCredentials=new AuthCredentialSupplier(username,password);
    //create HttpClientFeedFetcher object
    //(we can not use HttpURLFeedFetcher with basic authentication)
    FeedFetcher feedFetcher=new HttpClientFeedFetcher(null,authCredentials);
    List result=(feedFetcher).retrieveFeed(new URL(url)).getEntries();
    //get response.
    if(result!=null){
    .....
    }
    else{
    System.out.println("ERROR")
    }
    }catch(FetcherException e){
    int responseCode=e.getResponseCode();
    System.out.println("ERROR, response code="+responseCode+", error="+e);
    }catch(Exception e){
    System.out.println("Unexpected Exception, e="+e)
    }

2009年4月24日金曜日

[Grails]Grails1.1 Setup fo the Apache Log4j Logging

Sample Setup fo the Apache Log4j Logging on Grails 1.1, A new Log4j DSL is available on Grails 1.1.
  • References
  • Sample Setup in grails-app/conf/Config.goovy file
    This sample setup is modified based on the document(http://d.hatena.ne.jp/nobeans/20090323/1237826907, this documentation is written in Japanese).
    // log4 setup
    log4j = {
    appenders {
    //override the setuf of the default console out
    console(name:'stdout', layout:pattern(conversionPattern: '%d{HH:mm:ss} [%p] (%c{2}) %m%n'))

    //override the setup of the default log
    rollingFile(name:'file', file:'logs/debug.log', maxFileSize:'10MB', maxBackupIndex:5, layout:pattern(conversionPattern: '%d{HH:mm:ss} [%p] (%c{2}) %m%n'))

    //override the setup of the default error stack
    rollingFile(name:'stacktrace', file:'logs/stacktrace.log', maxFileSize:'10MB', maxBackupIndex:5, layout:pattern(conversionPattern: '%d{yyyy-MM-dd HH:mm:ss} [%p] (%c{2}) %m%n'))

    //daily rolling log
    appender new org.apache.log4j.DailyRollingFileAppender(name:'dailyRollingFile', datePattern:"'.'yyyy-MM-dd",layout:pattern(conversionPattern: '%d{HH:mm:ss} [%p] (%c{2}) %m%n'), file:'logs/daily.log');
    }

    root {
    error 'stdout', 'file'
    additivity = false
    }

    //controller
    error 'org.codehaus.groovy.grails.web.servlet'
    //gsp
    error 'org.codehaus.groovy.grails.web.pages'
    //layouts
    error 'org.codehaus.groovy.grails.web.sitemesh'
    //url mapping filter
    error 'org.codehaus.groovy.grails."web.mapping.filter'
    //url mapping
    error 'org.codehaus.groovy.grails."web.mapping'
    //core, classloader
    error 'org.codehaus.groovy.grails.commons'
    //plugins
    error 'org.codehaus.groovy.grails.plugins'
    //hibernate integration
    error 'org.codehaus.groovy.grails.orm.hibernate'
    error 'org.springframework'
    //info 'org.springframework.security'
    //hibernate
    error 'org.hibernate'

    //jetty
    warn 'org.mortbay.log'

    //error stack
    error(
    additivity:false
    //,stdout:"StackTrace"
    ,stacktrace:"StackTrace"
    )

    //debug for my my app
    //info dailyRollingFile:"grails.app.controller.TestController"
    info(
    additivity:false
    //,stdout:"grails.app.controller"
    ,dailyRollingFile:"grails.app.controller"
    )
    info(
    additivity:false
    ,dailyRollingFile:"grails.app.service"
    )
    info(
    additivity:true
    ,dailyRollingFile:"grails.app.task"
    )
    }



2009年4月17日金曜日

[Grails]Grails Upgrade Steps from 1.0.x to 1.1

Grails Upgrade Steps from 1.0.x to 1.1
  • References
  • Upgrade Steps
    • Backup all application files
    • Add following statements into the grails-app/conf/Config.groovy file
         //upgrade
      grails.views.enable.jsessionid=false
      //grails.project.plugins.dir="./plugins"
    • Create a file grails-app/conf/BuildConfig.groovy file if this file doesn't exist and add following statements into this file.
       //upgrade
      //grails.views.enable.jsessionid=false
      grails.project.plugins.dir="./plugins"
    • if you use some plugins, you need to re-install plugins.
      Remove all plugins files and cleanup files stored under plugins directory
         mv plugins/* /tmp/
    • Run the upgrade command as follows
      (After running this commands, grails will create a new plugin named hibernate-1.1 under the plugins directory automatically. we don't need to re-install this hibernate-1.1 plugin.)
      grails upgrade
    • re-install plugins by using "grails install-plugins" command
      (for example)
      grails install-plugin /tmp/grails-quartz-0.3.1.zip
  • TroubleShooting
  • Plugins TroubleShooting
    • quartz-0.3.1 plugin
      • Error Description
        After starting the grails application, following error appears while starting up the quartz plugin
      • Error Stack
        2009-04-14 16:12:39,907 [main] ERROR context.ContextLoader  - Context initialization failed
        org.springframework.beans.factory.access.BootstrapException: Error executing bootstraps; nested exception is org.codehaus.groovy.runtime.InvokerInvocationException: groovy.lang.MissingPropertyException: No such property: startDelay for class: QuartzGrailsPlugin
        at org.codehaus.groovy.grails.web.context.GrailsContextLoader.createWebApplicationContext(GrailsContextLoader.java:74)
        at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:199)
        at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:45)
      • Cause
        This error raises from following statements of the configureJobBeans closure in the QuartzGrailsPlugin.groovy file because of the "this" clause.
              // registering triggers
        jobClass.triggers.each {name, trigger ->
        "${name}Trigger"(trigger.clazz) {
        jobDetail = ref("${fullName}JobDetail")
        trigger.properties.findAll {it.key != 'clazz'}.each {
        this["${it.key}"] = it.value /* here */
        }
        }
        }
      • Resolution
        I changed this clause from "this" to "delegate", quartz plugin works fine.

2009年4月7日火曜日

[grails]upgraded the plugin for grails to execute native query

I created and upgraded the grails plugin named native-query(Version 0.2) to execute native query statement like ORACLE's query hint, mysql match-against statement on the grails framework.

Installation Steps
  1. download zip file named grails-native-query-0.2.zip via http://groups.google.com/group/taapps-sourcecode-libraries/web/grails-native-query-0.2.zip
  2. installing plugin into the grails application
    grails install-plugin grails-native-query-0.2.zip
How to use
  • use DomainObject.executeJdbcQuery method injected into the Domain Class
    • Description
      This executeJdbcQuery method is static method injected into the Domain Class object.
    • Return Value
      This method returns the List object and each objects stored into the List are Domain Objects of the fetched rows.
    • Arguments
      1. Required String type, where, order by, match against clauses(No need to enter the "select * from " statement)
      2. Optional String type, select option like the "SQL_CALC_FOUND_ROWS(mysql)" or hint text(ORACLE)
      3. List object or Map object stored the bind values
    • Example
      Following examples are using the Domain Class named "Test", this is a test domain class.
      • use the standard select statement
        def result=Test.executeJdbcQueryMap("where id=160")
      • use positonal parameter
        def result=Test.executeJdbcQueryMap("where id=?",[160])
      • use named map parameter
        def result=Test.executeJdbcQueryMap("where id=:id",[id:160])
      • use the positonal parameter wit
        def result=Test.executeJdbcQueryMap("where id=?","SQL_CALC_FOUND_ROWS",[160])
      • use the List object as the member of the positional parameters
        def result=Test.executeJdbcQueryMap("where id in (?)","SQL_CALC_FOUND_ROWS",[[160,161,162,163,164]])

  • use NativeQueryUtil.executeJdbcQueryMap method to execute free sql statement(not need to be related to the Domain Class)
    • Return Value
      This method returns the List object and each objects stored into the List are Map object stored the fetched column name and the value, map key is column name
    • Arguments
      1. String type, Select statement
      2. List object or Map object stored the bind values
    • Example
      • use the standard select statement
        def result=NativeQueryUtil.executeJdbcQueryMap("select * from test where id=103")
      • use positional map
        def result=NativeQueryUtil.executeJdbcQueryMap("select * from test where id=?",[103])
      • use namedmap parameter
        def result=NativeQueryUtil.executeJdbcQueryMap("select * from test where id=:id",[id:103])
      • use the List object as the member of the positional parameters
        def result=NativeQueryUtil.executeJdbcQueryMap("select * from test where id in (?)",[[103.104,105,106]])
  • use the NativeQueryUtil.executeNativeQuery method(execute sql statement via Hibernate's createQuery method)
    • Description
      This method executes the select statement via Hibernate's session.createQuery method.
    • Return Value
      Domain Object or List object stored the domain objects of the fetched rows.
    • Arguments
      1. String Type, select statement
      2. Map type, mapping the table name and domain class
      3. List object or Map object stored the bind values
    • Example
      • def result=NativeQueryUtil.executeNativeQuery("select /* query */ tbl.* from test tbl where id=?",[tbl:Test],[101])


2009年4月4日土曜日

[Grails]Upgraded Split Write Database Connection Plugin for master/slave configuration(Alpha version 0.3)

Upgraded the Grails Split Write Database Connection Plugin for master-slave configuration (Alpha version 0.3, like the magic multi connection for rails framework, multiple datasource)
I fixed some bugs and added new methods, changed the source code as follows.

Version 0.3
  • change the flush mode for the base session to avoid saving the changed domain class automatically into the base database. The flush mode for the base session is changed to the manual.
  • add new methods named discardXXX, attachXXX, refreshXXX for the save session.
  • added new CustomSavePersistentMethod and use this class when saving
  • added new method validateAndDiscard and discardAll which sync between the base session and save session.
Download
http://groups.google.com/group/taapps-sourcecode-libraries/web/grails-split-write-db-0.3.zip

2009年3月19日木曜日

[Grails]How to get the database table and column names mapped to the Domain Class

How to get the database table and column names mapped to the Domain Class
  • References
    • Grails FAQ "Q: How can I flush a Hibernate session multiple times within my controller?"
      http://grails.org/FAQ
    • http://www.mail-archive.com/hibernate-devel@lists.sourceforge.net/msg06401.html

  • Description
    the way to get the database table name and column names mapped to the domain classes
    in grails application.
    The example using in controller class is as follows. Following sample is using the controller class named TestController and domain class named SampleDomainClass.
    //import 3 classes
    import org.hibernate.SessionFactory
    import org.codehaus.groovy.grails.commons.DefaultGrailsApplication
    import org.codehaus.groovy.grails.commons.DefaultGrailsDomainClass

    class TestController {
    .....
    //variables to handle the Hibernate sessonFactory and grails application
    //please refer to the grails FAQ page if you know more details to get sessonFactory and grailsApplication objects
    def sessionFactory
    def grailsApplication

    //starting the action closure named "index"
    def index={
    .....

    //get the class object named the SampleDomainClass domain class
    def domainClass=grailsApplication.getClassForName("SampleDomainClass")
    //get hibernage meta data object
    def hibernateMetaClass=sessionFactory.getClassMetadata(domainClass)
    //get the table name mapped to the SampleDomainClass domain class
    def tableName=hibernateMetaClass.getTableName()
    println("table name=$tableName")

    //creaate a new GrailsDomainClass object for the SampleDomainClass
    def grailsDomainClass=new DefaultGrailsDomainClass(domainClass)
    //get the domain properties which keeps the domain class properties defined in Domain Class
    //grailsDomainClass.getProperties() is returned the GrailsDomainClassProperty[] objects
    //please refer to the javadoc
    //http://www.grails.org/doc/1.0.x/api/index.html?org/codehaus/groovy/grails/commons/DefaultGrailsDomainClass.html
    def domainProps=grailsDomainClass.getProperties()
    domainProps.each{prop->
    //get the property's name
    def propName=prop.getName()
    //get the database column name mapped to the domain property name
    //getPropertyColumnNames is returned the String array object
    //please refer to the hibernate javadoc
    //http://www.hibernate.org/hib_docs/v3/api/org/hibernate/persister/entity/AbstractEntityPersister.html
    def columnProps=hibernateMetaClass.getPropertyColumnNames(propName)
    if(columnProps && columnProps.length>0){
    //get the column name, which is stored into the first array
    def columnName=columnProps[0]
    println("prop=$propName, columnName=$columnName")
    }
    }
    .....
    }
    }

2009年2月7日土曜日

[Grails]can't override the methods(like save(), find()...) defined in domain class

References
  • Grails Documentation
    http://grails.org/doc/1.0.x/
Description
  • Can't override the standard methods (like the save(), find(), delete()..) and dynamic finders defined in domain class. For example, I define the original save() method into the domain class and start the grails jetty, System ignores my original save() method and overrides the save() method.
Cause
  • According to the doWithDynamicMethods() method defined in HibernateGrailsPlugin.groovy file, the Clusure named "lazyInit" is defined, and this closure is called from the methodMissing methods.
    When calling the methodMissing methods, System tries to initialize the domain class and inject various dynamic methods into the domain class.
    When System initializes the Domain Class and calls the dynamic method at the first time,
    Groovy calls the methodMissing method if System can't find the method definition in the domain class. And then, System injects and overrides the pre-defined and duplicated methods.
    Please refer to the org.codehaus.groovy.grails.plugins.orm.hibernate.HibernateGrailsPlugin in source code.
Resolution
  • Change the method name like save()==>saveXXX(), we have to avoid to duplicate the method name(or change and override the methodMissing method definition?).

2009年2月6日金曜日

[Grails]NullPointerException sometimes occurs after upgrading the grails to the version 1.0.4

References
  • bug information
    http://jira.codehaus.org/browse/GRAILS-3493
  • Content Negotiation
    http://www.ibm.com/developerworks/java/library/j-grails09168/?S_TACT=105AGX90&S_CMP=EDU
Description
  • Following exception sometimes occurs after upgrading the grails to the version 1.0.4.
    After starting up the grails jetty server, following exception occurs when accessing the application url. This exception occurs before executing the controller's beforeInterceptor() and any action method, this error is outputted by the grails HttpHeader parser program.
    java.lang.NullPointerException: Cannot invoke method toBigDecimal() on null object
    at org.codehaus.groovy.runtime.NullObject.invokeMethod(NullObject.java:77)
    at org.codehaus.groovy.runtime.InvokerHelper.invokePogoMethod(InvokerHelper.java:784)
    at org.codehaus.groovy.runtime.InvokerHelper.invokeMethod(InvokerHelper.java:758)
    at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.invokeMethodN(ScriptBytecodeAdapter.java:170)
    at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.invokeMethod0(ScriptBytecodeAdapter.java:198)
    at org.codehaus.groovy.grails.web.mime.QualityComparator.compare(DefaultAcceptHeaderParser.groovy:122)
    at java.util.Arrays.mergeSort(Arrays.java:1284)
    at java.util.Arrays.sort(Arrays.java:1223)
    at java.util.Collections.sort(Collections.java:159)
    at org.codehaus.groovy.runtime.DefaultGroovyMethods.sort(DefaultGroovyMethods.java:3509)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:585)
    at org.codehaus.groovy.runtime.metaclass.ReflectionMetaMethod.invoke(ReflectionMetaMethod.java:51)
    at org.codehaus.groovy.runtime.metaclass.NewInstanceMetaMethod.invoke(NewInstanceMetaMethod.java:54)
    .......
Cause
  • This problem is caused by the Http Content Negotiation logic and the values of http headers. If the user accesses the url, the browser is sending the http headers and values of the client information as follows
    (This output is sample http headers)
       accept: text/html,application/xhtml+xml,application/xml;q=0.9,/;q=0.8
    accept-language: en-us,en;q=0.5
    accept-encoding: gzip,deflate
    accept-charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
    keep-alive: 300
    connection: keep-alive
    Grails reads these http headers before executing the controller action, and DefaultAcceptHeaderParser program parses the http headers sent from the client. When the value of "accept" or "content type" http header is including the value "application/xhtml+xml" or "application/xml", Grails parses these http headers and tries to find the "q" parameter into the "accept" or "content-type" header.
    While parsing the headers, the NullPointerException occurs because some client program doesn't have the "q" parameter value into the http header even though the accept header includes the value "application/xhtml+xml" or "application/xml".
Resolution
  • According to the http://jira.codehaus.org/browse/GRAILS-3493, this problem is fixed in grails 1.1.beta1. I tried to the grails 1.1.beta3, this problem is fixed.(but other error occurs.)

2009年1月30日金曜日

[Grails]Exception occurs when using the grails amazon s3 plugin with mysql database server

References
  • Grails Amazon s3 plugin
    http://docs.codehaus.org/display/GRAILS/Amazon+S3+Plugin
  • Mysql Reserved Words
    http://dev.mysql.com/doc/refman/5.1/en/reserved-words.html
Description
  • Following exception occurs when using the grails amazon s3 plugin with mysql database server.
    [19016] core.JobRunShell Job GRAILS_JOBS.S3SyncNewJob threw an unhandled Exception:
    org.springframework.scheduling.quartz.JobMethodInvocationFailedException: Invocation of method 'execute' on target class [class S3SyncNewJob] failed; nested exception is org.springframework.dao.InvalidDataAccessResourceUsageException: could not execute query; nested exception is org.hibernate.exception.SQLGrammarException: could not execute query
    at org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean$MethodInvokingJob.executeInternal(MethodInvokingJobDetailFactoryBean.java:269)
    at org.springframework.scheduling.quartz.QuartzJobBean.execute(QuartzJobBean.java:86)
    at org.quartz.core.JobRunShell.run(JobRunShell.java:202)
    at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:529)
    Caused by: org.springframework.dao.InvalidDataAccessResourceUsageException: could not execute query; nested exception is org.hibernate.exception.SQLGrammarException: could not execute query
    at org.springframework.orm.hibernate3.SessionFactoryUtils.convertHibernateAccessException(SessionFactoryUtils.java:630)
    at org.springframework.orm.hibernate3.HibernateAccessor.convertHibernateAccessException(HibernateAccessor.java:412)
    ......
    Caused by: org.hibernate.exception.SQLGrammarException: could not execute query
    at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:67)
    at org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:43)
    at org.hibernate.loader.Loader.doList(Loader.java:2216)
    at org.hibernate.loader.Loader.listIgnoreQueryCache(Loader.java:2104)
    at org.hibernate.loader.Loader.list(Loader.java:2099)
    at org.hibernate.loader.criteria.CriteriaLoader.list(CriteriaLoader.java:94)
    at org.hibernate.impl.SessionImpl.list(SessionImpl.java:1569)
    at org.hibernate.impl.CriteriaImpl.list(CriteriaImpl.java:283)
    at org.codehaus.groovy.grails.orm.hibernate.metaclass.FindAllByPersistentMethod$1.doInHibernate(FindAllByPersistentMethod.java:78)
    at org.springframework.orm.hibernate3.HibernateTemplate.doExecute(HibernateTemplate.java:419)
    ... 83 more
    Caused by: java.sql.SQLException: Table 'test.s3asset' doesn't exist
    at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:2928)
    at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:1571)
    at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:1666)
    at com.mysql.jdbc.Connection.execSQL(Connection.java:2994)
    at com.mysql.jdbc.PreparedStatement.executeInternal(PreparedStatement.java:936)
    at com.mysql.jdbc.PreparedStatement.executeQuery(PreparedStatement.java:1030)
    at org.apache.commons.dbcp.DelegatingPreparedStatement.executeQuery(DelegatingPreparedStatement.java:92)
    at org.hibernate.jdbc.AbstractBatcher.getResultSet(AbstractBatcher.java:186)
    at org.hibernate.loader.Loader.getResultSet(Loader.java:1787)
    at org.hibernate.loader.Loader.doQuery(Loader.java:674)
    at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:236)
    at org.hibernate.loader.Loader.doList(Loader.java:2213)
    ... 90 more
Cause
  • This error occurs because the column name in Domain class and Service classes of Amazon s3 plugin is including the Mysql's reserved keyword. After installing the amazon s3 plugin, the file named "S3Asset.groovy" stored under the plugins/amazon-s3-0.1/grails-app/domain/ directory is including the "key" keyword, which keyword is a reserved word of mysql database. Grails will try to create a new table named "s3_asset" into the mysql database, "create table s3_asset ...." statement is errored out because of the syntax error,
    and then System executes the select statement fo s3_asset, grails errors out because the syntax error.
Resolution
  • Change the column name named "key" defined in domain class and service classes.
    I put the sample fixed source code, access to the http://groups.google.com/group/taapps-sourcecode-libraries and download the file grails-amazon-s3-0.1.zip.
    (This fixed source code is also changed the dependency version of the quartz plugin from 0.2 to 0.3.1.)