Showing posts with label annotations. Show all posts
Showing posts with label annotations. Show all posts

Tuesday, December 15, 2009

Logging using Aspect Oriented Programming

Most of the time while debugging I like to see the values of the incoming parameters and what is the returned value. The only solution is to use Eclipse debugger but some times its always not possible to run eclipse debugger. For example, the application is running in some environment where you cannot connect or sometimes you want to check quickly by just switching on DEBUG level logging.

Today I used Aspect Oriented Programming (AOP) to log incoming parameters and return value of a method execution. There are multiple ways but I like the following approach -

Ensure that required log4j and aspect jars are in the classpath of your project. The log4j is properly configured and gets initialized. If don't want to use log4j then in the MethodLogger class replace the log4j related code with whatever you require.

First create the following annotation which will be used against those methods for which logging is required -


public @interface LogMethod {
}


Now create following Aspect class in your source code. The comments explain the logic.


import org.apache.log4j.Logger;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

// The Aspect annotation makes this class an Aspect
@Aspect
public class MethodLogger {

// The Before annotation ensures beforeMethod is called before the
// execution of a method annotated by LogMethod.
@Before(value = "@annotation(LogMethod)")
public void beforeMethod(JoinPoint joinPoint) throws Throwable {
// This ensures that the logger is of the actual class. If %c
// pattern is used in log4j then the class name of the actual
// method will print.
Logger logger = Logger.getLogger(joinPoint.getTarget().getClass());
if (logger.isDebugEnabled()) {
StringBuilder builder = new StringBuilder();
// Here we print the name of the method
builder.append(joinPoint.getSignature().getName());
builder.append("(");
// Here we print the values of the method arguments
appendArgumentValues(builder, joinPoint.getArgs());
builder.append(")");

logger.debug(builder.toString());
}
}

// The AfterReturning annotation ensures afterReturningMethod
// is after the execution of a method annotated by LogMethod.
@AfterReturning(value = "@annotation(LogMethod)", returning = "returnValue")
public void afterReturningMethod(JoinPoint joinPoint, Object returnValue)
throws Throwable {
Logger logger = Logger.getLogger(joinPoint.getTarget().getClass());
if (logger.isDebugEnabled()) {
StringBuilder builder = new StringBuilder();
builder.append(joinPoint.getSignature().getName());
builder.append("(");
// This prints the method argument class type
appendArguments(builder, joinPoint.getArgs());
builder.append("):");
// This prints the return value of the method. Incase of void
// it prints null
builder.append(returnValue);

logger.debug(builder.toString());
}
}

private void appendArguments(StringBuilder builder, Object[] objects) {
for (Object obj : objects) {
builder.append(obj.getClass().getName()).append(",");
}

if (objects.length > 0) {
builder.deleteCharAt(builder.length() - 1);
}
}

private void appendArgumentValues(StringBuilder builder, Object[] objects) {
for (Object obj : objects) {
builder.append(obj).append(",");
}

if (objects.length > 0) {
builder.deleteCharAt(builder.length() - 1);
}
}
}

Monday, June 15, 2009

Hibernate OneToMany annotation mapping using JoinTable

Hibernate OneToMany annotation mapping using JoinTable

Recently I explored Hibernate OneToMany annotation mapping using JoinTable.

Consider an Airline entity having list of Airports. The mapping between the two is stored in a JoinTable airline_airports.

CREATE TABLE airline(
code char(2) PRIMARY KEY NOT NULL,
name varchar(50) NOT NULL
);

CREATE TABLE airport(
code char(3) PRIMARY KEY NOT NULL,
name varchar(50) NOT NULL
);

CREATE TABLE airline_airport(
airline_code char(2) NOT NULL,
airport_code char(3) NOT NULL,
CONSTRAINT PRIMARY PRIMARY KEY (airline_code,airport_code)
);

ALTER TABLE airline_airport ADD CONSTRAINT airline_airport_ibfk_1
FOREIGN KEY (airline_code) REFERENCES airline(code) ON DELETE NO ACTION ON UPDATE NO ACTION;

ALTER TABLE airline_airport ADD CONSTRAINT airline_airport_ibfk_2
FOREIGN KEY (airport_code) REFERENCES airport(code) ON DELETE NO ACTION ON UPDATE NO ACTION;

First of all we need to create Hibernate Entities for Airline and Airport using annotations. Notice the annotations defined for airports attribute of Airline class. This is where we are defining the relationship between Airline and Airport.

@Entity
@Table(name = "airline")
public class Airline implements Serializable {
@Id
@Column(name = "code")
private String code;
@OneToMany(fetch=FetchType.EAGER, cascade=CascadeType.ALL)
@JoinTable(name = "airline_airport", joinColumns = @JoinColumn(name = "airline_code")
, inverseJoinColumns = @JoinColumn(name = "airport_code")
)
private List airports;

public String getCode() {
return code;
}

public void setCode(String code) {
this.code = code;
}

public List getAirports() {
return airports;
}

public void setAirports(List airports) {
this.airports = airports;
}
}

