Keep walking around WebLogic server and JRockit traps

Happily developed web based application by using Groovy and Grails, and it runs happily as well on Tomcat. Then deploy to WebLogic server in staging environment which runs in JRockit JVM in RedHat Linux (better for performance), bugged.

Curiously try to duplicate the issue, I set up the exactly same version WebLogic server for Windows, but using standard JDK 1.6.x, same war file deployed, but application runs without any problem.

Always admire Oracle’s product quality and highly calibre service, I never bother to call them – help yourself, I can see Oracle sales smiling to me.

The first issue on WebLogic in JRockit is Exception thrown when try to start up the application:

Caused By: java.lang.IllegalArgumentException: com.sun.xml.internal.messaging.saaj.soap.LocalStrings != com.sun.xml.messaging.saaj.soap.LocalStrings
        at java.util.logging.Logger.getLogger(Logger.java:337)
        at com.sun.xml.messaging.saaj.soap.SAAJMetaFactoryImpl.(SAAJMetaFactoryImpl.java:71)
        at java.lang.Class.newInstance0(Class.java:355)
        at java.lang.Class.newInstance(Class.java:308)
        at javax.xml.soap.FactoryFinder.newInstance(FactoryFinder.java:59)

Did some search and found out this link on developers favourited StackOverflow – http://stackoverflow.com/questions/3889087/illegalargumentexception-com-sun-xml-internal-messaging-saaj-soap-localstrings

It seems JRockit has its own implementation of SOAP SAAJ which is not compatible, even in saaj-impl-1.3.2.jar file already specifies to use implementation inside jar file, rather then jar bundled with JVM:

    64 META-INF/services/javax.xml.soap.MessageFactory
    51 META-INF/services/javax.xml.soap.MetaFactory
    63 META-INF/services/javax.xml.soap.SOAPConnectionFactory
    57 META-INF/services/javax.xml.soap.SOAPFactory

The content inside these files are:

$ cat javax.xml.soap.MessageFactory
com.sun.xml.messaging.saaj.soap.ver1_1.SOAPMessageFactory1_1Impl

$ cat javax.xml.soap.MetaFactory
com.sun.xml.messaging.saaj.soap.SAAJMetaFactoryImpl

$ cat javax.xml.soap.SOAPConnectionFactory
com.sun.xml.messaging.saaj.client.p2p.HttpSOAPConnectionFactory

$ cat javax.xml.soap.SOAPFactory
com.sun.xml.messaging.saaj.soap.ver1_1.SOAPFactory1_1Impl

To fix or walk around this issue, I have to ask application to use JVM’s SOAP SAAJ implementation. This is easily to do in Grails BuildConfig.groovy file:

[groovy]
grails.war.resources = { stagingDir, args ->
println "Deleting non-dependent jars development WebLogic Server …"
delete(file: "${stagingDir}/WEB-INF/lib/saaj-impl-1.3.2.jar")

}
[/groovy]

Fix the first problem, then comes the second during runtime:

java.lang.NoSuchFieldError: org/springframework/util/ReflectionUtils.USER_DECLARED_METHODSLorg/springframework/util/ReflectionUtils$MethodFilter;
        at org.springframework.web.bind.annotation.support.HandlerMethodResolver.init(HandlerMethodResolver.java:101)
        at org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter$ServletHandlerMethodResolver.(AnnotationMethodHandlerAdapter.java:504)
        at org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter$ServletHandlerMethodResolver.(AnnotationMethodHandlerAdapter.java:503)
        at org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter.getMethodResolver(AnnotationMethodHandlerAdapter.java:445)
        at org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter.supports(AnnotationMethodHandlerAdapter.java:387)

So after reading the source codes ReflectionUtils.java, it seems there is a confliction related to spring-core package.

Filter out all spring jar files from war file:

    60835  WEB-INF/lib/grails-spring-1.3.5.jar
   320926  WEB-INF/lib/org.springframework.aop-3.0.3.RELEASE.jar
    53082  WEB-INF/lib/org.springframework.asm-3.0.3.RELEASE.jar
    30193  WEB-INF/lib/org.springframework.aspects-3.0.3.RELEASE.jar
   553901  WEB-INF/lib/org.springframework.beans-3.0.3.RELEASE.jar
   662191  WEB-INF/lib/org.springframework.context-3.0.3.RELEASE.jar
   100368  WEB-INF/lib/org.springframework.context.support-3.0.3.RELEASE.jar
   365217  WEB-INF/lib/org.springframework.core-3.0.3.RELEASE.jar
   160146  WEB-INF/lib/org.springframework.expression-3.0.3.RELEASE.jar
     1808  WEB-INF/lib/org.springframework.instrument-3.0.3.RELEASE.jar
   379691  WEB-INF/lib/org.springframework.jdbc-3.0.3.RELEASE.jar
   184665  WEB-INF/lib/org.springframework.jms-3.0.3.RELEASE.jar
   333500  WEB-INF/lib/org.springframework.orm-3.0.3.RELEASE.jar
    60838  WEB-INF/lib/org.springframework.oxm-3.0.3.RELEASE.jar
   231345  WEB-INF/lib/org.springframework.transaction-3.0.3.RELEASE.jar
   389117  WEB-INF/lib/org.springframework.web-3.0.3.RELEASE.jar
   404158  WEB-INF/lib/org.springframework.web.servlet-3.0.3.RELEASE.jar
   321190  WEB-INF/lib/spring-aop-3.0.5.RELEASE.jar
    53082  WEB-INF/lib/spring-asm-3.0.5.RELEASE.jar
   555410  WEB-INF/lib/spring-beans-3.0.5.RELEASE.jar
   668861  WEB-INF/lib/spring-context-3.0.5.RELEASE.jar
   100870  WEB-INF/lib/spring-context-support-3.0.5.RELEASE.jar
   382442  WEB-INF/lib/spring-core-3.0.5.RELEASE.jar
   169752  WEB-INF/lib/spring-expression-3.0.5.RELEASE.jar
    61379  WEB-INF/lib/spring-oxm-3.0.5.RELEASE.jar
   395587  WEB-INF/lib/spring-web-3.0.5.RELEASE.jar
   418977  WEB-INF/lib/spring-webmvc-3.0.5.RELEASE.jar
   487211  WEB-INF/lib/spring-ws-core-2.0.0.RELEASE.jar
    90472  WEB-INF/lib/spring-xml-2.0.0.RELEASE.jar

