Monday, October 24, 2011

CSS word-wrap in table

When

word-wrap: break-word;

is applied to table cell, it does seems to wrap anything.
If you want to make it work, just add

table-layout:fixed;

to table definition and it works nicely.

Friday, October 21, 2011

HTTPS without certificate validation check

By default Java validates certificates when you do HTTPS queries. If it is not needed (for test environments, for example), it is possible to disable it (even it is much more complicated then it should be). Just add this code somewhere during initialization (this is groovy, so sorry some formatting):


  import javax.net.ssl.*
  import java.security.cert.*

  javax.net.ssl.TrustManager tm = new javax.net.ssl.X509TrustManager() {
    public boolean isClientTrusted(X509Certificate[] chain) { return true; }
    public boolean isHostTrusted(X509Certificate[] chain) { return true; }
    public boolean isServerTrusted(X509Certificate[] chain) { return true; }
    public X509Certificate[] getAcceptedIssuers() { return null; }
    public void checkClientTrusted(X509Certificate[] chain, String s){  }
    public void checkServerTrusted(X509Certificate[] chain, String s) {  }
  }
  HostnameVerifier hv = new HostnameVerifier() {
        public boolean verify(String urlHostName, SSLSession session) { return true; }
  }
  TrustManager[] trustAllCerts = new TrustManager[1];
  trustAllCerts[0] = tm;
  SSLContext sc = SSLContext.getInstance("SSL");
  sc.init(null, trustAllCerts, new java.security.SecureRandom());
  HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
  HttpsURLConnection.setDefaultHostnameVerifier(hv);



Wednesday, October 19, 2011

JUnit report formatting in Grails

Sometimes it is needed to add some additional information to JUnit report. For example, if you want to add screenshots and so on. It can be easily done in Grails.
First, you need to modify scripts/_Events.groovy and redefine reporting template folder.

eventAllTestsStart = {
  junitReportStyleDir = "src/templates/reports/"
}

Then create src/templates/reports folder and just add junit-frames.xsl and junit-noframes.xsl from lib folder under Grails home.

Now Grails will use these files to format test results.

Renaming Session cookie in JBoss and Tomcat

JSESSIONID is standard defined name, but in some cases it is needed to rename it. In JBoss and Tomcat it is very easy, you can do it by defining system property org.apache.catalina.JSESSIONID in JAVA_OPTS, for example:

-Dorg.apache.catalina.JSESSIONID=PC1SESSIONID

Friday, October 14, 2011

Grails with custom Lucene configuration for each environment

Sometimes it is needed to deal with non-grails configuration in Grails project, and usually there is problem if you need to use different configuration for different environment.
For example, recently I had to configure Lucene with compass.cfg.xml. And in production it is using MySQL dialect, and in test environment H2.
So in this case you can hook into Grails and adjust configuration for specific environment via events in scripts/_Events.groovy. For example, in my case I was just deleting custom configuration and using RAM store in test environment, like:

eventPackagingEnd = { msg -> 
  def file = new File("${grailsSettings.resourcesDir}/compass.cfg.xml")
  file.delete()
}

Of course, in different situations you can use different approach and for example, replace it with another file and so on.

InvalidClassException: GrailsUser and plugin upgrade

Recently when I tried to deploy new version of application on live server without downtime, I got error:

2011-10-14 11:02:52,058 [Tribes-Task-Receiver-4] ERROR org.apache.catalina.ha.session.DeltaManager- Manager [localhost#]: Unable to receive message through TCP channel
java.io.InvalidClassException: org.codehaus.groovy.grails.plugins.springsecurity.GrailsUser; local class incompatible: stream classdesc serialVersionUID = -3114204362518930756, local class serialVersionUID =
        at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:579)
        at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1600)
        at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1513)
        at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1600)
        at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1513)
        at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1749)
        at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1346)
        at java.io.ObjectInputStream.defaultReadFields(ObjectInputStream.java:1963)
        at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1887)
        at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1770)
        at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1346)
        at java.io.ObjectInputStream.defaultReadFields(ObjectInputStream.java:1963)

