Monday, August 31, 2009

Create one stand-alone executable jar

Out of a sudden we realized that you cannot bundle library files (external jars) inside a jar file and create a Main class with MANIFEST.MF pointing to all the jar files bundled. We want to deliver our Java application as one jar file so the business analyst can execute it as
java -jar filename.jar

We always assumed that creating META-INF/MANIFEST.MF with the following settings would work:-
Main-Class: com.mydomain.mypackage.Main
Class-Path: *

filename.jar
| /META-INF
| | MANIFEST.MF
| | Main-Class: com.mydomain.mypackage.Main
| | Class-Path: spring.jar
| /com/mydomain/mypackage
| | Main.class
| spring.jar

It will work if the library files are outside the bundled jar file (containing the Main class) in the same directory.

The Java classloader does not know how to load classes from a Jar inside a Jar. The entries on the Class-Path must be references to files outside the Jar file, defeating the goal of delivering an application in a single Jar file.

The solution is to use : Fat Jar Eclipse Plugin. The tutorial is pretty neat and explains how to convert your Main method and bundle other library files along with it using Fat Jar plugin. Fat jar essentially explodes all the library files. It allows you to select a Main Class and creates a MANIFEST.MF file.

Now you can distribute one jar file to your customers :-)

Saturday, August 29, 2009

Call web service from oracle and parse XML in Oracle

Below is the code used to call web service from Oracle. In this code there is also information how to parse the response XML and get the result needed : http://ejvyas.googlepages.com/plsql_ws.sql

Oracle JDeveloper 11g

Oracle JDeveloper is a free integrated development environment that simplifies the development of Java-based SOA applications and user interfaces with support for the full development life cycle.


Shortcomings:-
  1. Cannot keep it alive till next day!
  2. Memory usage >300MB. Eclipse stays alive even with >500MB memory usage
  3. >1GB download size. There are no options!!
  4. Very crude and sophisticated interface
  5. Slow SOA component (BPEL)
  6. Annoying "Select Role" popup

Wednesday, August 26, 2009

Spring MVC throws java.lang.StackOverflowError if view is not present

I received StackOverflowError because the view name I was referring to in the controller did not exist in the WEB-INF directory. The solution is to check the view name and correct it.

09:51:11,796 ERROR [Engine] ApplicationDispatcher[/testSpring] Servlet.service() for servlet dispatcher threw exception
java.lang.StackOverflowError
at javax.servlet.http.HttpServletRequestWrapper.getSession(HttpServletRequestWrapper.java:215)
at org.apache.catalina.core.ApplicationHttpRequest.getSession(ApplicationHttpRequest.java:518)
at javax.servlet.http.HttpServletRequestWrapper.getSession(HttpServletRequestWrapper.java:215)
at org.apache.catalina.core.ApplicationHttpRequest.getSession(ApplicationHttpRequest.java:518)
at javax.servlet.http.HttpServletRequestWrapper.getSession(HttpServletRequestWrapper.java:215)
at org.apache.catalina.core.ApplicationHttpRequest.getSession(ApplicationHttpRequest.java:518)
at javax.servlet.http.HttpServletRequestWrapper.getSession(HttpServletRequestWrapper.java:215)
at org.apache.catalina.core.ApplicationHttpRequest.getSession(ApplicationHttpRequest.java:518)
at javax.servlet.http.HttpServletRequestWrapper.getSession(HttpServletRequestWrapper.java:215)
at org.apache.catalina.core.ApplicationHttpRequest.getSession(ApplicationHttpRequest.java:518)
at javax.servlet.http.HttpServletRequestWrapper.getSession(HttpServletRequestWrapper.java:215)
at org.apache.catalina.core.ApplicationHttpRequest.getSession(ApplicationHttpRequest.java:518)
at javax.servlet.http.HttpServletRequestWrapper.getSession(HttpServletRequestWrapper.java:215)
at org.apache.catalina.core.ApplicationHttpRequest.getSession(ApplicationHttpRequest.java:518)

Thursday, August 20, 2009

Grails - Date without hours and minutes fields

Replace

class Person{
String Name
Date Birthday
}

with

class Person{
String Name
java.sql.Date Birthday
}

specifying the property as java.sql.Date will render date control without hours and minutes fields.

Monday, August 17, 2009

Redirect Grails stacktrace to log4j file

Below is the configuration needed to redirect all stacktrace info to your log4j log file specified. Also you can redirect all console println statements to your log4j

Config.groovy:-