There are two versions Spring in the war file!

org.springframework.*-3.0.3.*.jar are dependencies of Grails version 1.3.5; spring-*-3.0.5.*.jar are the dependencies of spring-ws-core-2.0.0*.jar.

So add more to Grails BuildConfig.groovy file to delete the conflicted jar files:

[groovy]
delete(file: "${stagingDir}/WEB-INF/lib/spring-aop-3.0.5.RELEASE.jar")
delete(file: "${stagingDir}/WEB-INF/lib/spring-asm-3.0.5.RELEASE.jar")
delete(file: "${stagingDir}/WEB-INF/lib/spring-beans-3.0.5.RELEASE.jar")
delete(file: "${stagingDir}/WEB-INF/lib/spring-context-3.0.5.RELEASE.jar")
delete(file: "${stagingDir}/WEB-INF/lib/spring-context-support-3.0.5.RELEASE.jar")
delete(file: "${stagingDir}/WEB-INF/lib/spring-core-3.0.5.RELEASE.jar")
delete(file: "${stagingDir}/WEB-INF/lib/spring-expression-3.0.5.RELEASE.jar")
delete(file: "${stagingDir}/WEB-INF/lib/spring-oxm-3.0.5.RELEASE.jar")
delete(file: "${stagingDir}/WEB-INF/lib/spring-web-3.0.5.RELEASE.jar")
[/groovy]
Lesson learned is JRockit maybe has a different ClassLoader strategy to standard JDK release.

Annoying SLF4J problem in Weblogic server 12c

Using Grails quickly build a web-based application. Deploy and run happily on Tomcat. But after deploy the same war on the latest WebLogic server development version 12c, failed and Exception thrown:

Caused by: java.lang.NoSuchMethodError: org.slf4j.spi.LocationAwareLogger.log(Lorg/slf4j/Marker;Ljava/lang/String;ILjava/lang/String;Ljava/lang/Throwable;)V
at org.apache.commons.logging.impl.SLF4JLocationAwareLog.info(SLF4JLocationAwareLog.java:159)
at org.springframework.web.servlet.FrameworkServlet.initServletBean(FrameworkServlet.java:301)
at org.springframework.web.servlet.HttpServletBean.init(HttpServletBean.java:127)
at javax.servlet.GenericServlet.init(GenericServlet.java:240)

After a few drilled investigation, found out bloody WebLogic comes with SLF4J itself, under the directory:

./modules/org.slf4j.api_1.6.1.0.jar
./modules/org.slf4j.ext_1.6.1.0.jar
./modules/org.slf4j.jdk14_1.6.1.0.jar

These SLF4J jar files are conflicting with different version SLF4J jar files in the war file:

17097 10-04-10 10:07 WEB-INF/lib/jcl-over-slf4j-1.5.8.jar
 4464 10-04-10 10:07 WEB-INF/lib/jul-to-slf4j-1.5.8.jar
23445 10-04-10 10:07 WEB-INF/lib/slf4j-api-1.5.8.jar
 9679 10-04-10 10:07 WEB-INF/lib/slf4j-log4j12-1.5.8.jar

After another investigation, the only solution can try is to use SLF4J jar files in the package “overwrite” jar files in WebLogic modules. To do that, add weblogic.xml descriptor file under web-app/WEB-INF directory:

[xml]
<?xml version="1.0" encoding="UTF-8"?>
<wls:weblogic-web-app
xmlns:wls="http://xmlns.oracle.com/weblogic/weblogic-web-app&quot;
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance&quot;
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/ejb-jar_3_0.xsd http://xmlns.oracle.com/weblogic/weblogic-web-app http://xmlns.oracle.com/weblogic/weblogic-web-app/1.4/weblogic-web-app.xsd"&gt;
<wls:container-descriptor>
<wls:prefer-application-packages>
<wls:package-name>org.slf4j</wls:package-name>
</wls:prefer-application-packages>
</wls:container-descriptor>
</wls:weblogic-web-app>
[/xml]

Basically, it asks WebLogic server to filter out org.slf4j from classloader.

Reference

JSF 2, Spring 3, Spring MVC 3, Hibernate 4, Tomcat 7 – a lightweight approach

“What is your favourite design pattern?” Question asked in a job interview.

WTF question is that?

“Hmmm… my favourite design pattern is Salami pattern – approaching to the finish line slice by slice.”

— From Antidisestablishmentarianism I.T.

In my previous blog – Spring 3 shacks up JSF 2, the maverick way, demonstrating how JSF 2 a component, event driven framework as the presentation layer, and Spring 3, Spring MVC 3 work in middleware. Well, technology keeps evolving. Hopefully even pigs could programe one day. This time, Hibernate 4 is added as the persistence layer. Now, we have end-to-end, a “traditional” three tiers web application. Moreover, an unit test to test business logic layer, the most important, runs outside of container.

