Google

Feb 21, 2012

Java Web Services Interview Questions and Answers: SOAP clients

Q. How would you go about generating a SOAP Web service client in Java?
A. There are number of IDE based and other tools to achive this. The steps involved include

1. Get hold of the WSDL file for the service.
2. Generate the client Java code using the relevant IDE or maven plugin like jaxws-maven-plugin.
3. Use the generated code within your client Java file to invoke the service. Read the server details like host name and port-number from an environment based configuration file. The URL specified in the WSDL is hard-coded just for the contract sake.
The WSDL file can be drawn as shown below.




The sample WSDL file is OrderServices.WS_getOrdersForAccount.wsdl. This WSDL file could come from a Web service written in any language like C#, Java, etc or an enterprise Service bus like Tibco, Oracle Service Bus, web Methods, etc.


<?xml version="1.0" encoding="UTF-8"?>
<wsdl:definitions name="getOrdersForAccountWS" targetNamespace="http://server123/OrderExecutionServices.OrderServices.PublicServices:getOrdersForAccountWS" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:http="http://schemas.xmlsoap.org/wsdl/http/" xmlns:soapjms="http://www.w3.org/2010/soapjms/" xmlns:tns="http://server123/OrderExecutionServices.OrderServices.PublicServices:getOrdersForAccountWS" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/">
  <wsdl:types>
    <xsd:schema targetNamespace="http://server123/OrderExecutionServices.OrderServices.PublicServices:getOrdersForAccountWS" xmlns:tns="http://server123/OrderExecutionServices.OrderServices.PublicServices:getOrdersForAccountWS" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
          <xsd:element name="getOrdersForAccount" type="tns:getOrdersForAccount"/>
          <xsd:element name="getOrdersForAccountResponse" type="tns:getOrdersForAccountResponse"/>
          <xsd:complexType name="getOrdersForAccount">
            <xsd:sequence>
              <xsd:element name="accountNo" type="xsd:string"/>
              <xsd:element name="fromDate" type="xsd:string"/>
              <xsd:element name="toDate" type="xsd:string"/>
              <xsd:element name="minRow" type="xsd:string"/>
              <xsd:element name="maxRow" type="xsd:string"/>
            </xsd:sequence>
          </xsd:complexType>
          <xsd:complexType name="getOrdersForAccountResponse">
            <xsd:sequence>
              <xsd:element maxOccurs="unbounded" name="OrderSummaries" nillable="true" type="tns:OrderSummary"/>
            </xsd:sequence>

          </xsd:complexType>
          <xsd:complexType name="OrderSummary">
            <xsd:sequence>
              <xsd:element name="orderNo" nillable="true" type="xsd:string"/>
              <xsd:element name="description" nillable="true" type="xsd:string"/>
              <xsd:element name="orderDate" nillable="true" type="xsd:string"/>
              <xsd:element name="orderStatus" nillable="true" type="xsd:string"/>
              <xsd:element name="unitsValue" nillable="true" type="xsd:string"/>
              <xsd:element name="nettValue" nillable="true" type="xsd:string"/>
            </xsd:sequence>
          </xsd:complexType>
     </xsd:schema>
  </wsdl:types>
  <wsdl:message name="getOrdersForAccountWS_PortType_getOrdersForAccountResponse">

    <wsdl:part name="parameters" element="tns:getOrdersForAccountResponse">
    </wsdl:part>
  </wsdl:message>
  <wsdl:message name="getOrdersForAccountWS_PortType_getOrdersForAccount">
    <wsdl:part name="parameters" element="tns:getOrdersForAccount">
    </wsdl:part>
  </wsdl:message>
  <wsdl:portType name="getOrdersForAccountWS_PortType">
    <wsdl:operation name="getOrdersForAccount">

      <wsdl:input message="tns:getOrdersForAccountWS_PortType_getOrdersForAccount">
    </wsdl:input>
      <wsdl:output message="tns:getOrdersForAccountWS_PortType_getOrdersForAccountResponse">
    </wsdl:output>
    </wsdl:operation>
  </wsdl:portType>
  <wsdl:binding name="OrderExecutionServices_OrderServices_PublicServices_getOrdersForAccountWS_Binder" type="tns:getOrdersForAccountWS_PortType">
    <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
    <wsdl:operation name="getOrdersForAccount">

      <soap:operation soapAction="OrderExecutionServices_OrderServices_PublicServices_getOrdersForAccountWS_Binder_getOrdersForAccount" style="document"/>
      <wsdl:input>
        <soap:body parts="parameters" use="literal"/>
      </wsdl:input>
      <wsdl:output>
        <soap:body parts="parameters" use="literal"/>
      </wsdl:output>
    </wsdl:operation>
  </wsdl:binding>

  <wsdl:service name="OrderExecutionServices.OrderServices.PublicServices.getOrdersForAccountWS">
    <wsdl:port name="OrderExecutionServices_OrderServices_PublicServices_getOrdersForAccountWS_Port" binding="tns:OrderExecutionServices_OrderServices_PublicServices_getOrdersForAccountWS_Binder">
      <soap:address location="http://server123:7555/ws/OrderExecutionServices.OrderServices.PublicServices.getOrdersForAccountWS/OrderExecutionServices_OrderServices_PublicServices_getOrdersForAccountWS_Port"/>
    </wsdl:port>
  </wsdl:service>
</wsdl:definitions>




Ensure that you have required jar files required for jaxws-rt and jaxws-tools. It depends on the version of Java and Maven you are using.


The jaxws-rt.jar is required if you are using Java version 5. In Maven pom.xml, you will require the following dependnecy if using Java 5. The Java 6 comes with the relevant libraries within rt.jar under javax.xml.ws package.

<dependencies>
 <dependency>
  <groupId>com.sun.xml.ws</groupId>
  <artifactId>jaxws-rt</artifactId>
  <version>2.2</version>
 </dependency>
</dependencies>


If your Maven does not have the jaxws-tools included, then you will require the following dependency jar for the jaxws-maven-plugin.


<dependencies>
 <groupId>com.sun.xml.ws</groupId>
 <artifactId>jaxws-tools</artifactId>
 <version>2.2.1</version>
</dependencies>



Configure the Maven pom.xml file to generate the relevant code to invoke the Web service with the "jaxws-maven-plugin".

<build>
        <plugins>
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>jaxws-maven-plugin</artifactId>
                <version>1.10</version>
                <executions>
                    <execution>
                        <id>compile-orderservice-wsdl</id>
                        <goals>
                          <goal>wsimport</goal>
                        </goals>
                        <configuration>
                            <sourceDestDir>${basedir}/src/main/java</sourceDestDir>
                            <wsdlUrls>
                                <wsdlUrl>${basedir}/src/main/resources/wsdl/OrderServices.WS_getOrdersForAccount.wsdl</wsdlUrl>
                            </wsdlUrls>
                            <packageName>com.myapp.service.ws</packageName>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
            
   
   ....
   
</build>


Finally the client class that makes use of the generated classes and the JAXWS classes like QName and BindingProvider.


package com.myapp.ws.client;

import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;

import javax.xml.namespace.QName;
import javax.xml.ws.BindingProvider;

import org.apache.log4j.Logger;

//...other imports

public class OrderServiceImpl implements OrderService {

    private static final Logger LOG = Logger.getLogger(OrderServiceImpl.class);
 
    private static final String PACKAGE_NAME = "OrderExecutionServices.OrderServices.PublicServices";
 private static final String SERVICE_NAME = PACKAGE_NAME + ".getOrdersForAccountWS";
 private static final String NAMESPACE_URI = "http://server123/OrderExecutionServices.OrderServices.PublicServices:getOrdersForAccountWS"
  
   
    @Override
    public List<order> getOrder(OrderQuery query) throws OrderServiceException {
        LOG.trace("getOrdersForAccount accountNumber=" + query.getAccountNumber());
  //The WSDL service element has the sample endpoint location. Here provide the actual location
  //by reading the <hostname>:<portno> from a config file
        URL url = toUrl("http://myserver:5555/" + "ws/" + SERVICE_NAME +  + "?WSDL");
  // it takes namespaceURI and the localPart as the arguments
        QName qName = new QName(NAMESPACE_URI, SERVICE_NAME);
  //OrderExecutionServicesOrderServicesPublicServicesGetOrderForAccountWS is a generated class from the WSDL using wsimport
        OrderExecutionServicesOrderServicesPublicServicesGetOrderForAccountWS service =
            new  OrderExecutionServicesOrderServicesPublicServicesGetOrderForAccountWS(url, qName);
  //GetOrderForAccountWSPortType is a generated class from the WSDL using wsimport
        GetOrdersForAccountWSPortType port = service.getOrderExecutionServicesOrderServicesPublicServicesGetOrderForAccountWSPort();
        configurePort(port);

   // The binding element in the WSDL defines the operation name as "getOrdersForAccount" 
        List<order> listOfOrders = port.getOrderForAccount(
                query.getAccountNumber(),
                query.getFromDate(),
                queryl.getToDate(),
                query.getStartRow(),
                query.getStartRow() + query.getPageSize());
       
        return listOfOrders;
    }

   