Basically, error is trivial, just some Spring security classes are not versioned and cluster can't synchronize users data. Fortunately, as I have found out, no hacking is needed and it can be easily fixed by upgrading spring-security-core plugin to the latest version.

To upgrade to latest version, just uninstall plugin and install it again, like:

grails uninstall-plugin spring-security-core
grails install-plugin spring-security-core

Wednesday, October 12, 2011

Using properties from Grails script

If you have custom Grails script and need to access property file from it, I found that easiest way to do it is to use Ant. There is complete example how to access file and to use variables:
ant.property(file:'user.properties')
ant.scp(file:'target/app.war', todir:"${ant.project.properties.login}:${ant.project.properties.password}@server:/home/tomcat/", trust:true)

Sudo without password prompt

Remote scripts often do something that requires root rights, common approach is to use sudo. But by default it interactively prompts for password, which is not easy thing if you execute commands automatically. Fortunately, it can be easily configured not to do it. All that is needed to do is to update /etc/sudoers, and change something like this:
testuser    ALL=(ALL) ALL
to something like this:
testuser    ALL=(ALL) NOPASSWD: ALL

Quartz error: Parameter index out of range

Recently, I had to configure Quartz in cluster. Most common approach to do it is to use database, there is a lot of tutorials and examples, but whatever I was using I bumped into this exception:
2011-10-12 10:44:44,861 [main] ERROR context.GrailsContextLoader  - Error executing bootstraps: org.quartz.JobPersistenceException: Couldn't store trigger 'mySimpleTrigger' for 'TestUpdaterJob' job:Parameter index out of range (2 > number of parameters, which is 1). [See nested exception: java.sql.SQLException: Parameter index out of range (2 > number of parameters, which is 1).]
org.codehaus.groovy.runtime.InvokerInvocationException: org.quartz.JobPersistenceException: Couldn't store trigger 'mySimpleTrigger' for 'TestUpdaterJob' job:Parameter index out of range (2 > number of parameters, which is 1). [See nested exception: java.sql.SQLException: Parameter index out of range (2 > number of parameters, which is 1).]
    at org.grails.tomcat.TomcatServer.start(TomcatServer.groovy:212)
    at grails.web.container.EmbeddableServer$start.call(Unknown Source)
    at _GrailsRun_groovy$_run_closure5_closure12.doCall(_GrailsRun_groovy:158)
    at _GrailsRun_groovy$_run_closure5_closure12.doCall(_GrailsRun_groovy)
    at _GrailsSettings_groovy$_run_closure10.doCall(_GrailsSettings_groovy:280)
    at _GrailsSettings_groovy$_run_closure10.call(_GrailsSettings_groovy)
    at _GrailsRun_groovy$_run_closure5.doCall(_GrailsRun_groovy:149)
    at _GrailsRun_groovy$_run_closure5.call(_GrailsRun_groovy)
    at _GrailsRun_groovy.runInline(_GrailsRun_groovy:116)
    at _GrailsRun_groovy.this$4$runInline(_GrailsRun_groovy)
    at _GrailsRun_groovy$_run_closure1.doCall(_GrailsRun_groovy:59)
    at RunApp$_run_closure1.doCall(RunApp.groovy:33)
    at gant.Gant$_dispatch_closure5.doCall(Gant.groovy:381)
    at gant.Gant$_dispatch_closure7.doCall(Gant.groovy:415)
    at gant.Gant$_dispatch_closure7.doCall(Gant.groovy)
    at gant.Gant.withBuildListeners(Gant.groovy:427)
    at gant.Gant.this$2$withBuildListeners(Gant.groovy)
    at gant.Gant$this$2$withBuildListeners.callCurrent(Unknown Source)
    at gant.Gant.dispatch(Gant.groovy:415)
    at gant.Gant.this$2$dispatch(Gant.groovy)
    at gant.Gant.invokeMethod(Gant.groovy)
    at gant.Gant.executeTargets(Gant.groovy:590)
    at gant.Gant.executeTargets(Gant.groovy:589)
Caused by: org.quartz.JobPersistenceException: Couldn't store trigger 'mySimpleTrigger' for 'TestUpdaterJob' job:Parameter index out of range (2 > number of parameters, which is 1). [See nested exception: java.sql.SQLException: Parameter index out of range (2 > number of parameters, which is 1).]
    at org.quartz.impl.jdbcjobstore.JobStoreSupport.storeTrigger(JobStoreSupport.java:1245)
    at org.quartz.impl.jdbcjobstore.JobStoreSupport$5.execute(JobStoreSupport.java:1151)
    at org.quartz.impl.jdbcjobstore.JobStoreSupport$40.execute(JobStoreSupport.java:3691)
    at org.quartz.impl.jdbcjobstore.JobStoreCMT.executeInLock(JobStoreCMT.java:242)
    at org.quartz.impl.jdbcjobstore.JobStoreSupport.executeInLock(JobStoreSupport.java:3687)
    at org.quartz.impl.jdbcjobstore.JobStoreSupport.storeTrigger(JobStoreSupport.java:1147)
    at org.quartz.core.QuartzScheduler.scheduleJob(QuartzScheduler.java:849)
    at org.quartz.impl.StdScheduler.scheduleJob(StdScheduler.java:254)
    at org.quartz.Scheduler$scheduleJob.call(Unknown Source)
    at QuartzGrailsPlugin$_closure5_closure24.doCall(QuartzGrailsPlugin.groovy:229)
    at QuartzGrailsPlugin$_closure5.doCall(QuartzGrailsPlugin.groovy:224)
    at QuartzGrailsPlugin.invokeMethod(QuartzGrailsPlugin.groovy)
    at QuartzGrailsPlugin$_closure3_closure21.doCall(QuartzGrailsPlugin.groovy:175)
    at QuartzGrailsPlugin$_closure3.doCall(QuartzGrailsPlugin.groovy:173)
    ... 23 more
Caused by: java.sql.SQLException: Parameter index out of range (2 > number of parameters, which is 1).
    at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:1073)
    at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:987)
    at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:982)
    at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:927)
    at com.mysql.jdbc.PreparedStatement.checkBounds(PreparedStatement.java:3729)
    at com.mysql.jdbc.PreparedStatement.setInternal(PreparedStatement.java:3713)
    at com.mysql.jdbc.PreparedStatement.setString(PreparedStatement.java:4553)
    at com.mysql.jdbc.BlobFromLocator.length(BlobFromLocator.java:333)
    at org.quartz.impl.jdbcjobstore.StdJDBCDelegate.getObjectFromBlob(StdJDBCDelegate.java:3463)
    at org.quartz.impl.jdbcjobstore.StdJDBCDelegate.selectJobDetail(StdJDBCDelegate.java:904)
    at org.quartz.impl.jdbcjobstore.JobStoreSupport.storeTrigger(JobStoreSupport.java:1201)
    ... 36 more

Google does not have a lot of information about it and I had to dig Quartz sources to investigate it, but finally I found that problem is emulateLocators=true on my database URL. I use this parameter for Lucene clustering so it seems there is conflict between them, so it is not possible to use common data source. Otherwise, it fixes this exception easily.

Wednesday, October 5, 2011

Calling stored procedure from JdbcTemplate

I am using JdbcTemplate to make database queries, like:
List<Map<String,Object>> rows = jdbcTemplate.queryForList("select * from rows");
Recently I had to replace SQL selects with stored procedures, but after doing so, I had exception:
java.sql.SQLException: The SQL statement must not contain a procedure call or parameter markers.
        at net.sourceforge.jtds.jdbc.JtdsStatement.executeQuery(JtdsStatement.java:1297)
        at com.mchange.v2.c3p0.impl.NewProxyStatement.executeQuery(NewProxyStatement.java:35)
        at org.springframework.jdbc.core.JdbcTemplate$1QueryStatementCallback.doInStatement(JdbcTemplate.java:440)
        at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:395)
        at org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:455)
        at org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:463)
        at org.springframework.jdbc.core.JdbcTemplate.queryForList(JdbcTemplate.java:494)
As I found out finally, when queryForList is called without parameters it cann't call stored procedures, so to do it you just need to add empty parameter list:
List<Map<String,Object>> tables = jdbcTemplate.queryForList("{call getRows() }", new Object[] {});