Thursday, August 22, 2013

Entity-Attribute-Value

In the DB schema of a previous project (which was a 10 year old legacy system), there was a table with a set of attributes listed as rows instead of columns as shown below.


When asked why, it was told that there was a client request to add additional parameters to the product entity, but adding a new column was not feasible since the table contained millions of records. Instead they opted for this solution as a workaround. Two new tables were added and the "Product_Config_Param_Def" contained the new parameter definitions and "Product_Config_Param_Value" contained the corresponding values. This was flexible and allowed adding many more parameters without having to add columns. And the plus point is when there are products that don't have the entire set of parameters, we don't need to populate DB with nulls.

This is what is known as "Entity-Attribute-Value" Modeling (EAV) and it's a great way for us to get closer to the benefits offered by No SQL DBS with our relational DBs. No SQL DB of course offer us unlimited flexibility in adding dynamic attributes and is the way to go if our entities have large amount of varying attributes (In other words, when entities have different sets of attributes) and the relationships between entities is rather limited (Think FB accounts).

With EAV, data is modeled as attribute-value pairs.

EAV should be used with caution. Many identify it as an anti-pattern. It can be such a performance hit. If I may copy the example mentioned in http://tonyandrews.blogspot.com/2004/10/otlt-and-eav-two-big-design-mistakes.html

With no EAV:

select ename from emp where job=’CLERK’ and sal < 2000;

With EAV:

select ev1.name
from emp_values ev1, emp_values ev2 emp_values ev3
where ev1.code = ‘NAME’
and ev1.empno = ev2.empno
and ev2.code = ‘JOB’
and ev2.value = ‘CLERK’
and ev1.empno = ev3.empno
and ev3.code = ‘SAL’
and TO_NUMBER(ev3.value) < 2000;

Sunday, August 4, 2013

Camel-CXF: Java-First Service Implementation

Summarized from the documentation.

Web service interface is defined using a Service Endpoint Interface (SEI) and the WSDL is generated from it.

Steps
1. Implement & annotate the SEI.
2. Implement other requisite Java classes.
        - Any data types referenced by the SEI.
        - The implementation of the SEI. 3. Instantiate the Web service endpoint.
4. Generate the WSDL

1. Implement & annotate the SEI.

SEI is used as the;
• Base type of the Web service implementation (server side)—you define the Web service by implementing the SEI.
• Proxy type (client side)—on the client side, you use the SEI to invoke operations on the client proxy object.
• Basis for generating the WSDL contract—in the Java-first approach, you generate the WSDL contract by converting the SEI to WSDL.

In order to use the standard JAX-WS frontend, the SEI must be annotated with the @WebService annotation. Annotations are also used to customize the mapping from Java to WSDL.

import javax.jws.WebParam;
import javax.jws.WebService;

@WebService(targetNamespace = "http://demo.fusesource.com/wsdl/CustomerService/", name = "CustomerService", serviceName = "CustomerService", portName = "SOAPOverHTTP")
public interface CustomerService {
 public com.fusesource.demo.customer.Customer lookupCustomer(
   @WebParam(name = "customerId", targetNamespace = "") java.lang.String customerId);

 public void updateCustomer(
   @WebParam(name = "cust", targetNamespace = "") com.fusesource.demo.customer.Customer cust);

 public void getCustomerStatus(
   @WebParam(name = "customerId", targetNamespace = "") java.lang.String customerId,
   @WebParam(mode = WebParam.Mode.OUT, name = "status", targetNamespace = "") javax.xml.ws.Holder status,
   @WebParam(mode = WebParam.Mode.OUT, name = "statusMessage", targetNamespace = "") javax.xml.ws.Holder statusMessage);
}

2. Implement other requisite Java classes.

When you run the Java-to-WSDL compiler on the SEI, it converts not only the SEI, but also the classes referenced as parameters or return values. The parameter types must be convertible to XML, otherwise it would not be possible for WSDL operations to send or to receive those data types. In fact, when you run the Java-to-WSDL compiler, it is typically necessary to convert an entire tree of related classes to XML using the standard JAX-B encoding.

Each related class must have a default constructor .

public class Customer {
 protected String firstName;
 protected String lastName;
 protected String phoneNumber;
 protected String id;

 // Default constructor, required by JAX-WS
 public Customer() {
 }

 public Customer(String firstName, String lastName, String phoneNumber,
   String id) {
  super();
  this.firstName = firstName;
  this.lastName = lastName;
  this.phoneNumber = phoneNumber;
  this.id = id;
 }

 public String getFirstName() {
  return firstName;
 }

 public void setFirstName(String value) {
  this.firstName = value;
 }

 public String getLastName() {
  return lastName;
 }

 public void setLastName(String value) {
  this.lastName = value;
 }

 public String getPhoneNumber() {
  return phoneNumber;
 }

 public void setPhoneNumber(String value) {
  this.phoneNumber = value;
 }

 public String getId() {
  return id;
 }

 public void setId(String value) {
  this.id = value;
 }
}

3. Instantiate the Web service endpoint.

In Apache CXF, you create a WS endpoint by defining a jaxws:endpoint element in XML. The WS endpoint is effectively the runtime representation of the Web service: it opens an IP port to listen for SOAP/HTTP requests, is responsible for marshalling and unmarshalling messages (making use of the
generated Java stub code), and routes incoming requests to the relevant methods on the implementor class.

jaxws:endpoint => To integrate a WS endpoint with a Java implementation class.
cxf:cxfEndpoint => To integrate a WS endpoint with a Camel route.

Creating a Web service in Spring XML consists of the following two steps:
1. Create an instance of the implementor class, using the Spring bean element.
2. Create a WS endpoint, using the jaxws:endpoint element.


 
 
 
 
 
 
 

4. Generate the WSDL

To generate a WSDL contract from your SEI, you can use either the java2ws command-line utility or the cxf-java2ws-plugin Maven plug-in. Plug-in approached is given below.

Add plugin to the POM .


 ...
 
  2.4.2-fuse-00-05
 
 
  install
  
   ...
   
    org.apache.cxf
    cxf-java2ws-plugin
    ${cxf.version}
    
     
      org.apache.cxf
      cxf-rt-frontend-jaxws
      ${cxf.version}
     
     
      org.apache.cxf
      cxf-rt-frontend-simple
      ${cxf.version}
     
    
    
     
      process-classes
      process-classes
      
       org.fusesource.demo.camelcxf.ws.server.CustomerService
       
       
        ${basedir}/../src/main/resources/wsdl/CustomerService.wsdl
       
       true
       true
      
      
       java2ws