    private void configurePort(Object port) {
        if (port instanceof BindingProvider) {
            BindingProvider bindingProvider = BindingProvider.class.cast(port);
            Map<string, Object> requestContext = bindingProvider.getRequestContext();
            requestContext.put(BindingProvider.USERNAME_PROPERTY, "john"); //should be read from a properties file
            requestContext.put(BindingProvider.PASSWORD_PROPERTY, "");     //should be read from a properties file
            requestContext.put("com.sun.xml.ws.connect.timeout", 1000L);   //should be read from a properties file 
            requestContext.put("com.sun.xml.ws.request.timeout", 20L);     //should be read from a properties file in encrypted format and then decrypted
        } else {
            throw new RuntimeException("Expected " + port + " to be of " + BindingProvider.class);
        }
    }

    private static URL toUrl(String url) {
        try {
            return new URL(url);
        } catch (MalformedURLException e) {
            throw new RuntimeException(e);
        }
    }

}



Q. How would you generate a sample SOAP payload from the WSDL
A.

1. Open the soapUI tool. If not already installed, download the free version and install it.
2. Create a new soapUI project by clicking File --> New soapUI Project.
3. In the pop-up enter a project name and where it says initial WSDL/WADL point your WSDL file. It could be a local file or a URL pointing to your WSDL.For example, OrderServices.WS_getOrdersForAccount.wsdl.

http://server123:7555/ws/OrderExecutionServices.OrderServices.PublicServices.getOrdersForAccountWS/OrderExecutionServices_OrderServices_PublicServices_getOrdersForAccountWS_Port?wsdl


4. Say okay and yiu will have a soapUI project created with a sample request contang the SOAP payload under the operation "getOrdersForAccount" subfolder. The payload will look like

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ord="http://server123/OrderExecutionServices.OrderServices.PublicServices:getOrdersForAccountWS">
   <soapenv:Header/>
   <soapenv:Body>
      <ord:getOrdersForAccount>
         <accountNo>?</accountNo>
         <fromDate>?</fromDate>
         <toDate>?</toDate>
         <minRow>?</minRow>
         <maxRow>?</maxRow>
      </ord:getOrdersForAccount>
   </soapenv:Body>
</soapenv:Envelope>


Fill in the "?"s and configure the actual endpoint details, etc you will be able to test the actual service from the soapUI tool.

Labels:

Feb 14, 2012

Web Services Interview Questions and Answers - RESTful Web Service Overview



Q. Why would you use a RESTful Web service?
A. RESTful Web service is easy, straightforward, supports multiple data formats like XML, JSON, etc and easily testable. For example,

It can be tested by

1. Directly invoking the service  from the browser by typing a URL if the RESTFul service supports GET request with query parameters. For example,

http://localhost:8080/executionservices/execution/1.0/order/create?accountId=123&qty=25 

2. You could use a Firefox plugin like "poster" to post an XML request to your service.

3. You could write a Web Service client to consume the Web service from a test class or a separate application client. You could use libraries like HttpClient, CXF Client, URLConnection, etc to connect to the RESTful service.


Q. What are the various implementations of JAX-RS available to choose from in Java?
A. When you're developing with REST in Java, you have a lot of options to choose from in terms of the frameworks. There's Jersey, the reference implementation from Oracle, then you have RestEasy, the JBoss choice, and there is CXF, the Apache choice.


Q. How would you go about implemnting the RESTful Web service using the framework of your choice?
A. Let's look at CXF as it is easy to configure. The steps involved include

  • Bringing in the relevant framework jar files for Spring and CXF. For example, via Maven using a pom.xml file.
  • If the request and responses are XML based POST, then define an XSD file and generate JAXB annotated classes for marshalling and unmarshalling the request and response.
  • Define the WebService interface and implementation classes.
  • Define any interceptor and filter classes if required to intercept the request and responses for implementing the cross-cutting concerns like security validation, logging, auditing, setting up or initializing the locale, etc
  • Wire up the classes via Spring and CXF config files. For example, webservices-config.xml and cxf.xml.
  • Finally define the CXF servlet and bootstrap the relevant config files via the web.xml file.



Firstly, configure the maven pom.xml file to include the relevant jar files.


<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>3.0.5.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-web</artifactId>
    <version>3.0.5.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.apache.cxf</groupId>
    <artifactId>cxf-rt-frontend-jaxrs</artifactId>
    <version>2.2.3</version>
</dependency>


Define the request and response objects with JAXB annotations as our Web service payload is XML, hence it needs to be marshalled (i.e. convert from object to XML) and unmarshalled (XML to object). OrderRequest & OrderResponse classes can be generated from XSD file OrderRequestResponde.xsd shown below.

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<xs:schema version="1.0" xmlns:xs="http://www.w3.org/2001/XMLSchema">

   <xs:complexType name="OrderResponse">
      <xs:sequence>
         <xs:element name="orderId" type="xs:string" minOccurs="0" />
         <xs:element name="responseCode" type="responseCode" minOccurs="0" />
         <xs:element name="accountId" type="xs:string" minOccurs="0" />
      </xs:sequence>
   </xs:complexType>

   <xs:complexType name="OrderRequest">
      <xs:sequence>
         <xs:element name="accountId" type="xs:string" minOccurs="0" />
         <xs:element name="quantity" type="xs:int" minOccurs="0" />
      </xs:sequence>
   </xs:complexType>

   <xs:simpleType name="responseCode">
      <xs:restriction base="xs:string">
         <xs:enumeration value="SUCCESS" />
         <xs:enumeration value="FAILED" />
         <xs:enumeration value="REJECTED" />
         <xs:enumeration value="UNKNOWN" />
      </xs:restriction>
   </xs:simpleType>

</xs:schema>


The .java files can be generated via the xjc command to generate JAXB annotated classes. The OrderRequest.java, OrderResponse.java, ResponseCode.java and ObjectFactory.java files are generated under the package "com.myapp.data.order".


xjc -d "c:\temp" -p "com.myapp.data.order" "C:\temp\xsd\OrderRequestResponde.xsd" -extension

Here are the generated files:

OrderRequest.java

//
// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, vJAXB 2.1.3 in JDK 1.6 
// See http://java.sun.com/xml/jaxb 
// Any modifications to this file will be lost upon recompilation of the source schema. 
// Generated on: 2012.02.07 at 05:07:31 PM EST 
//


package com.myapp.data.order;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlType;


/**
 * Java class for OrderRequest complex type.
 * 
 * 
The following schema fragment specifies the expected content contained within this class.
 * 
 * 
* <complexType name="OrderRequest">
 *   <complexContent>
 *     <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
 *       <sequence>
 *         <element name="accountId" type="{http://www.w3.org/2001/XMLSchema}string" minOccurs="0"/>
 *         <element name="quantity" type="{http://www.w3.org/2001/XMLSchema}int" minOccurs="0"/>
 *       </sequence>
 *     </restriction>
 *   </complexContent>
 * </complexType>
 * 
* * */ @XmlAccessorType(XmlAccessType.FIELD) @XmlType(name = "OrderRequest", propOrder = { "accountId", "quantity" }) public class OrderRequest { protected String accountId; protected Integer quantity; /** * Gets the value of the accountId property. * * @return * possible object is * {@link String } * */ public String getAccountId() { return accountId; } /** * Sets the value of the accountId property. * * @param value * allowed object is * {@link String } * */ public void setAccountId(String value) { this.accountId = value; } /** * Gets the value of the quantity property. * * @return * possible object is * {@link Integer } * */ public Integer getQuantity() { return quantity; } /** * Sets the value of the quantity property. * * @param value * allowed object is * {@link Integer } * */ public void setQuantity(Integer value) { this.quantity = value; } }


OrderResponse.java


//
// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, vJAXB 2.1.3 in JDK 1.6 
// See http://java.sun.com/xml/jaxb 
// Any modifications to this file will be lost upon recompilation of the source schema. 
// Generated on: 2012.02.07 at 05:07:31 PM EST 
//


package com.myapp.data.order;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlType;


/**
 * Java class for OrderResponse complex type.
 * 
 * 
The following schema fragment specifies the expected content contained within this class.
 * 
 * 
* <complexType name="OrderResponse">
 *   <complexContent>
 *     <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
 *       <sequence>
 *         <element name="orderId" type="{http://www.w3.org/2001/XMLSchema}string" minOccurs="0"/>
 *         <element name="responseCode" type="{}responseCode" minOccurs="0"/>
 *         <element name="accountId" type="{http://www.w3.org/2001/XMLSchema}string" minOccurs="0"/>
 *       </sequence>
 *     </restriction>
 *   </complexContent>
 * </complexType>
 * 
* * */ @XmlAccessorType(XmlAccessType.FIELD) @XmlType(name = "OrderResponse", propOrder = { "orderId", "responseCode", "accountId" }) public class OrderResponse { protected String orderId; protected ResponseCode responseCode; protected String accountId; /** * Gets the value of the orderId property. * * @return * possible object is * {@link String } * */ public String getOrderId() { return orderId; } /** * Sets the value of the orderId property. * * @param value * allowed object is * {@link String } * */ public void setOrderId(String value) { this.orderId = value; } /** * Gets the value of the responseCode property. * * @return * possible object is * {@link ResponseCode } * */ public ResponseCode getResponseCode() { return responseCode; } /** * Sets the value of the responseCode property. * * @param value * allowed object is * {@link ResponseCode } * */ public void setResponseCode(ResponseCode value) { this.responseCode = value; } /** * Gets the value of the accountId property. * * @return * possible object is * {@link String } * */ public String getAccountId() { return accountId; } /** * Sets the value of the accountId property. * * @param value * allowed object is * {@link String } * */ public void setAccountId(String value) { this.accountId = value; } }




ResponseCode.java


//
// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, vJAXB 2.1.3 in JDK 1.6 
// See http://java.sun.com/xml/jaxb 
// Any modifications to this file will be lost upon recompilation of the source schema. 
// Generated on: 2012.02.07 at 05:07:31 PM EST 
//


package com.myapp.data.order;

import javax.xml.bind.annotation.XmlEnum;
import javax.xml.bind.annotation.XmlType;


/**
 * Java class for responseCode.
 * 
 * 
The following schema fragment specifies the expected content contained within this class.
 * 
* 
* <simpleType name="responseCode">
 *   <restriction base="{http://www.w3.org/2001/XMLSchema}string">
 *     <enumeration value="SUCCESS"/>
 *     <enumeration value="FAILED"/>
 *     <enumeration value="REJECTED"/>
 *     <enumeration value="UNKNOWN"/>
 *   </restriction>
 * </simpleType>
 * 
* */ @XmlType(name = "responseCode") @XmlEnum public enum ResponseCode { SUCCESS, FAILED, REJECTED, UNKNOWN; public String value() { return name(); } public static ResponseCode fromValue(String v) { return valueOf(v); } }



The ObjectFactory.java


//
// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, vJAXB 2.1.3 in JDK 1.6 
// See http://java.sun.com/xml/jaxb 
// Any modifications to this file will be lost upon recompilation of the source schema. 
// Generated on: 2012.02.07 at 05:07:31 PM EST 
//


package com.myapp.data.order;

import javax.xml.bind.annotation.XmlRegistry;


/**
 * This object contains factory methods for each 
 * Java content interface and Java element interface 
 * generated in the com.myapp.data.order package. 
 * An ObjectFactory allows you to programatically 
 * construct new instances of the Java representation 
 * for XML content. The Java representation of XML 
 * content can consist of schema derived interfaces 
 * and classes representing the binding of schema 
 * type definitions, element declarations and model 
 * groups.  Factory methods for each of these are 
 * provided in this class.
 * 
 */
@XmlRegistry
public class ObjectFactory {


    /**
     * Create a new ObjectFactory that can be used to create new instances of schema derived classes for package: com.myapp.data.order
     * 
     */
    public ObjectFactory() {
    }

    /**
     * Create an instance of {@link OrderResponse }
     * 
     */
    public OrderResponse createOrderResponse() {
        return new OrderResponse();
    }

    /**
     * Create an instance of {@link OrderRequest }
     * 
     */
    public OrderRequest createOrderRequest() {
        return new OrderRequest();
    }

}



Define the Web service interface ExecutionWebService.java.

package com.myapp.webservice.orderexecutionservices;


import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;

import  com.myapp.data.order.*;

@Path("execution/1.0")
public interface ExecutionWebService {


    // -------------------------
    // Create Order
    // -------------------------
     
    @POST
    @Path("/order/create")
    @Consumes("application/xml") 
    @Produces("text/xml")
    OrderResponse createOrder(OrderRequest orderRequest);
    
    // -------------------------
    // Amend Order
    // -------------------------
    
    @POST
    @Path("/order/amend")
    @Consumes("application/xml")
    @Produces("text/xml")
    OrderResponse amendOrder(OrderRequest orderRequest);
    
    //--------------------------
    // Cancel Order
    //--------------------------
    
    @POST
    @Path("/order/cancel")
    @Consumes("application/xml")
    @Produces("text/xml")
    OrderResponse cancelOrder(OrderRequest orderRequest);

}





Define the Web service implementation class ExecutionWebServiceImpl.java. This class makes use of the back-end service class OrderService.java to talk to to the DAO layer, which is not shown here.

package com.myapp.webservice.orderexecutionservices.impl;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.WebApplicationException;
import javax.xml.datatype.DatatypeConfigurationException;
import javax.xml.ws.WebServiceContext;
import javax.xml.ws.handler.MessageContext;


import com.myapp.data.order.OrderRequest;
import com.myapp.data.order.OrderResponse;

public class ExecutionWebServiceImpl implements ExecutionWebService {

    private OrderService orderService;

    public ExecutionWebServiceImpl() {
    }

    public ExecutionWebServiceImpl(OrderService orderService) {
        this.setOrderService(orderService);
    }

    public void setOrderService(OrderService orderService) {
        this.orderService = orderService;
    }

    public OrderService getOrderService() {
        return orderService;
    }

    @Override
    public OrderResponse createOrder(OrderRequest orderRequest) {
        OrderResponse response = null;
        try {
      //call to back end service 
            response = getOrderService().createOrder(orderRequest);
        } catch (Exceprion e) {
            throw new WebApplicationException(e);
        }
  
        return response;
    }

    @Override
    public OrderResponse amendOrder(OrderRequest orderRequest) {
       OrderResponse response = null;
        try {
            response = getOrderService().amendOrder(orderRequest);
        } catch (Exception e) {
            throw new WebApplicationException(e);
        } 

        return response;
    }

    @Override
    public OrderResponse cancelOrder(OrderRequest orderRequest) {

        OrderResponse response = null;

        try {
            response = getOrderService().cancelOrder(orderRequest, null);
        } catch (Exception e) {
            throw new WebApplicationException(e);
        } 

        return response;
    }
}


Now, wire up the above classes using Spring and CXF configuration files.


The webservices-config.xml file.

<?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:cxf="http://cxf.apache.org/core" 
 xmlns:jaxrs="http://cxf.apache.org/jaxrs" 
 xmlns:jaxws="http://cxf.apache.org/jaxws"
 xsi:schemaLocation="
    http://www.springframework.org/schema/beans 
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://cxf.apache.org/jaxws 
    http://cxf.apache.org/schemas/jaxws.xsd
    http://cxf.apache.org/jaxrs 
    http://cxf.apache.org/schemas/jaxrs.xsd">

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

 
    <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
    <!-- Configure Execution Web Services Beans -->
    <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
 <bean id="executionWebService" class="com.myapp.webservice.orderexecutionservices.impl.ExecutionWebServiceImpl">
  <constructor-arg ref="orderService" />
 </bean>
 
 <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
 <!-- CONFIGURE ENDPOINTS            -->
 <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
 <jaxrs:server id="executionservices" address="/executionservices/">
  <jaxrs:inInterceptors>
   <ref bean="myAppInInterceptor" />
  </jaxrs:inInterceptors>
  <jaxrs:outInterceptors>
   <ref bean="myAppOutInterceptor" />
  </jaxrs:outInterceptors>
  <jaxrs:providers>
   <ref bean="exceptionMapperInterceptor" />
  </jaxrs:providers>
  <jaxrs:serviceBeans>
   <ref bean="executionWebService" />
  </jaxrs:serviceBeans>
 </jaxrs:server>
    

 <!-- define orderService, interceptors etc. left out for brevity -->
 <!-- This can be defined in other Spring context files as well-->
 
</beans>




The cxf.xml file.

<?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:sec="http://cxf.apache.org/configuration/security"
 xmlns:http="http://cxf.apache.org/transports/http/configuration"
 xsi:schemaLocation="
          http://cxf.apache.org/configuration/security
          http://cxf.apache.org/schemas/configuration/security.xsd
          http://cxf.apache.org/transports/http/configuration
          http://cxf.apache.org/schemas/configuration/http-conf.xsd
          http://www.springframework.org/schema/beans
          http://www.springframework.org/schema/beans/spring-beans.xsd">

 <http:conduit name="*.http-conduit">
  <http:tlsClientParameters secureSocketProtocol="SSL" disableCNCheck="true"/>
  <http:client AllowChunking="false" ReceiveTimeout="60000"/>
 </http:conduit>
</beans>



Finally, register the CXF servlet and bootstrap the above config files via web.xml file. The web.xml file snippet is shown below.

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
 xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
 id="services" version="2.5">
 <display-name>webservices</display-name>

 <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
 <!-- CONFIGURE SPRING CONTAINER -->
 <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~ -->

 <context-param>
  <param-name>contextConfigLocation</param-name>
  <param-value>
   classpath*:transactionContext.xml
   classpath*:daoContext.xml
           
   /WEB-INF/webservice-config.xml
   classpath*:/cxf.xml
   
  </param-value>
 </context-param>
 <listener>
  <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
 </listener>

 <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
 <!-- CONFIGURE CXF SERVLET      -->
 <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
 <servlet>
  <servlet-name>CXFServlet</servlet-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>

 <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
 <!-- CONFIGURE JNDI RESOURCES   -->
 <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
 <resource-ref>
  <description>My App Data Source</description>
  <res-ref-name>jdbc/dataSource/MyDB</res-ref-name>
  <res-type>javax.sql.DataSource</res-type>
  <res-auth>Container</res-auth>
 </resource-ref>
 
</web-app>


Now, the sample client that consumes the Web service.


Note that the interface classes like ExecutionWebService, OrederRequest, OrderResponse, etc will be required by both the client (i.e. the Web service consumer) and the service implementation classes like ExecutionWebServiceImpl (i.e. service provider). Hence, the interface classes need to be packaged as separate jar file to be used by both the client and the service implementation.

package com.myapp.webservice.client;

import org.apache.cxf.jaxrs.client.Client;
import org.apache.cxf.jaxrs.client.JAXRSClientFactory;

//...other imports

public class ExecutionWebServiceClient {

    private static final String EXECUTION_SERVICES_CONTEXT_ROOT = "executionservices";
    
   
    public void consumeRestWebService( ) Throws Exception {
        ExecutionWebService serviceClient = JAXRSClientFactory.create("http://localhost:8080" + EXECUTION_SERVICES_CONTEXT_ROOT, ExecutionWebService.class);
        OrderRequest request = constructOrderReqObj(); // not shown
        OrderResponse response = serviceClient.createOrder(request); 
        //... do something with the response.  
    }   
}



The URLs that we be used will be like

http://localhost:8080/executionservices/execution/1.0/order/create
http://localhost:8080/executionservices/execution/1.0/order/amend
http://localhost:8080/executionservices/execution/1.0/order/cancel



That's all to it. You can also test your Web service using the "poster" plugin for Firefox.


The request payload will be something like:

<?xml version="1.0" encoding="UTF-8"?>
<OrderRequest>
  <orderId>orderId</orderId>
  <responseCode>responseCode</responseCode>
  <accountId>accountId</accountId>
</OrderRequest>


The response payload will be something like

<?xml version="1.0" encoding="UTF-8"?>
<OrderResponse>
  <orderId>orderId</orderId>
  <responseCode>responseCode</responseCode>
  <accountId>accountId</accountId>
</OrderResponse>

Note: You could generate the payload from the XSD file within eclipse IDE by selecting the schema file and then right clicking to click on Generate --> XML. Make sure you check optional elements to be included. It will also require a root element, henc you need to wrap you r schema with something like


<xs:element name="OrderRequest" >
   
   .......
 
</xs:element>


to generate the request payload and

<xs:element name="OrderResponse" >
   
   .......
 
</xs:element>


to generate the response payload within eclipse IDE.


Other relevant links on Web Services

Web Services Interview Questions and Answers - Overview

Labels:

Feb 10, 2012

Java Web Services Interview Questions and Answers: Overview

Q. What are the different application integration styles?
A. There are a number of different integration styles like

1. Shared database
2. batch file transfer
3. Invoking remote procedures (RPC)
4. Exchanging asynchronous messages over a message oriented middle-ware (MOM).


Q. How does a Java EE application integrates with other systems?
A. Using various protocols like HTTP(S), SOAP, RMI, FTP, proprietary, etc.

 

Q. What are the different styles of Web Services used for application integration?
A. SOAP WS and RESTful Web Service

Q. What are the differences between both SOAP WS and RESTful WS? 
A. 
  • The SOAP WS supports both remote procedure call (i.e. RPC) and message oriented middle-ware (MOM) integration styles. The Restful Web Service supports only RPC integration style.
  • The SOAP WS is transport protocol neutral. Supports multiple protocols like HTTP(S),  Messaging, TCP, UDP SMTP, etc. The REST is transport protocol specific. Supports only HTTP or HTTPS protocols.
  • The SOAP WS permits only XML data format.You define operations, which tunnels through the POST. The focus is on accessing the named operations and exposing the application logic as a service. The REST permits multiple data formats like XML, JSON data, text, HTML, etc. Any browser can be used because the REST approach uses the standard GET, PUT, POST, and DELETE Web operations. The focus is on accessing the named resources and exposing the data as a service. REST has AJAX support. It can use the XMLHttpRequest object. Good for stateless CRUD (Create, Read, Update, and Delete) operations.

             GET - read()
             POST - create()
             PUT - update()
             DELETE - delete()
  • SOAP based reads cannot be cached. REST based reads can be cached. Performs and scales better.
  • SOAP WS supports both SSL security and WS-security, which adds some enterprise security features like maintaining security right up to the point where it is needed, maintaining identities through intermediaries and not just point to point SSL only, securing different parts of the message with different security algorithms, etc. The REST supports only point-to-point SSL security. The SSL encrypts the whole message, whether all of it is sensitive or not.
  • The SOAP has comprehensive support for both ACID based  transaction management  for short-lived transactions and compensation based transaction management for long-running transactions. It also supports two-phase commit across distributed resources. The REST supports transactions, but it  is neither ACID compliant nor can provide two phase commit across distributed transactional resources as it is limited by its HTTP protocol.
  • The SOAP has success or retry logic built in and provides end-to-end reliability even through SOAP intermediaries. REST does not have a standard messaging system, and expects clients invoking the service to deal with communication failures by retrying.


  • Q. How would you decide what style of Web Service to use? SOAP WS or REST?
    A. In general, a REST based Web service is preferred due to its simplicity, performance, scalability, and support for multiple data formats. SOAP is favored where service requires comprehensive support for security and transactional reliability.

    The answer really depends on the functional and non-functional requirements. Asking the questions listed below will help you choose.

    • Does the service expose data or business logic? (REST is a better choice for exposing data, SOAP WS might be a better choice for logic).
    • Do the consumers and the service providers require a formal contract? (SOAP has a formal contract via WSDL)
    • Do we need to support multiple data formats?
    • Do we need to make AJAX calls? (REST can use the XMLHttpRequest)
    • Is the call synchronous or  asynchronous?
    • Is the call stateful or stateless? (REST is suited for statless CRUD operations)
    • What level of security is required? (SOAP WS has better support for security)
    • What level of transaction support is required? (SOAP WS has better support for transaction management)
    • Do we have limited band width? (SOAP is more verbose)
    • What’s best for the developers who will build clients for the service? (REST is easier to implement, test, and maintain)


    Q. What tools do you use to test your Web Services?
    A. SoapUI tool for SOAP WS and the Firefox "poster" plugin for RESTFul services.


    Q. What is the difference between SOA and a Web service?
    A.

    SOA is a software design principle and an architectural pattern for implementing loosely coupled, reusable and coarse grained services. You can implement SOA using any protocols such as HTTP, HTTPS, JMS, SMTP, RMI, IIOP (i.e. EJB uses IIOP), RPC etc. Messages can be in XML or Data Transfer Objects (DTOs).    

    Web service is an implementation technology and one of the ways to implement SOA. You can build SOA based applications without using Web services – for example by using other traditional technologies like Java RMI, EJB, JMS based messaging, etc. But what Web services offer is the standards based  and platform-independent service via HTTP, XML, SOAP, WSDL and UDDI, thus allowing interoperability between heterogeneous technologies such as J2EE and .NET.



    Q. Why not favor traditional style middle-ware such as RPC, CORBA, RMI and DCOM as opposed to Web services?
    A.

    The traditional middle-wares tightly couple connections to the applications and it can break if you make any modification to your application. Tightly coupled applications are hard to maintain and less reusable. Generally do not support heterogeneity. Do not work across Internet. Can be more expensive and hard to use.

    Web Services support loosely coupled connections. The interface of the Web service provides a layer of abstraction between the client and the server. The loosely coupled applications reduce the cost of maintenance and increases re-usability. Web Services present a new form of middle-ware based on XML and Web. Web services are language and platform independent. You can develop a Web service using any language and deploy it on to any platform, from small device to the largest supercomputer. Web service uses language neutral protocols such as HTTP and communicates between disparate applications by passing XML messages to each other via a Web API. Do work across internet, less expensive and easier to use.

    Q. What are the different approaches to developing a SOAP based Web service?
    A. 2 approaches.

    • The contract-first approach, where you define the contract first with XSD and WSDL and the generate the Java classes from the contract.
    • The contract-last approach where you  define the Java classes first and then generate the contract, which is the  WSDL file from the Java classes.

    Note: The WSDL describes all operations that the service provides, locations of the endpoints (i.e.e where the services can be invoked), and simple and complex elements that can be passed in requests and responses.

    Q. What are the pros and cons of each approach, and which approach would you prefer?

    A.

    Contract-first Web service


    PROS:

    • Clients are decoupled from the server, hence the implementation logic can be revised on the server without affecting the clients.
    • Developers can work simultaneously on client and server side based on the contract both agreed on.
    • You have full control over how the request and response messages are constructed -- for example, should "status" go as an element or as an attribute? The contract clearly defines it. You can change OXM (i.e. Object to XML Mapping) libraries without having to worry if the "status" would be generated as "attribute" instead of an element. Potentially, even Web service frameworks and tool kits can be changed as well from say Apache Axis to Apache CXF, etc
     
    CONS:

    • More upfront work is involved in setting up the XSDs and WSDLs. There are tools like XML Spy, Oxygen XML, etc to make things easier. The object models need to be written as well.
       
    • Developers need to learn XSDs and WSDLs in addition to just knowing Java.

     
    Contract-last Web service
     
    PROS:
    • Developers don't have to learn anything related to XSDs, WSDLs, and SOAP. The services are created quickly by exposing the existing service logic with frameworks/tool sets. For example, via IDE based wizards, etc.
        
    • The learning curve and development time can be smaller compared to the Contract-first Web service.
     
    CONS:
    •  The development time can be shorter to initially develop it, but what about the on going maintenance and extension time if the contract changes or new elements need to be added? In this approach, since the clients and servers are more tightly coupled, the future changes may break the client contract and affect all clients or require the services to be properly versioned and managed.
    •  In this approach, The XML payloads cannot be controlled. This means changing your OXM libraries could cause something that used to be an element to become an attribute with the change of the OXM.


    So, which approach will you choose?

    The best practice is to use "contract-first", and here is the link that explains this much better with examples -->  contract-first versus contract-last web services In a nutshell, the contract-last is more fragile than the "contract-first".  You will have to decide what is most appropriate based on your requirements, tool sets you use, etc.
     



    Note: More Java Web Services interview questions and answers including WSDL, SOAP, UDDI, JAXR, SAAJ, etc are covered in Java/J2EE Job Interview Companion with diagrams.


    More Web services Interview Questions and Answers

    Java Web Services Interview Questions and Answers: RESTful Web services
    Java Web Services Interview Questions and Answers: SOAP clients
    SOAP versus RESTful Web service -- comparison
    RESTFul Web Service URI conventions with Spring MVC examples

Labels:

Feb 8, 2012

Hibernate Interview Questions and Answers: integration testing the DAO layer

Before this, please refer to Hibernate Interview Questions and Answers: with annotations and Spring framework , as the examples below are extension of this blog.


Q. How would you go about integration testing your DAO (i.e. Data Access Objects) layer or your Hibernate Repository classes?
A. It is a bit tricky to write integration tests because any changes to the underlying data can make your tests to fail. For example, addition of new records, modification to existing data , etc. The key is to keep the data as static as possible. There are two possible strategies.


1. Use a separate in memory database like HSQL (i.e. Hyper Structured Query Language) Database. The data can be stored in flat text files -- say in pipe delimited format and loaded into the in memory database during test set up phase, and deleted during the test tear down phase.

2. The second alternative is to use a framework like DBUnit to extract the relevant data from a given database and convert it into XML based data sets that can be inserted into your test database during the test setUp phase and deleted in the test tear-down phase. The DBUnit takes care of the data extraction and data load.

Both the above approaches maintain static data in either xml or text based flat files and load the data during the test setup.

Q. How would you go about using an in memory database like HSQL DB?
A. It involves the following steps.

  • Define the data in flat files. Say in pipe delimited format.
  • Write the parser classes to read the data and convert them into SQL insert statements to be loaded into the HSQL DB. This is known as ETL (i.e. Extract Transform Load) operation.
  • Override the default hibernate datasource proprties with the HSQL DB related configuration.
  • Wire up the datasource override file via override-daoContext.xml, which uses the HSQL DB, and overrides the hibernate proprties from the file daoContext.xml.
  • Write a mock HSQL DB JNDI bootstrap class and wire it up using a Spring context file (e.g. hsqlBootstrapContext.xml).
  • Write a database populator class that makes use of a number of parser classes to populate the database. Also, wire this up via a Spring context file (e.g. databasePopulatorContext.xml)
  • Finally, the test class that bootstraps all the relevant classes via the Spring context files using dependency injection.


Define the data in a flat file say employeeSearch.txt

#employee_id, emp_code, manager_code, type, base_salary
A342|XSMITH|A456|IM|Permanent|43,500.00
A342|YSMITH|A678|IM|Contract|57,700.00 




Define the parser that loads the data by reading from the above flat file. For e.g. EmployeeParser.java. This class can be further improved by moving out the methods that will be shared by other parsers to a parent class or a helper class.


package com.myapp.database;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.Session;

public class EmployeeParser {

 private static final String DROP_SQL = "drop table tbl_employee";
    private static final String CREATE_SQL =
            "create table tbl_employee(employee_id varchar(12), emp_code varchar(12), " + 
            "manager_code  varchar(12), type  varchar(12), base_salary decimal";


    private static final String INSERT_SQL =
            "insert into tbl_employee (employee_id, emp_code, manager_code, type, base_salary) values (";

   
    public void parseEmployee(Session session) throws ParserException, IOException {
        createDatabaseTable(session);
        BufferedReader file = findFile(getFileName());
        String[] data = readLine(file);
        while (data != null) {
            Query query =
                    session.createSQLQuery(INSERT_SQL + "'" + data[0] + "','" + data[1] + "','"
                            + data[2] + "'," + data[3] +  "','" + data[4] + ")");
            query.executeUpdate();
            data = readLine(file); // read next line from the file
        }
    }
    
    
    protected String[] readLine(BufferedReader file) throws IOException {
     String[] data = null;
        String line = file.readLine();
        while (line != null && line.startsWith("#")) {
            line = file.readLine();
        }
        if (line != null) {
         data =  line.split("\\|"); //split by "|" 
        }
        return data;
    }


    private void createDatabaseTable(Session session) {
        Query query = session.createSQLQuery(DROP_SQL);
        try {
            query.executeUpdate();
        } catch (HibernateException e) {
        }
        query =  session.createSQLQuery(CREATE_SQL);
        query.executeUpdate();
    }
    
    protected BufferedReader findFile(String fileName) {
        final InputStreamReader file =
                new InputStreamReader(getClass().getClassLoader().getResourceAsStream(fileName));
        BufferedReader stream = new BufferedReader(file);
        return stream;
    }

  

    public String getFileName() {
        return "employeeSearch.txt";
    }

}
 

Define an override spring context file to override the actual datasource properies. For example, the actual database could be Sybase or Oracle. The override-daoContext.xml is shown below.


<?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:context="http://www.springframework.org/schema/context"
 xsi:schemaLocation="http://www.springframework.org/schema/beans
   http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
   http://www.springframework.org/schema/context
   http://www.springframework.org/schema/context/spring-context-2.5.xsd"
 default-autowire="byName">

    <bean id="hibernateProperties" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
        <property name="properties">
            <props>
                <prop key="hibernate.dialect">org.hibernate.dialect.HSQLDialect</prop>
                <prop key="hibernate.generate_statistics">true</prop>
                <prop key="hibernate.hbm2ddl.auto">create-drop</prop>
                <prop key="hibernate.jdbc.batch_size">1000</prop>
                <prop key="hibernate.show_sql">true</prop>
                <prop key="hibernate.use_sql_comments">true</prop>
                <prop key="hibernate.cache.use_query_cache">false</prop>
                <prop key="hibernate.cache.use_second_level_cache">false</prop>
                <prop key="hibernate.query.factory_class">org.hibernate.hql.classic.ClassicQueryTranslatorFactory</prop>
            </props>
        </property>
        <property name="location">
            <value>classpath:/hibernate.properties</value>
        </property>
    </bean>


</beans>



Write a JNDI based bootstrapper class HsqlDevBootstrapper.java that emulates JNDI boot strapping for your test classes. This is achieved via the Spring mock class SimpleNamingContextBuilder.

package com.myapp.test.db;
 
import javax.naming.NamingException;
 
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.mock.jndi.SimpleNamingContextBuilder;
 
/**
 * helper class to bootstrap the sybase database datasources
 */
public class HsqlDevBootstrapper  {
     
 public static final String JNDI_BINDING_DB = "java:comp/env/jdbc/dataSource/mydb";
    public static final String DRIVER_CLASS = "org.hsqldb.jdbcDriver";
 
 private SimpleNamingContextBuilder builder;  //Spring JNDI mock class
 
 /**
  * setup HSQL DB, and bind it to jndi tree
  */
 public void start() {
        try {
            builder = SimpleNamingContextBuilder.emptyActivatedContextBuilder();
 
            DriverManagerDataSource ds = new DriverManagerDataSource();
            ds.setDriverClassName(DRIVER_CLASS);
            ds.setUrl("jdbc:hsqldb:mem:my_db"); //in memory HSQL DB URL
            ds.setUsername("user");
            ds.setPassword("pwd");
            builder.bind(JNDI_BINDING_DB, ds);
             
             
        } catch (NamingException e) {
            throw new BeanCreationException(e.getExplanation());
        }
    }
  
 public void stop() {
    builder.deactivate();
    builder.clear();
 }
 
}


Wire up the JNDI bootstrap class via Spring the config file hsqlBootstrapContext.xml.


<?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:context="http://www.springframework.org/schema/context"
 xsi:schemaLocation="http://www.springframework.org/schema/beans
   http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
   http://www.springframework.org/schema/context
   http://www.springframework.org/schema/context/spring-context-2.5.xsd"
 default-autowire="byName">

 <bean id="hsqlBootstrapper" class="com.myapp.test.db.HsqlBootstrapper" init-method="start" destroy-method="stop"/>

</beans>



Define a DatabasePopulator.java class that populates all the relevant (i.e. associated) database tables incling tbl_employee.

package com.myapp.database;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.sql.SQLException;
import java.text.ParseException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.springframework.orm.hibernate3.HibernateTemplate;


public class DatabasePopulator {

    private final HibernateTemplate daoTemplate;

   

    public DatabasePopulator(HibernateTemplate daoTemplate) throws Exception {
        this.daoTemplate = daoTemplate;
        try {
            createDB();
        } catch (Exception e) {
            throw e;
        }
    }

 /**
   * This is where all the loading happens
   */
    public void createDB() throws HibernateException, SQLException, IOException {
        Session session = daoTemplate.getSessionFactory().openSession();
  
  Transaction tran = session.beginTransaction();
  
  //make use of the parser to read the data from a file and load it (i.e. ETL operation - Extract, Transaform, and Load)
        EmployeeParser empParser = new EmployeeParser();
  empParser.parseEmployee(session);
        
  //load other relevant data
  
        session.flush();
        tran.commit();   
    }
 
}


Wire the DatabasePopulator via Spring config file databasePopulatorContext.xml.


<?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:context="http://www.springframework.org/schema/context"
 xsi:schemaLocation="http://www.springframework.org/schema/beans
   http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
   http://www.springframework.org/schema/context
   http://www.springframework.org/schema/context/spring-context-2.5.xsd"
 default-autowire="byName">

    <bean name="databasePopulator" class="com.myapp.database.DatabasePopulator">
        <constructor-arg ref="daoTemplate" />
    </bean>
 
</beans>


Finally the test class. Some of the missing Spring context files and classes were defined in a different blog entry mentioned at the beginning of this blog. The context files need to be loaded in the right order.


package com.myapp.repository;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

import java.util.ArrayList;
import java.util.List;

import javax.annotation.Resource;

import org.joda.time.DateTime;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestExecutionListeners;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;



@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={
  "classpath:hsqlBootstrapContext.xml",
  "classpath:transactionContext.xml",
  "classpath:daoContext.xml",
        "classpath:override-daoContext.xml",
        "classpath:databasePopulatorContext.xml"
})
@TestExecutionListeners(value = { 
  DependencyInjectionTestExecutionListener.class,
  SessionBindingHibernateListener.class
})

public class EmployeeRepositoryTest {

 @Resource
 EmployeeTableRepository  tableRepository;
    
    @Test
    public void testLoadEmployee() {
 
     Assert.assertTrue(tableRepository != null);
        Emplyee employee = null; 
        try {
            employee = tableRepository.loadEmployee("A342");
        } catch (RepositoryException e) {
            fail("Load employee threw an exception " + e);
        }
  
  assertTrue(employee != null);
        assertEquals(employee.getType(), "Permanent");
  //...more assertions
       
    }
 
 
 //....test other repository methods like saveEmployee, etc.
}


Labels:

Feb 6, 2012

Spring and Hibernate Interview Questions and Answers: AOP, interceptors, and deadlock retry

Before this, please refer to Hibernate Interview Questions and Answers: with annotations and Spring framework  , as the examples below are extension of this blog.

Q. How would you go about implementing a dead lock retry service using Spring and hibernate?
A.
  • Define an annotation to annotate the methods that needs deadlock retry service. E.g. DeadlockRetry.java
  • Define the interceptor that gets wired up via AOP to perform the retry functionality by invoking the annotated method. E.g. DeadlockRetryMethodInterceptor
  • Wire up the annotation and deadlock retry classes via Spring config. E.g. transactionContext.xml
  • Finally, annotate the method that needs perform the retry service. E.g. EmployeeServiceImpl --> saveEmployee (....)

Define a custom annotation class DeadlockRetry.java.

package com.myapp.deadlock;

import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Inherited
public @interface DeadlockRetry {

    int maxTries() default 21;
    int tryIntervalMillis() default 100;
    
}


Define the DeadlockRetryMethodInterceptor for performing retries. The above annotation will be bound to the following implementation via Spring.

package com.myapp.deadlock;

import java.lang.reflect.Method;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.hibernate.exception.LockAcquisitionException;
import org.springframework.dao.DeadlockLoserDataAccessException;


public class DeadlockRetryMethodInterceptor implements MethodInterceptor {

    private static final Logger LOGGER = LoggerFactory.getLogger(DeadlockRetryMethodInterceptor.class);
        
    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        Object obj = invocation.getThis();
        Method method = invocation.getMethod();
        Method targetMethod = obj.getClass().getMethod(method.getName(), method.getParameterTypes());
        DeadlockRetry dlRetryAnnotation = targetMethod.getAnnotation(DeadlockRetry.class);
        int maxTries = dlRetryAnnotation.maxTries();
        int tryIntervalMillis = dlRetryAnnotation.tryIntervalMillis();
        for (int i = 0; i < maxTries; i++) {
            try {
                LOGGER.info("Attempting to invoke " + invocation.getMethod().getName());
                Object result = invocation.proceed();    // retry
                LOGGER.info("Completed invocation of " + invocation.getMethod().getName());
                return result;
            } catch (Throwable e) {
                Throwable cause = e;
    
      //... put the logic to identify DeadlockLoserDataAccessException or LockAcquisitionException
      //...in the cause. If the execption is not due to deadlock, throw an exception 
    
            if (tryIntervalMillis > 0) {
              try {
                  Thread.sleep(tryIntervalMillis);
              } catch (InterruptedException ie) { 
                LOGGER.warn("Deadlock retry thread interrupted", ie);
              }
            }
    
        }
  
        //gets here only when all attempts have failed
        throw new RuntimeException
            ("DeadlockRetryMethodInterceptor failed to successfully execute target " 
                    + " due to deadlock in all retry attempts", 
              new DeadlockLoserDataAccessException("Created by DeadlockRetryMethodInterceptor", null));
    }    
}


Wire up the annotation and the interceptor via Spring config transactionContext.xml. Only the snippet is shown.

<!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
    <!-- Deadlock Retry AOP             -->
    <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
    <bean id="deadlockRetryPointcut" class="org.springframework.aop.support.annotation.AnnotationMatchingPointcut">
            <constructor-arg><null/></constructor-arg>
            <constructor-arg value="com.myapp.deadlock.DeadlockRetry" />
    </bean>
 
    <bean id="deadlockRetryMethodInterceptor" class="com.myapp.deadlock.DeadlockRetryMethodInterceptor" />
 
    <bean id="deadlockRetryPointcutAdvisor"
        class="org.springframework.aop.support.DefaultPointcutAdvisor">
            <constructor-arg ref="deadlockRetryPointcut" />
            <constructor-arg ref="deadlockRetryMethodInterceptor" />        
    </bean>

 
Finally, annotate the method that needs to be retried in the event of dead lock issues.

package com.myapp.service;


import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;
//....other imports

public class EmployeeServiceImpl implements EmployeeService {
    
    private final EmployeeTableRepository employeeRepository;
    private PlatformTransactionManager transactionManager;
   
    public EmployeeServiceImpl(EmployeeTableRepository employeeRepository, PlatformTransactionManager transactionManager) {
        this.employeeRepository = employeeRepository;
        this.transactionManager = transactionManager;
    }


    @DeadlockRetry
    public Employee saveEmployee(Employeee employee) throws RepositoryException {
        TransactionStatus transactionStatus =
                transactionManager.getTransaction(new DefaultTransactionDefinition(
                        TransactionDefinition.PROPAGATION_REQUIRED));
        try {
            employee = this.employeeRepository.saveEmployee(employee);
        } catch (Exception e) {
            transactionManager.rollback(transactionStatus);
            throw new RepositoryException(e);
        } finally {
            if (!transactionStatus.isCompleted()) {
                transactionManager.commit(transactionStatus);
            }
        }
        return employee;
    }
 
 
 //....other methods omitted for brevity

} 

The above example gives a real life example using a custom annotation and AOP (i.e. Aspect Oriented Programming).

Note: Also refer to Spring Interview Questions and Answers for explanation on interceptors.

Q. What are the pros and cons between Spring AOP and AspectJ AOP?
A. The Spring AOP is simpler as it is achieved via a runtime dynamic proxy class. Unlike AspectJ, it does not have to go through load time weaving or a compiler. The Spring AOP uses the proxy and decorator design patterns.

Since Spring AOP uses proxy based AOP, it can only offer method execution pointcut, whereas the AspectJ  AOP supports all pointcuts.

In some cases, the compile time weaving (e.g. AspectJ AOP) offers better performance.

Labels: ,

Feb 1, 2012

Hibernate Interview Questions and Answers: with annotations and Spring framework

Spring Interview Questions and Answers Q1 - Q14 are FAQs

Q1 - Q4 Overview & DIP Q5 - Q8 DI & IoC Q9 - Q10 Bean Scopes Q11 Packages Q12 Principle OCP Q14 AOP and interceptors
Q15 - Q16 Hibernate & Transaction Manager Q17 - Q20 Hibernate & JNDI Q21 - Q22 read properties Q23 - Q24 JMS & JNDI Q25 JDBC Q26 Spring MVC Q27 - Spring MVC Resolvers

This is more like a Hibernate and Spring tutorial. Takes you through the key steps with code snippets. Also, very handy to refresh your memory prior to your job interviews.

Q17. What are the general steps involved in creating Hibernate related class?
A17. The general steps involved in creating Hibernate related classes involve the following steps

  • Define the domain (aka entity) objects like Employee, Address, etc to represent relevant tables in the underlying database with the appropriate annotations or using the *.hbm.xml mapping files.
  • Define the Repository  (aka DAO -- Data Access Objects) interfaces and implementations classes that use the domain objects and the hibernate session to perform data base CRUD (Create, Read, Update and Delete) operations the hibernate way.
  • Define the service interfaces and the classes that make use of one or more repositories (aka DAOs) in a transactional context.A transaction manager will be used to coordinate transactions (i.e. commit or rollback) between  a number of repositories.
  • Finally, use an IoC container like Spring framework to wire up the Hibernate classes like SessionFactory, Session, transaction manager, etc and the user defined repositories, and the service classes. A number of interceptors can be wired up as well for deadlock retry, logging, auditing, etc using Spring.
For more detail, refer to Spring and Hibernate integration Interview questions and answers
Q18. How would you define a hibernate domain object with table mappings, native named queries, and custom data conversion using annotations?
A18.

Firstly, define a parent domain object class for any common method implementations.


package com.myapp.domain.model;

public class MyAppDomainObject {
    
 //for example
    protected boolean isPropertyEqual(Object comparee, Object compareToo) {
        if (comparee == null) {
            if (compareToo != null) {
                return false;
            }
        } else if (!comparee.equals(compareToo)) {
            return false;
        }
        return true;
    }

}


Now, extend the common DomainObject for specific DomainObject classes.


package com.myapp.domain.model;

@Entity
@org.hibernate.annotations.Entity(selectBeforeUpdate = true)
@Table(name = "tbl_employee")
@TypeDefs(value = { @TypeDef(name = "dec", typeClass = DecimalUserType.class)})  // custom data type conversion

@NamedNativeQueries({
    @NamedNativeQuery(name = "HighSalary", query = "select * from tbl_employee where salary > :median_salary " , resultClass = Employee.class),
 @NamedNativeQuery(name = "LowSalary", query = "select * from tbl_employee where salary < :median_salary " ,  resultClass = Employee.class)
})

public class Employee extends MyAppDomainObject implements Serializable {
    
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "employee_id")
    private Long id;
 
    @Column(name = "emp_code")
    private String accountCode;

    @Column(name = "manager_code")
    private String adviserCode;

 
 
    @Column(name = "type")
    @Enumerated(EnumType.STRING)
    private EmployeeType type = EmployeeType.PERMANENT;
 

    @Type(type = "dec")
    @Column(name = "base_salary")
    private Decimal salary = Decimal.ZERO;
 
    @Transient
    private Decimal salaryWithBonus; //not persisted to database
 
    @Formula("base_salary*2")
    private Decimal doubleSalary;   //derived or calculated read only property
 
    @Formula("(select base_salary where type = 'Permanent' )")
    private Decimal permanantLeaveLoading;   //derived or calculated read only property
 
 
    @OneToOne(cascade = { CascadeType.REFRESH })
    @JoinColumn(name = "emp_code", insertable = false, updatable = false)
    private EmployeeExtrInfo extraInfo;

    @ManyToOne(cascade = { CascadeType.REFRESH })
    @JoinColumn(name = "manager_code", insertable = false, updatable = false)
    private Manager manager;

    @OneToMany(cascade = { ALL, MERGE, PERSIST, REFRESH }, fetch = FetchType.LAZY)
    @JoinColumn(name = "emp_code", nullable = false)
    @Cascade({ org.hibernate.annotations.CascadeType.SAVE_UPDATE, org.hibernate.annotations.CascadeType.DELETE_ORPHAN })
    private List<PaymentDetail> paymentDetails = new ArrayList<PaymentDetail>();

 //getters and setters omitted for brevity
 
}

The dependency classes like EmployeeExtrInfo, Manager, and PaymentDetail will be mapped in a similar manner as the Employee class. The EmployeeType enum class is shown below. Also note the verys usefull annotations like @NamedNativeQueries, @TypeDefs, and @Formula. The @Formula marks a property as derived, or calculated, read-only property, where its value is calculated at fetch time using SQL expressions.

package com.myapp.domain.model;


public enum EmployeeType {
    
    PERMANENT("Permanent"),
    CONTRACTOR("Contractor"),
    CASUAL("Casual");
    
    private String type;
    
    private EmployeeType (String type) {
        this.type = type;
    }
    
    public String getType() {
        return this.type;
    }
}


The "dec" is a custom data type, you need to define the custom data type class. The "salary" attribute will be making use of this special data type. This is ust a trivial example, but more powerful custom type conversion classes can be created.

package com.myapp.domain.model;

import java.io.Serializable;
import java.math.BigDecimal;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.Properties;

import org.hibernate.usertype.ParameterizedType;
import org.hibernate.usertype.UserType;

public class DecimalUserType implements UserType, ParameterizedType {
    public static final int PRECISION = 28;
    public static final int SCALE = 15;

    public int[] sqlTypes() {
        return new int[]{Types.DECIMAL};
    }

    public Class<Decimal> returnedClass() {
        return BigDecimal.class;
    }

    public boolean equals(Object x, Object y) {
        if (x == y) {
            return true;
        }
        if (x == null || y == null) {
            return false;
        }
        return x.equals(y);
    }

    public int hashCode(Object x) {
        return 0;
    }

    public Object nullSafeGet(ResultSet rs, String[] names, Object owner) throws SQLException {
        BigDecimal forReading = rs.getBigDecimal(names[0]);

        if (forReading == null) {
            return null;
        }

        return forReading.setScale(2, RoundingMode.HALF_EVEN);   //round to 2 decimal places
    }


    public void nullSafeSet(PreparedStatement st, Object value, int index) throws SQLException {
        if (value == null) {
            st.setNull(index, Types.NUMERIC);
            return;
        }
  
  
        BigDecimal forSaving = (BigDecimal) value;
        st.setBigDecimal(index, forSaving.setScale(2, RoundingMode.HALF_EVEN));
    }

    public Object deepCopy(Object value) {
        return value;
    }

    public boolean isMutable() {
        return false;
    }

    public Serializable disassemble(Object value) {
        return null;
    }

    public Object assemble(Serializable cached, Object owner) {
        return null;
    }

    public Object replace(Object original, Object target, Object owner) {
        return original;
    }

    public void setParameterValues(Properties parameters) {
    }
}



The named queries are also shown above with the @NamedNativeQueries and @NamedNativeQuery annotations. The parametrized values like :median_salary needs to be supplied via the Hibernate repository class that makes use of the Employee domain object. Firstly define the interface.

package com.myapp.domain.repo;

import java.util.List;


public interface EmployeeTableRepository {

 Employee saveEmployee(Employee employee) throws RepositoryException ; 
 Employee loadEmployee(Long employeeId) throws RepositoryException ;
 List<Employee> findAllEmployeesWithHighSalary(BigDecimal medianSalary) throws RepositoryException;
 List<Employee> findAllEmployeesWithLowSalary(BigDecimal medianSalary) throws RepositoryException 

}



Next the implementation of the above interface.

package com.myapp.domain.repo;


@SuppressWarnings("unchecked")
public class EmployeeTableHibernateRepository extends HibernateDaoSupport implements EmployeeTableRepository {

    public EmployeeTableHibernateRepository (HibernateTemplate hibernateTemplate) {
        setHibernateTemplate(hibernateTemplate);
    }

 //The employee objects gets constructed and passed to repo via the Business Service layer
    public Employee saveEmployee(Employee employee) throws RepositoryException {
        Session session = getHibernateTemplate().getSessionFactory().getCurrentSession();
        session.saveOrUpdate(employee);
        session.flush();
        session.evict(employee);
        return this.loadEmployee(employee.getId());
    }
 
 public Employee loadEmployee(Long employeeId) throws RepositoryException {
        Session session = getHibernateTemplate().getSessionFactory().getCurrentSession();
        Criteria crit = session.createCriteria(Employee.class);
        crit.add(Restrictions.eq("id",employeeId));
        List<Employee> employees = crit.list();
        if (employees.size() == 1) {
            return employees.get(0);
        }
  
  //this is a custom exception class
        throw new RepositoryException("Found more than one or no employee with Id:" + employeeId);
    }
 
 
 public List<Employee> findAllEmployeesWithHighSalary(BigDecimal medianSalary) throws RepositoryException {
        Session session = getHibernateTemplate().getSessionFactory().getCurrentSession();
        Query query = session.getNamedQuery("HighSalary");    // query name defined in Employee class
        query.setBigDecimal(":median_salary", medianSalary);  // query parameter defined in Employee class 
        return (List<Employee>) query.list();
    }
 
 public List<Employee> findAllEmployeesWithLowSalary(BigDecimal medianSalary) throws RepositoryException {
        Session session = getHibernateTemplate().getSessionFactory().getCurrentSession();
        Query query = session.getNamedQuery("LowSalary");     // query name defined in Employee class
        query.setBigDecimal(":median_salary", medianSalary);  // query parameter defined in Employee class 
        return (List<Employee>) query.list();
    }

    
  //other methods can be defined here
}

The Service classes shown below will be making use of the repository (or DAO) classes. The service class can use any number of the repository classes, and also responsible for cordinating the transaction as well with a TransactionManger. In the example below, we will be using the "PlatformTransactionManager" implementation provided by the Spring framework.
package com.myapp.service;

public interface EmployeeService {

   Employee saveEmployee(Employeee employee) throws RepositoryException;
   Employee loadEmployee(Long employeeId) throws RepositoryException;

}

The implementation class is shown with the transaction manager. The employeeRepository and transactionManager are dependency injected
package com.myapp.service;


import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;
//....other imports

public class EmployeeServiceImpl implements EmployeeService {

    
    private final EmployeeTableRepository employeeRepository;
 private PlatformTransactionManager transactionManager;
   
    public EmployeeServiceImpl (EmployeeTableRepository employeeRepository, PlatformTransactionManager transactionManager) {
        this.employeeRepository = employeeRepository;
  this.transactionManager = transactionManager;
    }


    public Employee saveEmployee(Employeee employee) throws RepositoryException {
        TransactionStatus transactionStatus =
                transactionManager.getTransaction(new DefaultTransactionDefinition(
                        TransactionDefinition.PROPAGATION_REQUIRED));
        try {
            employee = this.employeeRepository.saveEmployee(employee);
        } catch (Exception e) {
            transactionManager.rollback(transactionStatus);
            throw new RepositoryException(e);
        } finally {
            if (!transactionStatus.isCompleted()) {
                transactionManager.commit(transactionStatus);
            }
        }
        return employee;
    }
 
 public Employee loadEmployee(Long employeeId) throws RepositoryException {
        return this.employeeRepository.loadEmployee(employeeId);
    }
 
 
 //....other methods

}




Q19. How will you wire up the code snippet discussed above using Spring?
A19. The following 3 Spring configuration files are used for wiring up the classes defined above.


  • The daoContext.xml file to define the hibernate session factory, jndi data source, hibernate properties, and the user defined domain class and the repository.
  • The transactionContext.xml file to define the transaction manager.
  • The servicesContext.xml to define the custom services class


Firstly the daoContext.xml file:


<?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:context="http://www.springframework.org/schema/context"
 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/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
   http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">

 <bean id="dataSourceMyDB" class="org.springframework.jndi.JndiObjectFactoryBean" scope="singleton">
  <property name="jndiName">
   <value>java:comp/env/jdbc/dataSource/mydb</value>
  </property>
 </bean>

 <bean id="hibernateProperties" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
  <property name="properties">
   <props>
    <prop key="hibernate.dialect">org.hibernate.dialect.SybaseDialect</prop>
    <prop key="hibernate.generate_statistics">false</prop>
    <prop key="hibernate.hbm2ddl.auto">verify</prop>
    <prop key="hibernate.jdbc.batch_size">50</prop>
    <prop key="hibernate.show_sql">false</prop>
    <prop key="hibernate.format_sql">false</prop>
    <prop key="hibernate.cache.use_query_cache">false</prop>
    <prop key="hibernate.cache.use_second_level_cache">false</prop>
    <prop key="hibernate.query.factory_class">org.hibernate.hql.classic.ClassicQueryTranslatorFactory</prop>
   </props>
  </property>
  <property name="location">
   <value>classpath:/hibernate.properties</value>
  </property>
 </bean>
 
    <bean id="hibernateAnnotatedClasses" class="org.springframework.beans.factory.config.ListFactoryBean">
        <property name="sourceList">
            <list>
                <value>com.myapp.domain.model.Employee</value>
            </list>
        </property>
    </bean>

 <bean id="hibernateSessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
  <property name="dataSource" ref="dataSourceShadow" />
  <property name="hibernateProperties">
   <ref local="hibernateProperties" />
  </property>
  <property name="entityInterceptor">
        </property>
  <property name="annotatedClasses">
      <ref local="hibernateAnnotatedClasses" />
  </property>
  <property name="annotatedPackages">
   <list></list>
  </property>
 </bean>
 
 <bean id="daoTemplate" class="org.springframework.orm.hibernate3.HibernateTemplate">
  <constructor-arg index="0" ref="sessionFactory" />
  <constructor-arg index="1" value="true" />
 </bean>

 <!-- Repository beans -->
 <bean id="employeeTableRepository" class="com.myapp.domain.repo.EmployeeTableHibernateRepository">
  <constructor-arg ref="daoTemplate" />
 </bean>
 
</beans>



The transactionContext.xml

<?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:context="http://www.springframework.org/schema/context" 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/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
            http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">

    <!-- Transaction manager for a single Hibernate SessionFactory (alternative to JTA) -->
    
    <alias name="hibernateSessionFactory" alias="sessionFactory"/>
       
    <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
        <property name="sessionFactory" ref="sessionFactory" />
    </bean>
    <bean id="advisorAutoProxy" class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" />
    <bean id="transactionAttrSource" class="org.springframework.transaction.interceptor.TransactionAttributeSourceAdvisor">
        <property name="transactionInterceptor" ref="transactionInterceptor" />
    </bean>
    <bean id="transactionInterceptor" class="org.springframework.transaction.interceptor.TransactionInterceptor">
        <property name="transactionManager" ref="transactionManager" />
        <property name="transactionAttributeSource">
            <bean class="org.springframework.transaction.annotation.AnnotationTransactionAttributeSource" />
        </property>
    </bean>

</beans>



Finally the servicesContext.xml

<?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:context="http://www.springframework.org/schema/context"
    xmlns:aop="http://www.springframework.org/schema/aop"
 xsi:schemaLocation="http://www.springframework.org/schema/beans
   http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
   http://www.springframework.org/schema/context
   http://www.springframework.org/schema/context/spring-context-2.5.xsd
   http://www.springframework.org/schema/aop 
   http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">

 <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
 <!-- CONFIGURE SERVICE BEANS         -->
 <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
 <bean id="employeeService" class="com.myapp.service.EmployeeService">
  <constructor-arg ref="employeeTableRepository" />
  <constructor-arg ref="transactionManager" />
 </bean>
 
</beans>

Q20. How will you go about writing an integration or unit test for the EmployeeService described above?
A20. Since the dataSource is looked up via JNDI, you need to emulate the JNDI lookup.  This can be achieved with the Spring helper classes SimpleNamingContextBuilder and DriverManagerDataSource.

This involves 3 steps.

  • Define a bootsrapper class that emulates JNDI lookup using Spring helper classes like SimpleNamingContextBuilder and DriverManagerDataSource. For example, SybaseDevBootstrapper.java file.
  • Wire-up this via a Spring config file named sybaseDevBootstrapContext.xml.
  • Finally, write the JUnit test class EmployeeServicesSybTest.java.
  • Define the TestExecutionListeners if required.
 Firstly the SybaseDevBootstrapper.java


package com.myapp.test.db;

import javax.naming.NamingException;

import org.springframework.beans.factory.BeanCreationException;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.mock.jndi.SimpleNamingContextBuilder;

/**
 * helper class to bootstrap the sybase database datasources
 */
public class SybaseDevBootstrapper  {
    
 public static final String JNDI_BINDING_DB = "java:comp/env/jdbc/dataSource/mydb";
    public static final String DRIVER_CLASS = "com.sybase.jdbc3.jdbc.SybDriver";

 private SimpleNamingContextBuilder builder;  //Spring JNDI emulator class

 /**
  * setup sybase databases, and bind to specific places in jndi tree
  */
 public void start() {
        try {
         builder = SimpleNamingContextBuilder.emptyActivatedContextBuilder();

         DriverManagerDataSource ds = new DriverManagerDataSource();
            ds.setDriverClassName(DRIVER_CLASS);
            ds.setUrl("jdbc:sybase:Tds:host-name:10004/my_db");
            ds.setUsername("user");
            ds.setPassword("pwd");
            builder.bind(JNDI_BINDING_DB, ds);
            
            
        } catch (NamingException e) {
            throw new BeanCreationException(e.getExplanation());
        }
    }
 
 public void stop() {
  builder.deactivate();
  builder.clear();
 }
}




Next, wire the above Java class.



<?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:context="http://www.springframework.org/schema/context"
 xsi:schemaLocation="http://www.springframework.org/schema/beans
   http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
   http://www.springframework.org/schema/context
   http://www.springframework.org/schema/context/spring-context-2.5.xsd"
 default-autowire="byName">

 <bean id="sybaseDevBootstrapper" class="com.myapp.test.db.SybaseDevBootstrapper" init-method="start" destroy-method="stop"/>

</beans>



Finally the test class EmployeeServicesSybTest.java


package com.myapp.test.services;

import javax.annotation.Resource;

import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestExecutionListeners;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;

//...other imports


@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={
  "classpath:/sybaseDevBootstrapContext.xml",
  "classpath:/transactionContext.xml",
  "classpath:/daoContext.xml",
  "classpath:/servicesContext.xml",
})
@TestExecutionListeners(value = { 
  DependencyInjectionTestExecutionListener.class,
  SessionBindingHibernateListener.class
})

public class EmployeeServicesSybTest {
 
 @Resource
 EmployeeService employeeService;
    
    @Test
    public void testSaveEmployee() throws RepositoryException {
      Assert.assertTrue(employeeService != null);
      
      Employee employee = new Emloyee();
      //....assign values here
      
      employeeService.seaveEmployee(employee);
      
    }

}


The JUnit's way of setting up cross cutting concerns like security, locale, currency, timezone, and any other pre-initilization rules for the test cases function correctly is via annoattions like @Before and @After. The Spring's TestContext framework uses the anootation @TestExecutionListeners to acheive setting up of these cross cutting concerns. In the above example, we are using the DependencyInjectionTestExecutionListener.class from the Spring framework to provide support for dependncy injection and the custom SessionBindingHibernateListener.class to bind the session to the current thread. The custom implementation shown below extends the AbstractTestExecutionListener, which is the abstract implementation of TestExecutionListener class from the Spring framework.



/**
 * Helper class for binding sessions to the current thread
 * 
 */
public class SessionBindingHibernateListener extends SessionBindingListener {
    
 private static final String BEAN_NAME = "hibernateSessionFactory";

 public SessionBindingHibernateListener() {
  super(BEAN_NAME);
 }
}






import org.hibernate.FlushMode;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.orm.hibernate3.SessionFactoryUtils;
import org.springframework.orm.hibernate3.SessionHolder;
import org.springframework.test.context.TestContext;
import org.springframework.test.context.support.AbstractTestExecutionListener;
import org.springframework.transaction.support.TransactionSynchronizationManager;


/**
 * Helper class for binding sessions to the current thread
 * 
 */
public class SessionBindingListener extends AbstractTestExecutionListener {
    
    private final String beanName;
    
 public SessionBindingListener(String beanName) {
  this.beanName = beanName;
 }
    
 @Override
 public void prepareTestInstance(TestContext testContext) throws Exception {
  ApplicationContext context = testContext.getApplicationContext();
  SessionFactory sessionFactory = (SessionFactory) context.getBean(beanName);
  
        Session session = SessionFactoryUtils.getSession(sessionFactory, true);
        session.setFlushMode(FlushMode.MANUAL);
        if (!TransactionSynchronizationManager.hasResource(sessionFactory)) {
            TransactionSynchronizationManager.bindResource(sessionFactory, new SessionHolder(session));
        }
 }
}


Labels: