Tuesday, September 20, 2011

Running ant task in maven

Sometimes you have some task that you just need to execute: without attaching to build phase, or any process - just run. Unfortunately maven is not very flexible about it. Fortunately, it is still possible. To do it, you need use antrun plugin with custom profile. For example, like this:

<profiles>
  <profile>
     <id>deploy</id>
     <build>
        <plugins>
          <plugin>
            <artifactId>maven-antrun-plugin</artifactId>
            <version>1.3</version>
            <configuration>
              <tasks>
                <property file="user.properties" prefix="user"/>
                <scp file="target/target.war" todir="${user.login}:${user.password}@${user.server}:/home/user/" trust="true"/>
                <sshexec host="${user.server}" username="${user.login}" password="${user.password}" command="sudo ./redeploy.sh" trust="true" />
              </tasks>
            </configuration>
            <dependencies>
                <dependency>
                    <groupId>org.apache.ant</groupId>
                    <artifactId>ant-jsch</artifactId>
                    <version>1.7.1</version>
                </dependency>
                <dependency>
                    <groupId>com.jcraft</groupId>
                    <artifactId>jsch</artifactId>
                    <version>0.1.42</version>
                </dependency>
            </dependencies>
          </plugin>
        </plugins>
     </build>
  </profile>
...

</profiles>

Later, you can run this script like:


mvn antrun:run -Pdeploy

Of course you can have different scripts in several profiles.

This approach even has advantages over having separate Ant's build.xml - you don't need to have separate Ant installation, or for instance, with example above, you don't need to download jsch plugin separately - it is all done by Maven.

Friday, September 16, 2011

Java web application monitoring tool

I was looking for production monitoring tool recently. I didn't have any specific requirements, just to have some understanding about load and bottlenecks. Finally, after some googling I found one very interesting option JavaMelody.

This tool takes risky, but very interesting approach, proxies JDBC calls, loads as web filter, advisor for Spring, etc. Additionally, it watches usual VM stuff, like JMX. As result it has a lot of very detailed and deep statistics, like sql queries time, page load times, and so on. Also it provides a lot of graphics and tables, to watch, generally looks nice and advanced. Of course with this approach it looks quite dangerous, but seems like this is already mature tool and I would like to try to use it in my projects.

Also, it has very nice and simple integration with Grails. Just install Grails-melody plugin, and you have your statistics under URL <hostname:port/app-ctx>/monitoring. This is regular application URL, so you can easily configure access rights and login in your application.

Thursday, September 1, 2011

Hibernate "not-found" in Grails

Often when you delete object from db instead of just marking it as deleted, there is nasty exception that this object is referenced by someone else.
2011-09-01 15:18:38,953 [http-8080-1] ERROR errors.GrailsExceptionResolver  - Exception occurred when processing request: [GET] xxx
Stacktrace follows:
org.codehaus.groovy.grails.web.pages.exceptions.GroovyPagesException: Error processing GroovyPageView: Error executing tag <g:form>: Error executing tag <sec:ifAllGranted>: Error evaluating expression [xxx.yyy?.id] on line [70]: No row with the given identifier exists: [xxx#21244] at xxx.gsp:71 at xxx.gsp:102
    at java.lang.Thread.run(Thread.java:662)
Caused by: org.codehaus.groovy.grails.web.taglib.exceptions.GrailsTagException: Error executing tag <g:form>: Error executing tag <sec:ifAllGranted>: Error evaluating expression [xxx.yyy?.id] on line [70]: No row with the given identifier exists: [yyy#21244] at xxx.gsp:71 at xxx.gsp:102
    ... 1 more
Caused by: org.codehaus.groovy.grails.web.taglib.exceptions.GrailsTagException: Error executing tag <sec:ifAllGranted>: Error evaluating expression [xxx.yyy?.id] on line [70]: No row with the given identifier exists: [xxx#21244] at xxx.gsp:71
    ... 4 more
Caused by: org.codehaus.groovy.grails.web.pages.exceptions.GroovyPagesException: Error evaluating expression [xxx.yyy?.id] on line [70]: No row with the given identifier exists: [xxx#21244]
    at grails.plugins.springsecurity.SecurityTagLib$_closure1.doCall(SecurityTagLib.groovy:60)
    ... 7 more
Caused by: org.hibernate.ObjectNotFoundException: No row with the given identifier exists: [xxx#21244]
    ... 10 more
In Hibernate there is nice not-found="ignore" for such cases. In Grails/GORM there is similar option which is not documented for some reason. Just add ignoreNotFound to domain objects mapping section and that is it. For example:
class Log {
  User user

  static mapping = {
    user ignoreNotFound : true 
  }

}

Tuesday, August 2, 2011

"Slash n" in db migrations scripts in Grails

If it is needed to print \n from migration script, it is needed to write 4 slashes before n (1, 2 or 3 will just print new line symbol), so it should look something like:

  changeSet(author: "user", id: "1") {
    insert(tableName: "table") {
      column (name:'column', value:'''aaaa\\\\nbbbb''')
    }
  }


will result in value being -

aaaa\nbbbb

Monday, August 1, 2011

Cannot delete parent object in GORM

Sometimes, when trying to delete parent object in GORM, even when there is children cascade:'all-delete-orphan' there is exception:

com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Cannot delete or update a parent row: a foreign key constraint fails (`db`.`domain`, CONSTRAINT `FKB8FB823DBF953235` FOREIGN KEY (`domain_id`) REFERENCES `user` (`id`))
    at com.mysql.jdbc.Util.handleNewInstance(Util.java:409)
    at com.mysql.jdbc.Util.getInstance(Util.java:384)
    at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:1041)
    at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3566)
    at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3498)
    at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:1959)
    at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2113)
    at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2568)
    at com.mysql.jdbc.PreparedStatement.executeInternal(PreparedStatement.java:2113)
    at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2409)
    at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2327)
    at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2312)
    at org.apache.commons.dbcp.DelegatingPreparedStatement.executeUpdate(DelegatingPreparedStatement.java:105)
    at org.apache.commons.dbcp.DelegatingPreparedStatement.executeUpdate(DelegatingPreparedStatement.java:105)

It is possible to solve this issue, by explicitly clearing all children from parent object before delete.

    domain.children.clear()


After that it successfully deletes domain object.

Sunday, July 31, 2011

NoSuchElementException after click() in Selenium 2

Sometimes in Selenium 2 after clicking on some element, it does not execute actual click (but does not throws error that element is not found), cann't find element from next page and therefore throws this exception:

Unable to locate element: {"method":"class name","selector":"alert"} For documentation on this error, please visit: http://seleniumhq.org/exceptions/no_such_element.html Build info: version: '2.2.0', revision: '13073', time: '2011-07-26 00:54:48' System info: os.name: 'Windows 7', os.arch: 'amd64', os.version: '6.1', java.version: '1.6.0_24' Driver info: driver.version: RemoteWebDriver

org.openqa.selenium.NoSuchElementException: Unable to locate element: {"method":"class name","selector":"alert"}
For documentation on this error, please visit: http://seleniumhq.org/exceptions/no_such_element.html
Build info: version: '2.2.0', revision: '13073', time: '2011-07-26 00:54:48'
System info: os.name: 'Windows 7', os.arch: 'amd64', os.version: '6.1', java.version: '1.6.0_24'
Driver info: driver.version: RemoteWebDriver
    at org.openqa.selenium.remote.ErrorHandler.createThrowable(ErrorHandler.java:131)
    at org.openqa.selenium.remote.ErrorHandler.throwIfResponseFailed(ErrorHandler.java:105)
    at org.openqa.selenium.remote.RemoteWebDriver.execute(RemoteWebDriver.java:405)
    at org.openqa.selenium.remote.RemoteWebDriver.findElement(RemoteWebDriver.java:193)
    at org.openqa.selenium.remote.RemoteWebDriver.findElementByClassName(RemoteWebDriver.java:250)
    at org.openqa.selenium.By$ByClassName.findElement(By.java:349)
    at org.openqa.selenium.remote.RemoteWebDriver.findElement(RemoteWebDriver.java:185)
    at org.openqa.selenium.WebDriver$findElement.call(Unknown Source)
...
Caused by: org.openqa.selenium.remote.ErrorHandler$UnknownServerException: Unable to locate element: {"method":"class name","selector":"alert"}
Build info: version: '2.2.0', revision: '13073', time: '2011-07-26 00:54:48'
System info: os.name: 'Windows 7', os.arch: 'amd64', os.version: '6.1', java.version: '1.6.0_24'
Driver info: driver.version: unknown

First, you should check that there is no element with same selector and it does not tries to click some div. Second, I found that sometimes if there is floating div, especial over your links or buttons, it cann't properly click it. In latter case, best solution for form buttons is to use submit, instead of click, like:
driver.findElement(By.id("update")).submit();
Another thing that helps - especially with links, is to remove overflowing div (closing it - depends on your application logic) and waiting for second, like:
Thread.sleep(1000);

Tuesday, July 26, 2011

How to ignore failed database migrations

When using Grails database migration plugin, failing migrations is sign of some problem - usually some syntax error. But in some cases it is not needed and it is ok to ignore them. For example, I faced this problem while testing application which created db in memory from Hibernate entities. Some particular migrations, like ones dealing with existing records or legacy columns, were failing with no meaning.
Fortunately, there is easy way to ignore some migrations. Just add failOnError:'false' to change set definition and it will report exception in logs, but will continue with loading. There is example:

  changeSet(author: "user", id: "96", failOnError:'false') {
    update(tableName: "table") {
      column(name: "column", valueBoolean: false)
      where ('column is null')
    }
  }


This is good that it is specified per change set, as only these dealing with particular columns will be able to fail, it will be still strict with others. Only way it would be better, if there would be global property, so it would be still strict in production, but more forgiving in test environment.