log4j {
appender.stdout = "org.apache.log4j.ConsoleAppender"
appender.'stdout.layout'="org.apache.log4j.PatternLayout"
appender.'stdout.layout.ConversionPattern'='[%r] %c{2} %m%n'
appender.stacktraceLog = "org.apache.log4j.FileAppender"
appender.'stacktraceLog.layout'="org.apache.log4j.PatternLayout"
appender.'stacktraceLog.layout.ConversionPattern'='[%r] %c{2} %m%n'
appender.'stacktraceLog.File'="../logs/ProcessParamEditor.log"
//rootLogger="WARN"
logger {
grails="error"
StackTrace="error,stacktraceLog"
org {
codehaus.groovy.grails.web.servlet="error" // controllers
codehaus.groovy.grails.web.pages="error" // GSP
codehaus.groovy.grails.web.sitemesh="error" // layouts
codehaus.groovy.grails."web.mapping.filter"="error" // URL mapping
codehaus.groovy.grails."web.mapping"="error" // URL mapping
codehaus.groovy.grails.commons="info" // core / classloading
codehaus.groovy.grails.plugins="error" // plugins
codehaus.groovy.grails.orm.hibernate="error" // hibernate integration
springframework="off"
hibernate="off"
}
}
additivity.StackTrace=false
}

environments {
development {
rootLogger="INFO"
log4j{
logger{
org{
org.codehaus.groovy.grails.plugins.searchable="debug"
}
}
}
}
test {
rootLogger="INFO"
log4j = { appenders { file name:'stacktrace', file:"../logs/stacktrace.log".toString() } }
}
production {
rootLogger="WARN"
log4j = { appenders { file name:'stacktrace', file:"../logs/ProcessParamEditor.log".toString() } }
}

Passing Multiple parameters to remoteFunction

JavaScript in GSP page:

${remoteFunction(controller:'processParamAssign',
action:'operList',update:'operDiv',
params:'\'mfgAreaLoc=\'+mfgAreaLoc+\'&oper=\'+oper')};

In the above line I am passing mfgAreaLoc and oper as 2 parameters to the remoteFunction operList in the controller processParamAssign. NOTE: mfgAreaLoc and oper are JavaScript variables declared before the remoteFunction call is made.

The controller action looks like this:

def operList={
println "Params:"+params
println "Oper:"+params.oper
println "mfgAreaLoc:"+params.mfgAreaLoc
}


Output is:

Params:[oper:LASERM, action:operList,
controller:processParamAssign, mfgAreaLoc:VICMRKCUT]
Oper:LASERM
mfgAreaLoc:VICMRKCUT

Tuesday, August 11, 2009

Bind collections to Spring form

 



The Spring Web Tutorial does a good job at explaining how to bind form to the view for display and input purposes. Unfortunately, it is not so easy when we get into forms that have collections.

Form:-
public class Grid {
     private List blocks = new ArrayList();
     public List getBlocks() {
          return blocks;
     }
     public void setBlocks(List list) {
          blocks = list;
     }
}
public class Block {
     private String id, description;
     public String getDescription() {
          return description;
     }
     public String getId() {
          return id;
     }
     public void setDescription(String string) {
          description = string;
     }
     public void setId(String string) {
          id = string;
     }
}


JSP:-

<c:foreach items="${grid.blocks}" varstatus="gridRow">
     <spring:bind path="grid.blocks[${gridRow.index}].id">
      <c:out value="${status.value}" />
        <input type="hidden" name="<c:out value="${status.expression}">
         id="<c:out value="${status.expression}" />"
         value="<c:out value="${status.value}" />" />
       </spring:bind>
       
     <spring:bind path="grid.blocks[${gridRow.index}].description">
       <c:out value="${status.value}" />
        <input type="hidden" name="&lt;c:out value="${status.expression}">
         id="<c:out value="${status.expression}" />"
         value="<c:out value="${status.value}" />" />
     </spring:bind>
     
</c:foreach>


But this does not work and throws the following exception:-
  InvalidPropertyException: Invalid property 'blocks[1].id' of bean class [Grid]: Index of out of bounds in property path 'blocks[1]'; nested exception is java.lang.IndexOutOfBoundsException: Index: 1, Size: 0 
This is explained very well in the following articles   
1.
Dynamic list binding in Spring

2. Dynamic list binding problem...why? object graph


The workaround is to decorate the ArrayList with LazyList
Form:-

public class Grid { 
      private List blocks = LazyList.decorate( new ArrayList(), 
            FactoryUtils.instantiateFactory(Block.class)); 
 
      public List getBlocks() { 
            return blocks; 
      } 
 
      public void setBlocks(List list) { 
            blocks = list; 
      } 
} 

The rest of the code does not change and I had the desired results :-) Try it and let me know if it works...

Tuesday, August 04, 2009

Spring Programming FAQs

Neat Spring tricks

Read more...