Firstly, files and directories structure:

The backend database is Oracle’s example of  Human Resources (HR) application for a fictitious company (check the reference at the end of the blog).

Let’s start with configuration in the project.

pom.xml

[xml]
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0&quot;
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance&quot;
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"&gt;
<modelVersion>4.0.0</modelVersion>
<groupId>hellospring</groupId>
<artifactId>hellospring</artifactId>
<packaging>war</packaging>
<version>0.0.1-SNAPSHOT</version>
<name>hellospring</name>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>

<dependencies>
<dependency>
<groupId>com.sun.faces</groupId>
<artifactId>jsf-api</artifactId>
<version>2.1.7</version>
</dependency>
<dependency>
<groupId>com.sun.faces</groupId>
<artifactId>jsf-impl</artifactId>
<version>2.1.7</version>
</dependency>

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>3.1.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>3.1.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>3.1.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.webflow</groupId>
<artifactId>spring-faces</artifactId>
<version>2.3.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>3.1.1.RELEASE</version>
</dependency>

<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.6.1</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jul-to-slf4j</artifactId>
<version>1.6.1</version>
</dependency>

<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.2.2</version>
</dependency>

<!– Unit Test –>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.8.2</version>
<scope>test</scope>
</dependency>

<dependency>
<groupId>cglib</groupId>
<artifactId>cglib-nodep</artifactId>
<version>2.2.2</version>
<scope>test</scope>
</dependency>

<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>test</scope>
</dependency>

<!– ORACLE database driver –>
<dependency>
<groupId>com.oracle</groupId>
<artifactId>ojdbc6</artifactId>
<version>11.2.0</version>
</dependency>

<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>4.1.1.Final</version>
</dependency>

<!– Hibernate c3p0 connection pool –>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-c3p0</artifactId>
<version>4.1.1.Final</version>
</dependency>

<dependency>
<groupId>javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.12.1.GA</version>
</dependency>

<!– logback –>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.0.1</version>
</dependency>

<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.0.1</version>
</dependency>

<!– not include log4j –>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.16</version>
<scope>provided</scope>
</dependency>
</dependencies>

<build>
<finalName>HelloSpring</finalName>