@Entity
@Table(name = "airport")
public class Airport implements Serializable {
@Id
@Column(name = "code")
private String code;
@Column(name = "name")
private String name;

public String getCode() {
return code;
}

public void setCode(String code) {
this.code = code;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}
}

Create an Airline object and add a list of Airlines. When you will save the airline object, hibernate will persist all the data in airline, airport and airline_airport table.

If you don't use the cascade annotation then ii will not save the data in airport table. This means it will fail to insert record in airline_airport table if an airport is not already in the airport table.

Thursday, May 21, 2009

Apache cxf using annotations and integration with Spring

Today I will show you how you can create a webservice using Apache CXF version 2.2.1. I will be building this webservice on top of my previous application.

Create a interface AirlineManager to expose methods over webservice. The create method takes airline code and name to create a Airline record in database. Annotation @WebService tells that this is a interface for webservice.

package com.travel;

import javax.jws.WebService;

@WebService
public interface AirlineManager {
public void create(String code, String name);
}

Implement the above webservice interface. We are using AirlineDao created in earlier post. Notice the @WebService(endpointInterface = "com.travel.AirlineManager") which tells that this implementation is for AirlineManager interface.

package com.travel;

import com.travel.dao.AirlineDao;
import javax.jws.WebService;

@WebService(endpointInterface = "com.travel.AirlineManager")
public class AirlineManagerImpl implements AirlineManager {
private AirlineDao airlineDao;

public AirlineManagerImpl(AirlineDao aAirlineDao) {
airlineDao = aAirlineDao;
}

@Override
public void create(String code, String name) {
airlineDao.create(code, name);
}
}

Add this in the spring-context.xml. Here we are importing some cxf beans, defining bean AirlineManagerImpl and creating airlineManagerService webservice.

<import resource="classpath:META-INF/cxf/cxf.xml" />
<import resource="classpath:META-INF/cxf/cxf-extension-soap.xml" />
<import resource="classpath:META-INF/cxf/cxf-extension-http-jetty.xml"/>
<import resource="classpath:META-INF/cxf/cxf-servlet.xml" />

<bean id="airlineManager" class="com.kaleconsultants.travel.AirlineManagerImpl">
<constructor-arg><ref bean="airlineDao"/></constructor-arg>
</bean>

<jaxws:endpoint id="airlineManagerService" implementor="#airlineManager" address="/AirlineManagerService" />

Add this in your web.xml.

<servlet>
<servlet-name>CXFServlet</servlet-name>
<display-name>CXF Servlet</display-name>
<servlet-class>
org.apache.cxf.transport.servlet.CXFServlet
</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
<servlet-name>CXFServlet</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>

Deploy the application in tomcat and your webservice is ready. You can check the wsdl http://host:port/travel/AirlineManagerService?wsdl.

To test the webservice, you need to create a client. You can use wsdl2java available in the bin directory of cxf.

Execute this command, this will generate the wsdl stubs.
wsdl2java http://host:port/travel/AirlineManagerService?wsdl

Write a client like this and there you go.......

package com.travel;

public class ClientTest {
public static void main(String[] args) {
AirlineManagerImplService service = new AirlineManagerImplService();
AirlineManager airlineManager = service.getAirlineManagerImplPort();
airlineManager.create("IC", "Indian");
}
}

Wednesday, May 20, 2009

Hibernate integration with Spring

In my last post Basic Hibernate Annotations example, I wrote about a simple hibernate example. Today I will take that example forward and show you how you can integrate hibernate with Spring. I used Spring version 2.5.6.SEC01.

Create spring-context.xml file. We define a spring datasource, hibernate session factory, hibernate template(spring wrapper api of hibernate session), transaction manager. Bean airlineDao is configured to use annotation based transaction handling. The "<tx:annotation-driven/>" actually enables Spring container to execute methods under transaction, if transaction annotations are used.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">

<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName" value="java:comp/env/jdbc/travel"/>
<property name="resourceRef" value="true" />
</bean>

<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource"><ref bean="dataSource"/></property>
<property name="configLocation"><value>classpath:hibernate.cfg.xml</value></property>
<property name="configurationClass"><value>org.hibernate.cfg.AnnotationConfiguration</value></property>
</bean>

<bean id="hibernateTemplate" class="org.springframework.orm.hibernate3.HibernateTemplate">
<property name="sessionFactory"><ref bean="sessionFactory"/></property>
</bean>

<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory"><ref bean="sessionFactory"/></property>
</bean>

<tx:annotation-driven/>

<bean id="airlineDao" class="com.travel.dao.AirlineDao">
<property name="hibernateTemplate"><ref bean="hibernateTemplate"/></property>
</bean>
</beans>

Create AirlineDao class. Notice the @Transactional (readOnly = true) annotation which brings the execution of this class under transaction. The create method have (readOnly = false) because in this method we are actually updating the table.

package com.travel.dao;

import com.travel.domain.Airline;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;

@Transactional (readOnly = true)
public class AirlineDao extends HibernateDaoSupport {
@Override
@Transactional (readOnly = false)
public void create(String code, String name) {
Airline airline = new Airline();
airline.setCode(code);
airline.setName(name);
getHibernateTemplate().save(airline);
}
}

Add the below xml in your web.xml. This Spring ContextLoaderListener initializes the spring beans defined in file spring-context.xml (we created above).

<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/classes/spring-context.xml</param-value>
</context-param>

<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

Create a simple servlet to test the DAO. The execution of this servlet will create a new airline record in airline table.

package com.travel.servlet;

import com.kaleconsultants.travel.dao.AirlineDao;
import org.springframework.context.ApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class AirlineServlet extends HttpServlet {
private static final long serialVersionUID = 2409603422056073641L;

protected void service(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {

String code = request.getParameter("code");
String name = request.getParameter("name");
ApplicationContext context = WebApplicationContextUtils.getRequiredWebApplicationContext(getServletContext());
AirlineDao airlineDao = (AirlineDao) context.getBean("airlineDao");
airlineDao.create(code, name);
}
}

Monday, May 18, 2009

Basic Hibernate Annotations example

As part of my POC, today I developed a simple hibernate annotations based example. To run this example, you need JDK 5.0 or higher and hibernate 3.2.0.GA or above. I am using JDK 1.6, MySql 5.1, Tomcat 6.0.18 and Hibernate 3.3.1.GA.

Create hibernate.cfg.xml file. This file should be available in the classpath at runtime. I developed this example as part of a web application and I am using a datasource defined in my container. See my earlier post on How to configure a datasource in Tomcat


<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="connection.datasource">java:comp/env/jdbc/travel</property>
<property name="dialect">org.hibernate.dialect.MySQLDialect</property>
<property name="current_session_context_class">thread</property>
<property name="show_sql">true</property>
<mapping class="com.travel.domain.Airline"/>
</session-factory>
</hibernate-configuration>


This example needs following table in your database.


CREATE TABLE airline (
code char(2) PRIMARY KEY,
name varchar(50) NOT NULL,
created timestamp DEFAULT NOW() NOT NULL
);


Create HibernateUtil class which provides Hibernate session to interact with database. new AnnotationConfiguration().configure() looks for hibernate.cfg.xml in the classpath, so make sure to deploy the file in WEB-INF/classes directory.


package com.travel.hibernate.util;

import org.hibernate.SessionFactory;
import org.hibernate.cfg.AnnotationConfiguration;

public class HibernateUtil {
private static final SessionFactory sessionFactory;

static {
try {
sessionFactory = new AnnotationConfiguration().configure()
.buildSessionFactory();
} catch (Throwable ex) {
System.err.println("Initial SessionFactory creation failed." + ex);
throw new ExceptionInInitializerError(ex);
}
}

public static SessionFactory getSessionFactory() {
return sessionFactory;
}
}


Airline class maps with the airline table we created above. Notice the annotations used to define the mapping.


package com.travel.domain;

import java.io.Serializable;
import java.util.Date;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name = "airline")
public class Airline implements Serializable {
private static final long serialVersionUID = 6864693643526282786L;
@Id
@Column(name = "code")
private String code;
@Column(name = "name")
private String name;
@Column(name = "created")
private Date createdTime;

public String getCode() {
return code;
}

public void setCode(String code) {
this.code = code;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public Date getCreatedTime() {
return createdTime;
}
}


Simple servlet to test the application. I used the hibernate code directly in my servlet for simplicity. You should define one DAO layer to encapsulate the hibernate code. To test acccess your servelt use url like http://host:port/travel/AirlineServlet?code=LH&name=Lufthansa


package com.travel.servlet;

import com.travel.domain.Airline;
import com.travel.hibernate.util.HibernateUtil;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class AirlineServlet extends HttpServlet {
private static final long serialVersionUID = 2409603422056073641L;

protected void service(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
SessionFactory session = HibernateUtil.getSessionFactory();
Session sess = session.getCurrentSession();
Transaction tx = sess.beginTransaction();
// Get the airline code and name
String airlineCode = request.getParameter("code");
String airlineName = request.getParameter("name");
// Create Airline object
Airline airline = new Airline();
airline.setCode(airlineCode);
airline.setName(airlineName);
// Save the record in database
sess.save(airline);
tx.commit();
session.close();
}
}