<plugins>
<!–
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>2.5</version>
<executions>
<execution>
<id>copy-resources</id>
<phase>validate</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<outputDirectory>${basedir}/target/classes</outputDirectory>
<resources>
<resource>
<directory>${basedir}/src-generated</directory>
<excludes>
<exclude>**/*.java </exclude>
</excludes>
</resource>
</resources>
</configuration>
</execution>
</executions>
</plugin>
–>

<!– Plugin to add source path to build cycle –>
<!–
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<version>1.7</version>
<executions>
<execution>
<id>add-source</id>
<phase>generate-sources</phase>
<goals>
<goal>add-source</goal>
</goals>
<configuration>
<sources>
<source>${basedir}/src-generated</source>
</sources>
</configuration>
</execution>
<execution>
<id>attach-artifacts</id>
<phase>package</phase>
<goals>
<goal>attach-artifact</goal>
</goals>
<configuration>
<artifacts>
<artifact>
<file>${basedir}/src-generated/hibernate.cfg.xml</file>
<type>xml</type>
<classifier>hibernate</classifier>
</artifact>
</artifacts>
</configuration>
</execution>
</executions>
</plugin>
–>

<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.0-beta-1</version>
<configuration>
<url>http://localhost:8080/manager/html</url&gt;
<server>TomcatServer</server>
<path>/HelloSpring</path>
</configuration>
</plugin>
</plugins>
</build>
</project>
[/xml]

applicationContext.xml

[xml]
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans&quot;
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance&quot;
xmlns:context="http://www.springframework.org/schema/context&quot;
xmlns:aop="http://www.springframework.org/schema/aop&quot;
xmlns:tx="http://www.springframework.org/schema/tx&quot;
xmlns:mvc="http://www.springframework.org/schema/mvc&quot;
xmlns:p="http://www.springframework.org/schema/p&quot;

xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
">

<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="classpath:properties/jdbc.properties"/>
</bean>

<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"
p:driverClassName="${jdbc.driverClass}"
p:url="${jdbc.url}"
p:password="${jdbc.password}"
p:username="${jdbc.username}"
>
</bean>

<!– Hibernate session factory –>
<bean id="sessionFactory"
class="org.springframework.orm.hibernate4.LocalSessionFactoryBean"
p:dataSource-ref="dataSource">
<property name="annotatedClasses">
<list>
<value>org.paradise.sandbox.entity.OehrCustomers</value>
<value>org.paradise.sandbox.entity.OehrJobHistory</value>
<value>org.paradise.sandbox.entity.OehrDepartments</value>
<value>org.paradise.sandbox.entity.OehrLocations</value>
<value>org.paradise.sandbox.entity.OehrEmployees</value>
<value>org.paradise.sandbox.entity.OehrAccountManagers</value>
<value>org.paradise.sandbox.entity.OehrProductInformation</value>
<value>org.paradise.sandbox.entity.OehrCountries</value>
<value>org.paradise.sandbox.entity.OehrJobs</value>
<value>org.paradise.sandbox.entity.OehrSydneyInventory</value>
<value>org.paradise.sandbox.entity.OehrOcOrders</value>
<value>org.paradise.sandbox.entity.OehrBombayInventory</value>
<value>org.paradise.sandbox.entity.OehrProducts</value>
<value>org.paradise.sandbox.entity.OehrOcCustomers</value>
<value>org.paradise.sandbox.entity.OehrProductPrices</value>
<value>org.paradise.sandbox.entity.OehrWarehouses</value>
<value>org.paradise.sandbox.entity.OehrProductDescriptions</value>
<value>org.paradise.sandbox.entity.OehrTorontoInventory</value>
<value>org.paradise.sandbox.entity.OehrCustomers</value>
<value>org.paradise.sandbox.entity.OehrRegions</value>
<value>org.paradise.sandbox.entity.OehrOrderItems</value>
<value>org.paradise.sandbox.entity.OehrOcInventories</value>
<value>org.paradise.sandbox.entity.OehrOcProductInformation</value>
<value>org.paradise.sandbox.entity.OehrOrders</value>
<value>org.paradise.sandbox.entity.OehrOcCorporateCustomers</value>
<value>org.paradise.sandbox.entity.OehrPromotions</value>
<value>org.paradise.sandbox.entity.OehrInventories</value>
</list>
</property>
<property name="hibernateProperties">
<value> hibernate.dialect=org.hibernate.dialect.Oracle10gDialect
hibernate.show_sql=${jdbc.showSql}
hibernate.format_sql=${jdbc.formatSql}
</value>
</property>
</bean>

<!– Activates annotation-based bean configuration –>
<context:annotation-config/>

<context:component-scan base-package="org.paradise.sandbox" />

<!– map all requests to /resources/** to the container default servlet (ie, don’t let Spring handle them) –>
<bean id="defaultServletHttpRequestHandler"
class="org.springframework.web.servlet.resource.DefaultServletHttpRequestHandler" />

<bean id="simpleUrlHandlerMapping"
class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="urlMap">
<map>
<entry key="/resources/**" value-ref="defaultServletHttpRequestHandler" />
</map>
</property>
</bean>

<bean class="org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter" />

<mvc:annotation-driven />

<!– JSF for representation layer. All JSF files under /WEB-INF/views directory –>
<bean id="viewResolver"
class="org.springframework.web.servlet.view.UrlBasedViewResolver">
<property name="cache" value="false" />
<property name="viewClass" value="org.springframework.faces.mvc.JsfView" />
<property name="prefix" value="/WEB-INF/views/" />
<property name="suffix" value=".xhtml" />
</bean>
</beans>
[/xml]

logging with logback.xml

[xml]
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<Pattern>%d{yyyy-MM-dd_HH:mm:ss.SSS} %-5level %logger{36} – %msg%n</Pattern>
</encoder>
</appender>

<appender name="FILE"
class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>/usr/local/tomcat/logs/hibernate.log</file>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<Pattern>%d{yyyy-MM-dd_HH:mm:ss.SSS} [%thread] %-5level %logger{36} – %msg%n</Pattern>
</encoder>

<rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
<FileNamePattern>/usr/local/tomcat/logs/hibernate.%i.log.zip</FileNamePattern>
<MinIndex>1</MinIndex>
<MaxIndex>10</MaxIndex>
</rollingPolicy>

<triggeringPolicy
class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
<MaxFileSize>2MB</MaxFileSize>
</triggeringPolicy>
</appender>

<appender name="lilith" class="ch.qos.logback.classic.net.SocketAppender">
<RemoteHost>localhost</RemoteHost>
<Port>4560</Port>
<ReconnectionDelay>170</ReconnectionDelay>
<IncludeCallerData>true</IncludeCallerData>
</appender>

<logger name="org.hibernate.type" level="ALL" />
<logger name="org.hibernate" level="INFO" />

<root level="INFO">
<appender-ref ref="FILE" />
<appender-ref ref="STDOUT" />
<appender-ref ref="lilith" />
</root>
</configuration>
[/xml]

Now, let’s have a look the presentation tier codes.

HelloSpring.java – a simple data transfer bean

[java]
package org.paradise.sandbox;

import org.springframework.context.annotation.Scope;
import org.springframework.context.annotation.ScopedProxyMode;
import org.springframework.stereotype.Component;

@Component
@Scope(value="request", proxyMode=ScopedProxyMode.TARGET_CLASS)
public class HelloSpring {
private String message;

public String getMessage() {
return message;
}

public void setMessage(String message) {
this.message = message;
}
}
[/java]

  • @Component annotation indicates this is an auto scan component
HelloSpringController.java – a controller in MVC architecture

[java]
package org.paradise.sandbox;

import org.hibernate.Session;
import org.paradise.sandbox.entity.OehrCustomers;
import org.paradise.sandbox.util.HibernateUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;

import org.paradise.sandbox.business.CustomerBo;

@Controller
public class HelloSpringController {

@Autowired
private HelloSpring helloSpring;
@Autowired
private CustomerBo customerBo;

@RequestMapping(value="/helloSpring", method=RequestMethod.GET)
public void helloWorld() {
helloSpring.setMessage("Hello Spring from Spring MVC to JSF");
}

@RequestMapping(value = "/helloSpring", method = RequestMethod.POST)
public void helloWorld(@RequestParam String msg) {
OehrCustomers oherCustomer = customerBo.findByCustomerId(101);

if (oherCustomer != null) {
helloSpring.setMessage(oherCustomer.getCustFirstName() + " "
+ oherCustomer.getCustLastName() + " says " + msg);
} else {
helloSpring.setMessage(msg);
}
}

}
[/java]

  • @Controller annotation indicates this is a controller component in the presentation layer

Good practice is NOT include any kind of business logic in the controller. All the business logic should be delegated to business layer. Controller should be the as thin as possible layer between the web world and business world. You can seen object CustomerBo as the delegator is injected into the controller.

Now, in business layer.

CustomerBo.java

[java]
package org.paradise.sandbox.business;

import org.paradise.sandbox.entity.OehrCustomers;

public interface CustomerBo {

void save(OehrCustomers customer);

void update(OehrCustomers customer);

void delete(OehrCustomers customer);

OehrCustomers findByCustomerId(int customerId);

}
[/java]

CustomerBoImpl.java

[java]
package org.paradise.sandbox.business;

import org.paradise.sandbox.dao.CustomerDao;
import org.paradise.sandbox.entity.OehrCustomers;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service("customerBo")
public class CustomerBoImpl implements CustomerBo {

@Autowired
CustomerDao customerDao;

@Override
public void save(OehrCustomers customer) {
}

@Override
public void update(OehrCustomers customer) {
}

@Override
public void delete(OehrCustomers customer) {
}

@Override
public OehrCustomers findByCustomerId(int customerId) {
return customerDao.findByCustomerId(customerId);
}

}
[/java]

  • @Service annotation indicates this is service component in the business layer

Last but not least persistence layer.

CustomerDao.java

[java]
package org.paradise.sandbox.dao;

import org.paradise.sandbox.entity.OehrCustomers;

public interface CustomerDao {

void save(OehrCustomers customer);

void update(OehrCustomers customer);

void delete(OehrCustomers customer);

OehrCustomers findByCustomerId(int customerId);

}
[/java]

CustomerDaoImpl.java

[java]
package org.paradise.sandbox.dao;

import org.paradise.sandbox.entity.OehrCustomers;
import org.paradise.sandbox.util.DaoSupport;
import org.springframework.stereotype.Repository;

@Repository("customerDao")
public class CustomerDaoImpl extends DaoSupport implements CustomerDao {

@Override
public void save(OehrCustomers customer) {
}

@Override
public void update(OehrCustomers customer) {
}

@Override
public void delete(OehrCustomers customer) {
}

@Override
public OehrCustomers findByCustomerId(int customerId) {
return (OehrCustomers) getSessionFactory().openSession().get(
OehrCustomers.class, customerId);
}

}
[/java]

  • @Repository annotation indicates this is DAO component in the persistence layer

DaoSupport.java

[java]
package org.paradise.sandbox.util;

import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;

public abstract class DaoSupport {

@Autowired
private SessionFactory sessionFactory;

public SessionFactory getSessionFactory() {
return this.sessionFactory;
}

}
[/java]

A high quality application must come with high quality test. The following unit test tests DAO component in persistence layer domain only, and outside any container.

CustomerDaoTest.java

[java]
package org.paradise.sandbox.dao;

import junit.framework.Assert;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.paradise.sandbox.entity.OehrCustomers;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration( { "/applicationContext.xml" })
public class CustomerDaoTest {

@Autowired
private CustomerDao customerDao;

private int customerId = 101;
private String expectedCustomerFirstName = "Constantin";
private String expectedCustomerLastName = "Welles";

@Test
public void testFindByCustomerId() {
OehrCustomers customer = customerDao.findByCustomerId(customerId);

Assert.assertEquals(expectedCustomerFirstName, customer.getCustFirstName());
Assert.assertEquals(expectedCustomerLastName, customer.getCustLastName());
}
}
[/java]

The dependency to above unit test in Eclipse is just amazing. See how many jars files need to set up:

This is output of unit test run by Maven:

tmiao@chmbp02-2 ~/Projects/Sandbox/src/HelloSpring
13:57:22 362 $ mvn test
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building hellospring 0.0.1-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] --- maven-resources-plugin:2.4.3:resources (default-resources) @ hellospring ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] Copying 10 resources
[INFO]
[INFO] --- maven-compiler-plugin:2.3.2:compile (default-compile) @ hellospring ---
[INFO] Nothing to compile - all classes are up to date
[INFO]
[INFO] --- maven-resources-plugin:2.4.3:testResources (default-testResources) @ hellospring ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] skip non existing resourceDirectory /Users/tmiao/Projects/Sandbox/src/HelloSpring/src/test/resources
[INFO]
[INFO] --- maven-compiler-plugin:2.3.2:testCompile (default-testCompile) @ hellospring ---
[INFO] Compiling 1 source file to /Users/tmiao/Projects/Sandbox/src/HelloSpring/target/test-classes
[INFO]
[INFO] --- maven-surefire-plugin:2.7.2:test (default-test) @ hellospring ---
[INFO] Surefire report directory: /Users/tmiao/Projects/Sandbox/src/HelloSpring/target/surefire-reports

-------------------------------------------------------
 T E S T S
-------------------------------------------------------
Running org.paradise.sandbox.dao.CustomerDaoTest
Hibernate:
    select
        oehrcustom0_.CUSTOMER_ID as CUSTOMER1_0_0_,
        oehrcustom0_.CREDIT_LIMIT as CREDIT2_0_0_,
        oehrcustom0_.CUST_ADDRESS as CUST3_0_0_,
        oehrcustom0_.CUST_EMAIL as CUST4_0_0_,
        oehrcustom0_.CUST_FIRST_NAME as CUST5_0_0_,
        oehrcustom0_.CUST_GEO_LOCATION as CUST6_0_0_,
        oehrcustom0_.CUST_LAST_NAME as CUST7_0_0_,
        oehrcustom0_.NLS_LANGUAGE as NLS8_0_0_,
        oehrcustom0_.NLS_TERRITORY as NLS9_0_0_,
        oehrcustom0_.ACCOUNT_MGR_ID as ACCOUNT11_0_0_,
        oehrcustom0_.PHONE_NUMBERS as PHONE10_0_0_
    from
        OEHR_CUSTOMERS oehrcustom0_
    where
        oehrcustom0_.CUSTOMER_ID=?
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 4.97 sec

Results :

Tests run: 1, Failures: 0, Errors: 0, Skipped: 0

[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 11.143s
[INFO] Finished at: Sun Apr 22 16:57:11 EST 2012
[INFO] Final Memory: 12M/81M
[INFO] ------------------------------------------------------------------------

In this demo, I try to use Annotation as many as possible. The reason for that the difference between Annotation and XML configuration. XML is good if some settings need to be kept updating. On the contrary, Annotation which is for the typesafe and injection, is good for some settings just need to write once.

Another thing in persistence layer of this demo, sessionFactory is Autowired and injected into the DAO bean. The reason why not using HibernateDaoSupport / HibernateTemplate as parent class for DAO bean is because they are unnecessarily ties your codes to Spring classes. This is important that Hibernate should not know Spring and what Spring is doing at all.

Well, that’s all for this blog. What do you reckon? Is it a simple, straight-forward, test friendly and a very light way to do the web application development?

Related Content:

Step by step to create a dynamic Map chart in APEX

In last blog, I demo how to create a dynamic 3D bar chart in APEX. Now let’s have a bit fun – let’s create a map chart in APEX. It is based on the same example of  Human Resources (HR) application for a fictitious company called ManyCo Corp.

I’m going to create a map page for ManyCo app, drawing a map including number of employees around world by each country.

Log on APEX, select ManyCo app, click on “Create Page” button and select “Chart”, then click on “Next”:

Choose “World and Continent Maps” in Select Map Type step, then click on “Next”:

Choose “World” in Create Map page, then click on “Next”:

Input in Page Attributes, then click on “Next”:

Choose default “Do not use tabs” in Tab Options step, then click on “Next”.

Input Map Attributes, choose Equirectangular for Map Projection, then click on “Next”:

Enter SQL Query, and give “no country data found” for When No Data Found Message :

SELECT NULL LINK, COUNTRY LABEL, PEOPLE Populations
FROM (
Select sum(employee_numbers) PEOPLE, COUNTRY FROM (
SELECT d.DEPARTMENT_ID,
d.DEPARTMENT_NAME,
(SELECT COUNT (*) FROM OEHR_EMPLOYEES WHERE DEPARTMENT_ID = d.DEPARTMENT_ID) employee_numbers,
c.COUNTRY_NAME COUNTRY
FROM OEHR_DEPARTMENTS d,
OEHR_EMPLOYEES e,
OEHR_LOCATIONS l,
OEHR_COUNTRIES c
WHERE d.LOCATION_ID = l.LOCATION_ID
AND l.COUNTRY_ID = c.COUNTRY_ID
AND d.DEPARTMENT_ID = e.DEPARTMENT_ID
AND d.MANAGER_ID = e.EMPLOYEE_ID
ORDER BY d.DEPARTMENT_ID
)
Group by COUNTRY
)

 

Just remind the number of employees by country as the following:

COUNTRY        PEOPLE
-------------- ------
United Kingdom     35
United States      68
Germany             1
Canada              2

 

On the Confirm step, click on “Finish”.

Run the page and you get this dynamic Map. There are two ManyCo employees in Canada, 35 employees in United Kingdom …

Fantastic!

These tutorial blogs only give a glimpse of APEX. APEX actually is a huge development platform. It has all the muscles but also with flexibility to expand easily. The features and potentials to explore in APEX is unbelievable. For example, AnyChart integration kit offers 3rd party plugin for APEX that let you create cuter and more sophisticated charts. AnyStock integration kit turns your real time data into highly sophisticated financial analysis charts with interactive capability and designed to display data and time based information.

For better and worse, APEX has introduced a new way to develop dynamic web-based application, a new way exploring and discovering data mined deeply in the back-end Oracle database. Hopefully, we would see more application developed by APEX in a rapid and quick fashion in strong future.

Related Content:

Step by step to create a dynamic 3D bar chart in APEX

Here I’m going to demo how to Step by step to create a dynamic 3D bar chart in APEX.

Remember Oracle has this an example of  Human Resources (HR) application for a fictitious company called ManyCo Corp?

Here is ManyCo departments page. Remind you ManyCo has several offices around world:

I select IT department, in employees page, five staff belong to IT listed. This time, we are going to particular interested in “Hire Date” field:

Now I’m going to create a chart page for ManyCo app, drawing a chart including employees hired by year.

Log on APEX, select ManyCo app, click on “Create Page” button and select “Chart”, then click on “Next”:

Choose “Flash Chart”, then click on “Next”:

Choose default “Column” chart type, then click on “Next”:

Choose default “3D Column”, then click on “Next”:

Input Page and Region Attributes (highlighted are not default value), then click on “Next”:

Choose default “Do not use tabs” in “Tab Options” step, then click on “Next”.

Input “Employee by Hired Year” in Chart Title field in “Chart Attributes” step, then click on “Next”:

In Query step, let’s add the following SQL Query:

SELECT NULL link,
hire_year VALUE,
no_of_employees_hired "No. of Employees by Hired Year"
FROM ( SELECT 'Year ' || TO_CHAR (emp.HIRE_DATE, 'YYYY') hire_year,
COUNT (emp.EMPLOYEE_ID) no_of_employees_hired
FROM OEHR_EMPLOYEES emp
GROUP BY TO_CHAR (emp.HIRE_DATE, 'YYYY')
ORDER BY hire_year )

and leave “No employee data found” for When No Data Found Message.

On Confirm step, click on “Finish”.

Run the page, and you can see dynamic 3D chart of “No. of Employees by Hired Year” created in a simple and rapid way:

Related Content:

Rapid application development with Oracle Application Express

When people were growing up in 1960’s and 1970’s, labour and time saving machines are propelling engine for productivity. So that you can see top-loading washing machine had replaced the front step, than came the television with remote control, which meant you no longer had to sit through 24 hours weather channel because you couldn’t be bothered get off your bums.

Life was very good.

But, it doesn’t much the same story in IT.

When database invented and became core repository storing business information, people in company’s IT department became mad loading tons and tons relevant and irrelevant data into it without considering the best way to utilise and mine them; and after WWW invented, so web based application was getting into the mainstream for lightweight and loose-coupling enterprise IT solution, and you got dot-com bubble and new bubble now change theirs name to web 2.0 and social network …

To developers, life is getting worse and worse. Automation in IT didn’t reduce developers work hours, but make them more overworked and underpaid.

Developers have to have a board knowledge base, mastering every technology aspect from client to server, craftmaning from business logic implementation to unit test developing.

There is an example. The requirement is to develop a web-based application, to let end-users browse, view, search and report some business information stored in back-end database.

First thought you would like “OK, it’s web app we should go with JSP, Struts for front-end; with back-end database, we could go with Hibernate, and use Spring to “glue” all these components together. Oh, you want to generate some ‘sexy’ report as well, we can use Open Source graphic tools GNU, erh …, something. Sorry,  I can’t remember the exact name”.

“Methodology, we will practice Agile. It’s so popular and vast majority now.  We  need bring a few testers on board as well”.

Now, it comes the title role cast in this blog – Oracle Application Express (Oracle APEX).

You might imagine that APEX has been spared all those above nonsense; after all, you only want to interface you can operate on a database,  that are meant to be a pure, clean, unfettered all integrated development platform you want. And there is no place for complexity in such things. Well, dream on, because APEX is a developer’s fantasy. Every component, parts can be tuned to let developer deliver something different, and quickly.

Want to give a try and play APEX if you are impatient, read my other blog – Step by step to install Oracle Application Express (APEX) 4.1.1

According to Oracle, Oracle Application Express is a rapid web application development tool for the Oracle database. Using only a web browser and limited programming experience, you can develop and deploy professional applications that are both fast and secure.

Basically, you don’t need have the knowledge of HTML, CSS, AJAX, ORM, MVC architecture, Java programming, but you still can develop an enterprise level application with APEX. Simply put, even a database administrator used to be reckoned as the last dinosaur going  to extinct in IT can program!

APEX Architectural Overview

APEX is not a mainstream 3-Tier web-based application. It is effectively a 2-Tier architecture browser talks through to APEX Engine which resides in the Oracle database.

APEX application is based on Page (dynamic). APEX Engine fetches data relating to the page, rendering to the HTML and sending back to browser. Between the request and response on each page, there are authentication and authorisation for user check. Each page is associated with a shared pooled database session which is for data relating validations, computations and processes.

The good thing is the thinner layer and simple architecture make APEX have less overhead of process consuming and network latency.

Another benefit from APEX is it can be easily integrated with other data source via web services and db links. That mean APEX is a Cloud friendly development platform.

APEX Integrated Applications

There are some other nice features built-in APEX:

  • Easily to build form based web application, rather than scratch from boilerplate of HTML components
  • Interactive Report. This is a big gun in APEX. End users can interactively generate dynamic report which enhance user capabilities and experience
  • Built-in XSL-SO engine. Report can be rendered as HTML as well as PDF format
  • Extensibility:  3rd party could build add-on plug-ins for APEX

In the next blog, I will give you an example, not the example from Sales people, but an example of APEX implementation in real world. Stay tuned …

Reference:

Step by step to install Oracle Application Express (APEX) 4.1.1

I’m now showing you a step by step instructions how to install Oracle Application Express (APEX) 4.1.1 on Windows 7. With help from Cygwin environment.

  • Download Oracle Application Express (APEX) version 4.1.1 and Listener from OTN website: http://www.oracle.com/technetwork/developer-tools/apex/downloads/index.html and http://www.oracle.com/technetwork/developer-tools/apex-listener/downloads/index.html
  • Run sqlplus and log on database as user SYS:

    sqlplus SYS/Welcome1@localhost:1521/OracleDB as sysdba

  • Create tablespace “APEX”:

    DROP TABLESPACE APEX INCLUDING CONTENTS AND DATAFILES;

    CREATE TABLESPACE APEX DATAFILE 'C:APPORADATAORACLEDBAPEX01.DBF' SIZE 256M AUTOEXTEND ON NEXT 16M MAXSIZE UNLIMITED
    LOGGING
    ONLINE
    PERMANENT
    EXTENT MANAGEMENT LOCAL AUTOALLOCATE
    BLOCKSIZE 8K
    SEGMENT SPACE MANAGEMENT AUTO
    FLASHBACK ON;

  • Assume you are under APEX package directory, then run the APEX installation sql script:

    SQL> @apexins APEX APEX01 TEMP /i/

    The installation will take a while. Time for a cup of tea till you see these on the screen:

    ...
    ...Compiled 701 out of 2896 objects considered, 0 failed compilation 14:01:03
    ...253 packages
    ...245 package bodies
    ...447 tables
    ...12 functions
    ...19 procedures
    ...3 sequences
    ...445 triggers
    ...1266 indexes
    ...196 views
    ...0 libraries
    ...6 types
    ...0 type bodies
    ...0 operators
    ...0 index types
    ...Begin key object existence check 14:01:03
    ...Completed key object existence check 14:01:03
    ...Setting DBMS Registry 14:01:03
    ...Setting DBMS Registry Complete 14:01:03
    ...Exiting validate 14:01:03
    timing for: Validate Installation
    Elapsed: 00:02:10.40
    timing for: Development Installation
    Elapsed: 00:15:59.51
    Disconnected from Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 - 64bit Production
    With the Partitioning, OLAP, Data Mining and Real Application Testing options

  • Unpack APEX Listener to e.g. C:appapex directory

    To start APEX (running under Windows 7 with Cygwin). First time when you start APEX you will be redirected to URL, e.g.:http://localhost:8585/apex/listenerConfigure to set up DB connection.

    C:appapexbin>java -Dapex.home=C:appapextmp -Dapex.images=C:appapeximages -Dapex.port=8585 -jar apex.war

    INFO: Starting: C:appapexbinapex.war
     See: 'java -jar apex.war --help' for full range of configuration options
    INFO: Extracting to: C:app/apex/tmpapex                                       INFO: Using classpath: file:/C:/app/apex/tmp/apex/apex/____embedded/start.jar:file:/C:/app/apex/tmp/apex/apex/WEB-INF/lib/apex.jar:file:/C:/app/apex/tmp/apex/apex/WEB-INF/lib/commons-fileupload-1.2.1.jar:file:/C:/app/apex/tmp/apex/apex/WEB-INF/lib/je-4.0.103.jar:file:/C:/app/apex/tmp/apex/apex/WEB-INF/lib/ojdbc6.jar:file:/C:/app/apex/tmp/apex/apex/WEB-INF/lib/ojmisc.jar:file:/C:/app/apex/tmp/apex/apex/WEB-INF/lib/poi-3.6-20091214.jar:file:/C:/app/apex/tmp/apex/apex/WEB-INF/lib/ucp.jar:file:/C:/app/apex/tmp/apex/apex/WEB-INF/lib/xdb-11.2.0.jar:file:/C:/app/apex/tmp/apex/apex/WEB-INF/lib/xmlparserv2-11.2.0.jar:
    INFO: Starting Embedded Web Container in: C:appapextmpapex
    Enter a username for the APEX Listener Administrator [adminlistener]:
    Enter a password for adminlistener:
    Confirm password for adminlistener:
    Enter a username for the APEX Listener Manager [managerlistener]:
    Enter a password for managerlistener:
    Confirm password for managerlistener:
    03/04/2012 12:32:49 PM ____bootstrap.Deployer deploy
    INFO: Will deploy application path=C:appapextmpapexapexWEB-INFweb.xml
    03/04/2012 12:32:49 PM ____bootstrap.Deployer deploy
    INFO: deployed application path=C:appapextmpapexapexWEB-INFweb.xml
    Using config file: C:appapextmpapexapex-config.xml
    APEX Listener version : 1.1.3.243.11.40
    APEX Listener server info: Grizzly/1.9.18-o
    03/04/2012 12:32:50 PM com.sun.grizzly.Controller logVersion
    INFO: Starting Grizzly Framework 1.9.18-o - Tue Apr 03 12:32:50 EST 2012
    INFO: Please complete configuration at: http://localhost:8585/apex/listenerConfigure
    Database connection not yet configured
    


    The following is screen output is the second time run after setup:

    INFO: Starting: C:appapexbinapex.war
     See: 'java -jar apex.war --help' for full range of configuration options
    INFO: Extracting to: C:appapextmp
    INFO: Using classpath: file:/C:/app/apex/tmp/apex/____embedded/start.jar:file:/C:/app/apex/tmp/apex/WEB-INF/lib/apex.jar:file:/C:/app/apex/tmp/apex/WEB-INF/lib/commons-fileupload-1.2.1.jar:file:/C:/app/apex/tmp/apex/WEB-INF/lib/je-4.0.103.jar:file:/C:/app/apex/tmp/apex/WEB-INF/lib/ojdbc6.jar:file:/C:/app/apex/tmp/apex/WEB-INF/lib/ojmisc.jar:file:/C:/app/apex/tmp/apex/WEB-INF/lib/poi-3.6-20091214.jar:file:/C:/app/apex/tmp/apex/WEB-INF/lib/ucp.jar:file:/C:/app/apex/tmp/apex/WEB-INF/lib/xdb-11.2.0.jar:file:/C:/app/apex/tmp/apex/WEB-INF/lib/xmlparserv2-11.2.0.jar:
    INFO: Starting Embedded Web Container in: C:appapextmp
    02/04/2012 4:23:14 PM ____bootstrap.Deployer deploy
    INFO: Will deploy application path=C:appapextmpapexWEB-INFweb.xml
    02/04/2012 4:23:14 PM ____bootstrap.Deployer deploy
    INFO: deployed application path=C:appapextmpapexWEB-INFweb.xml
    Using config file: C:appapextmpapex-config.xml
    -- listing properties --
    PropertyCheckInterval=60
    ValidateConnection=true
    MinLimit=1
    MaxLimit=10
    InitialLimit=3
    AbandonedConnectionTimeout=900
    MaxStatementsLimit=10
    InactivityTimeout=1800
    MaxConnectionReuseCount=1000
    APEX Listener version : 1.1.3.243.11.40
    APEX Listener server info: Grizzly/1.9.18-o
    02/04/2012 4:23:23 PM com.sun.grizzly.Controller logVersion
    INFO: Starting Grizzly Framework 1.9.18-o - Mon Apr 02 16:23:23 EST 2012
    INFO: http://localhost:8585/apex started.
    Using JDBC driver: Oracle JDBC driver version: 11.2.0.2.0
    

  • APEX Developer Login interface:

  • APEX Admin Login interface:

Related Content: