Showing posts with label Spring. Show all posts
Showing posts with label Spring. Show all posts

Thursday, 30 January 2014

Spring Annotations

Introduction:

In Spring, XML based configurations is the most popular configuration style. Spring uses annotations as an alternative to XML for declarative configuration.

To make annotations and work in spring, use the <context:component-scan> tag in spring bean configuration file. So that spring will scan and find out the beans and register in the Spring Container.

Syntax:

Ex:

Note: If it requires adding multiple packages to scan, use comma separator.

Auto Components Scan Annotation Types:
In Spring, there are 4 types of auto components scan annotation types
  • @Component – Indicates an auto scan component.
  • @Repository – Indicates DAO component in the persistence layer.
  • @Service – Indicates a Service component in the business layer.
  • @Controller – Indicates a controller component in the presentation layer.

<context:annotation-config> is used to activate annotations in beans already registered in the application context.

Component-scan vs annotation-config:
<context:annotation-config>: Only registers 4 BeanPostProcessors that are part of the Spring Framework and have specific configuration objectives.
  • CommonAnnotationBeanPostProcessor: Recognizes and processes the JSR 250 common annotations (@PostConstruct, @PreDestroy, @Resource).
  • AutowiredAnnotationBeanPostProcessor: Recognizes the Autowired related annotations (@Autowired, @Value, @Inject, @Qualifier, etc)
  • RequiredAnnotationBeanPostProcessor: Recognizes the @Required annotation
  • PersistenceAnnotationBeanPostProcessor: Recognizes the @PersistenceUnit and @PersistenceContext annotations (related to JPA).
All of these annotations are typically placed either on a class field, constructor or method. <context:component-scan>: is a super set of the <context:annotation-config> tag. It registers the same bean post processors and also will perform component scanning. It looks for classes in the given base-package that are annotated with a category of annotations (@Component, @Repository, @Controller, etc). The purpose of using is to provide an alternative way to 'discover' Spring components other than using XML.

Spring related Annotations:

Below are the few important spring related annotations and their package.
Spring Annotation
Package
@Controller
org.springframework.stereotype.Controller
@RequestMapping
org.springframework.web.bind.annotation.RequestMapping
@RequestParam
org.springframework.web.bind.annotation.RequestParam
@PathVariable
org.springframework.web.bind.annotation.PathVariable
@ModelAttribute
org.springframework.web.bind.annotation.ModelAttribute
@SessionAttributes
org.springframework.web.bind.annotation.SessionAttributes
@PreAuthorize
org.springframework.security.access.prepost.PreAuthorize

@Controller Annotation:

The @Controller annotation indicates that a particular class serves the role of a controller. The controller class is no longer need to extends any of base controllers. When we @Controller annotation, its mandatory to use @RequestMapping annotation. This annotation is used to map a particular http/https request.

@RequestMapping Annotation:

This annotation can be used at both class level and method level. In case of multi-action controller, it’s recommended to use this annotation at method level and also at class level if required.

Syntax at class level: @RequestMapping("/") 
Syntax at method level: @RequestMapping(value = “/”, method = RequestMethod.[GET/POST]) 

We can use wildcard characters like * for path pattern matching. 

Simple scenario:
@Controller
@RequestMapping("/blogspot")
public class blogspotController {

 @RequestMapping(value="/properties")
 public String findAllProperties(){

 }
}
Testing URL: /blogspot/properties

Request parameter binding:
@RequestMapping(value="/properties")
public String findByProperty(@RequestParam("propertyId") String propertyId){
  
}
Testing URL: /blogspot/properties?propertyId =<some_id>

Extracting path variables:
@RequestMapping(value="/blogspot/{propertyId}")
public String findByProperty(@PathVariable String paropertyId){

}
(OR)
@RequestMapping(value="/blogspot/{propertyId}")
public String findByProperty(@PathVariable("propertyId") String somePropertyId){

}
Testing URL: /blogspot/properties/propertyId/23

Extracting multiple path variables:
@RequestMapping(value="/properties/{propertyId}/owner/{ownerId}")
public String findByProperty( @PathVariable String propertyId,  @PathVariable String ownerId){

}
Testing URL: /blogspot/ properties/23/owner/39

Extracting regular expressions:
@RequestMapping(value="/{textualPart:[a-z-]+}.{numericPart:[\\d]+}")
public String regularExpression( @PathVariable String textualPart, @PathVariable String numericPart){

}
Testing URL: /blogspot/text_chars.123

@RequestParam Annotation:

It’s a method argument annotation. This @RequestParam annotation can be used for getting the parameters from the request(query parameters). The scope of the variable passed is method scope. Syntax:
@RequestParam(value = “requestParam”, required = true/false, defaultValue = "defaultVal") dataTye variableName)
Ex:
public void printPageInfo(@RequestParam(value = "pageNumber", required = false, defaultValue = "1")  Integer pageNumber)

@PathVariable Annotation:

It’s a method argument annotation. The @PathVariable annotation can be used for getting the value of URI template variable. Syntax:
@PathVariable("pathVarName") dataTyep variableName)
Ex: If we want to create a controller that would allow users to see view pages by using a URL as below http://parameshk.blogspot.in/search/label/Java - display details for java http://parameshk.blogspot.in/search/label/Spring - display details for spring
@Controller
@RequestMapping("/search")
public class ShowPagesController {
    @RequestMapping("/label/{labelName}") // Handle any request of the form "/label/XXXXX"
    public String showProduct(Model model, @PathVariable("labelName") String labelName) {
    }
}

@ModelAttribute Annotation:

It supports RequestMapping annotated handler classes. It binds a method parameter or method return value to a named model attribute that exposed to a web view. Uses of @ModelAttribute: Useful to read data from an existing model by assigning it to handler method parameters. If a view/jsp want to populate some values while onload, we can inject the data to jsp by using this annotation. Ex: Below is the code snippet, which binds Employee object to a view page. Code snippet in controller:
@RequestMapping(value="/property-details")  
public ModelAndView processPerson(@ModelAttribute Property prop) {  
    ModelAndView modelAndView = new ModelAndView();  
    modelAndView.setViewName("property-details");  
    modelAndView.addObject("property", prop);  
    return modelAndView;  
}
Code snippet in jsp/view page:
Property Details:
Property Name: ${ property.name}
Property Location: ${ property.location}
Property Owner: ${ property.owner}

@ SessionAttributes Annotation:

The @SessionAttributes annotation is used to store the model object in the session. It is used on the Controller class level to declare used session attributes. All Model attributes having names or types defined in this annotation will be automatically persisted and restored between the subsequent requests. It’s a very small declaration instead of using HttpSession's getAttribute() & setAttribute() calls. Model attributes are persisted at the end of request handling by AnnotationMethodHandlerAdapter, after calling the handler method responsible for request handling and after determining the ModelAndView for the response. Their names in Model are used as their names in the persistent storage. Ex: @SessionAttributes("form") will trigger persisting Model attribute named "form" as HttpSession attribute named "form". Persisted Model attributes will be removed only when SessionStatus.setComplete() method called in handler methods. Ex:
@Controller
@RequestMapping(value="/dashboard", method=RequestMethod.POST)
@SessionAttributes("property")
public class LoginController {
    @RequestMapping(value="/authenticate",method=RequestMethod.POST)
    public String authenticate(@RequestParam String propertyId, Model model){
        Property property = new Property();
        property.setPropertyId(propertyId);
        property.setName("PPB_1");
        property.setLocation("USA");
        model.addAttribute("property", property);
        return "xxxxx";
    }
}
Note: To store multiple objects in @SessionAttributes use comma separator.

RESTful Web Service - JAX-RS Annotations:

  • Jersey, the reference implementation of JAX-RS, implements support for the annotations defined in JSR 311, making it easy for developers to build RESTful web services by using the Java programming language.
  • REST is an architectural style which is based on web-standards and the HTTP protocol.
  • In REST based architecture everything is a resource. A resource is accessed via a common interface based on the HTTP standard methods.
  • In a REST based architecture you typically have a REST server which provides access to the resources and a REST client which accesses and modify the REST resources.
  • Every resource should support the HTTP common operations. Resources are identified by global IDs (which are typically URIs).
  • REST allows that resources have different representations, e.g. text, xml, json etc. The rest client can ask for specific representation via the HTTP protocol (content negotiation).
JAX-RS Annotation
Package
@Path
javax.ws.rs.Path
@Produces
javax.ws.rs.Produces
@Consumes
javax.ws.rs.Consumes
@GET
javax.ws.rs.GET
@POST
javax.ws.rs.POST
@PUT
javax.ws.rs.PUT
@DELETE
javax.ws.rs.DELETE
@FormParam
javax.ws.rs.FormParam
@PathParam
javax.ws.rs.PathParam
@QueryParam
javax.ws.rs.QueryParam

@Path Annotation:

It’s a both class & method level annotation. This identifies the URI path template to which the resource responds. The @Path annotation's value is a partial URI path template relative to the base URI of the server on which the resource is deployed, the context root of the WAR, and the URL pattern to which the Jersey helper servlet responds.

Syntax:
    @Path("/uri")

The @Path annotation is not limited to simple path expressions. We can also have the ability to insert regular expressions into @Path's value.

Syntax: @Path (“uri/{variable[:regular-expression]}”)
Ex:
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.core.Response;

@Path("/properties")
public class UserRestService {

 @GET
 public Response getProperties() {
  //TODO provide method body
 }
 //Target Url: /properties

 @GET
 @Path("/ppb")
 public Response getPropertyPPB() {
  //TODO provide method body
 }
 //Target Url: /properties/ppb
 
 @GET
 @Path("{name}")
 public Response getPropertyByName(@PathParam("name") String name) {
  //TODO provide method body
 }
 //Target Url: /properties/abcdefg

 @GET
 @Path("{id : \\d+}") //id is regular expression which accepts only numbers
 public Response getPropertyById(@PathParam("id") String id) {
  //TODO provide method body
 }
 //Target Url: /properties/1234

 @GET
 @Path("/propertyname/{propertyname : [a-zA-Z][a-zA-Z_0-9]+}")
 public Response getPropertyByPropertyName(@PathParam("propertyname") String propertyname) {
  //TODO provide method body
 }
 //Target Url: /properties/propertyname/myproperty123

}

@Consumes Annotation:

The @Consumes annotation is used to specify which MIME media types of representations, a resource can accept/ consume from the client. @Consumes annotation can be applied at both the class and method levels. If it’s applied at class level, by default all the methods will accept the same MIME type. If it’s applied at the method level, overrides the class level annotation if applied. The possible MIME types which can be used for this annotation can be available in the class javax.ws.rs.core.MediaType.

Syntax: 
   @Consumes(MediaType.XXX) @Consumes({MediaType.XXX, MediaType.YYY, …})

@Produces Annotation:

The @Produces annotation is used to specify the MIME media types of representations a resource can produce and send back to the client. @Produces annotation can be applied at both the class and method levels. If it’s applied at class level, by default all the methods will accept the same MIME type. If it’s applied at the method level, overrides the class level annotation if applied. The possible MIME types which can be used for this annotation can be available in the class javax.ws.rs.core.MediaType.

Syntax: 
    @Produces (MediaType.XXX) @Produces ({MediaType.XXX, MediaType.YYY, …})

@GET, @PUT, @POST, @DELETE & @HEAD Annotations:

@GET, @PUT, @POST, @DELETE and @HEAD are resource method designator annotations defined by JAX-RS and which correspond to the similarly named HTTP methods. In the example above, the annotated Java method will process HTTP GET requests. The behavior of a resource is determined by which of the HTTP methods the resource is responding to.
  • GET defines a reading access of the resource without side-effects. The resource is never changed via a GET request, e.g. the request has no side effects (idempotent).
  • PUT creates a new resource, must also be idempotent.
  • DELETE removes the resources. The operations are idempotent, they can get repeated without leading to different results.
  • POST updates an existing resource or creates a new resource.

@QueryParam Annotation:

Binds the value of a HTTP query parameter to a resource method parameter or resource class field or resource class bean property. A default value can be specified using the @DefaultValue annotation. Syntax:
@QuaryParam(“param1”) @DefaultValue(“defaultVal”) datatype varName
Ex:
@QuaryParam(“param1”) @DefaultValue(“defaultVal”) datatype varName

Ex:
@GET
@Path("eligibleBooks")
public ProductInfos getEligibleProducts(
 @QueryParam("category") @DefaultValue("JAVA") String category,
 @QueryParam(bookId) String bookId, 
 @QueryParam("bookName") String bookName,
 @QueryParam("accountType") String accountType,
 @QueryParam("countryCode") @DefaultValue("US") String countryCode) {
    
 //TODO method body
}

Target URL: /eligibleBooks?category=JAVA&bookId=1234&bookName=abcd123&accountType=technology&countryCode=IN

Saturday, 9 November 2013

Spring Batch Flat File Reader reads multiple files


Problem :
Implement a standalone flight search engine that lists matching flights for a traveller who is looking to fly between 2 locations on a given date. The input locations should be in 3-letter location code format as present in the airlines flight data.
Specifications: You are given 3 CSV files, each containing the available flights data for British Airways, Lufthansa Airlines and Air France respectively.
Each file contains the following fields:
  1. FLIGHT_NUM - Unique flight number, starting with 2-letter airline code.
  2. DEP_LOC - Departure location code of the flight.
  3. ARR_LOC - Arrival location code of the flight.
  4. VALID_TILL - Date (DD-MM-YYYY) till which each flight is available. It means that this flight would fly once every day till this date.
  5. FLIGHT_TIME - Local time (HHmm) at which the flight departs from the departure location.
  6. FLIGHT_DURN - Flight duration (HH.mm) from departure location to the arrival location.
  7. FARE - This is the fare of the flight per person in $ (US Dollars).
Following are the constraints should apply:
  • The engine should accept 4 input parameters: Departure Location, Arrival Location, Flight Date, Output Preference. “Output Preference” is a String suggesting whether the flight results should be sorted only by fare, or by both fare and flight duration.
  • The engine should then search for the flights in all 3 CSV files, and list the aggregate results on standard output, sorted by Fare and Flight Duration, depending on “Output Preference”.
  • The engine should be written considering that there could be more CSV files in future, and each CSV file might contain more data than present.
  • If the Departure location or/and Arrival location is not present in any of the CSV files, then engine should return a user friendly error.
  • If there are no flights available for the user entered input parameters, then engine should return a different user friendly error.
Input data: The following PIPE delimited sample data is given to you. You are allowed to extend on this data and create heavier input files. Flight_Details_1.csv
FLIGHT_NUM|DEP_LOC|ARR_LOC|VALID_TILL|FLIGHT_TIME|FLIGHT_DURN|FARE
AF299|FRA|LHR|20-11-2010|0600|4.10|480
AF118|DUB|MUC|21-12-2010|1410|5.40|580
AF371|AMS|MAD|30-11-2010|1210|3.45|320
AF453|BOS|CDG|20-11-2010|1350|7.30|1000
AF544|BOM|LHR|10-12-2010|1150|8.10|950
AF271|AMS|MAD|27-10-2010|1100|3.30|500
AF249|JFK|LHR|01-12-2010|1550|8.40|1030
Flight_Details_2.csv
FLIGHT_NUM|DEP_LOC|ARR_LOC|VALID_TILL|FLIGHT_TIME|FLIGHT_DURN|FARE
AF299|FRA|LHR|20-11-2010|0600|4.10|480
AF118|DUB|MUC|21-12-2010|1410|5.40|580
AF371|AMS|MAD|30-11-2010|1210|3.45|320
AF453|BOS|CDG|20-11-2010|1350|7.30|1000
AF544|BOM|LHR|10-12-2010|1150|8.10|950
AF271|AMS|MAD|27-10-2010|1100|3.30|500
AF249|JFK|LHR|01-12-2010|1550|8.40|1030
Flight_Details_3.csv
FLIGHT_NUM|DEP_LOC|ARR_LOC|VALID_TILL|FLIGHT_TIME|FLIGHT_DURN|FARE
BA123|DEL|AMS|12-10-2010|0050|8.00|950
BA412|BOS|CDG|31-12-2010|0210|7.50|800
BA413|BOS|AMS|30-11-2010|1530|7.00|750
BA111|LHR|PEK|30-10-2010|2340|12.50|1200
BA765|LHR|BOM|31-12-2010|1420|8.50|825
BA322|CDG|NRT|15-11-2010|0010|13.00|1150
BA438|DEL|AMS|30-11-2010|1325|10.50|920
Problem Solution:
  1. Create a Data TransferObject/ValueObject which can hold the properties of the flight.
  2. Create another Data Transfer Objects which suits for search criteria.
  3. Create a mapper class which splits the each line at specified delimiter and map the tokens to ValueObject.
  4. Create a Writer class and implement the business logic.
  5. Make necessary configurations to run the program.
  6. Create a client program and to provide search criteria.
FlightDetailsVO.java: A Data Transfer/Value Object which has the flight properties.
package com.flight.dto;

import java.io.Serializable;
import java.util.Date;

public class FlightDetailsVO implements Serializable {

 /**
  * 
  */
 private static final long serialVersionUID = 1L;
 
 private String flightNum;
 private String depLoc;
 private String arrLoc;
 private Date validTill;
 private String flightTime;
 private double flightDuration;
 private double fare;
 
 public String getFlightNum() {
  return flightNum;
 }
 public void setFlightNum(String flightNum) {
  this.flightNum = flightNum;
 }
 public String getDepLoc() {
  return depLoc;
 }
 public void setDepLoc(String depLoc) {
  this.depLoc = depLoc;
 }
 public String getArrLoc() {
  return arrLoc;
 }
 public void setArrLoc(String arrLoc) {
  this.arrLoc = arrLoc;
 }
 public Date getValidTill() {
  return validTill;
 }
 public void setValidTill(Date validTill) {
  this.validTill = validTill;
 }
 public String getFlightTime() {
  return flightTime;
 }
 public void setFlightTime(String flightTime) {
  this.flightTime = flightTime;
 }
 public double getFlightDuration() {
  return flightDuration;
 }
 public void setFlightDuration(double flightDuration) {
  this.flightDuration = flightDuration;
 }
 public double getFare() {
  return fare;
 }
 public void setFare(double fare) {
  this.fare = fare;
 }
 
 @Override
 public String toString() {
  return flightNum + "|" 
    + depLoc + "|" 
    + arrLoc + "|" 
    + validTill + "|" 
    + flightTime + "|" 
    + flightDuration + "|" 
    + fare;
 }
 
}

SearchVO.java: It is used to pass the user given search criteria to search engine.
package com.flight.dto;

import java.io.Serializable;
import java.util.Date;

public class SearchVO implements Serializable {
 
 /**
  * 
  */
 private static final long serialVersionUID = 1L;
 
 private String depLoc;
 private String arrLoc;
 private Date flightDate;
 private String sortedBy;
 
 public String getDepLoc() {
  return depLoc;
 }
 public void setDepLoc(String depLoc) {
  this.depLoc = depLoc;
 }
 public String getArrLoc() {
  return arrLoc;
 }
 public void setArrLoc(String arrLoc) {
  this.arrLoc = arrLoc;
 }
 public Date getFlightDate() {
  return flightDate;
 }
 public void setFlightDate(Date flightDate) {
  this.flightDate = flightDate;
 }
 public String getSortedBy() {
  return sortedBy;
 }
 public void setSortedBy(String sortedBy) {
  this.sortedBy = sortedBy;
 }

}
FlightSearchFieldSetMapper.java: It’s a field set mapper and it will fetch all the lines from multiple files and will set to a list and will provide the same to writer.
package com.flight.mapper;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

import org.springframework.batch.item.file.mapping.FieldSetMapper;
import org.springframework.batch.item.file.transform.FieldSet;
import org.springframework.validation.BindException;

import com.flight.dto.FlightDetailsVO;

public class FlightSearchFieldSetMapper implements FieldSetMapper<FlightDetailsVO> {

 private static final String PARSE_ERROR = "PARSE_ERROR";
 @Override
 public FlightDetailsVO mapFieldSet(FieldSet fieldSet) throws BindException {
  
  FlightDetailsVO flightDetailsVO = new FlightDetailsVO();
  
  flightDetailsVO.setFlightNum(fieldSet.readString(0));
  flightDetailsVO.setDepLoc(fieldSet.readRawString(1));
  flightDetailsVO.setArrLoc(fieldSet.readRawString(2));
  
  String validTill = fieldSet.readRawString(3);

  try {
   flightDetailsVO.setValidTill(convertStringToDate(validTill));
  } catch(ParseException pe) {
   flightDetailsVO.setFlightNum(PARSE_ERROR);
  }
  
  flightDetailsVO.setFlightTime(fieldSet.readRawString(4));
           flightDetailsVO.setFlightDuration(Double.parseDouble(fieldSet.readRawString(5)));
  flightDetailsVO.setFare(Double.parseDouble(fieldSet.readRawString(6)));
  
  return flightDetailsVO;
 }
 
 public Date convertStringToDate(String strDate) throws ParseException {
  
  SimpleDateFormat formatter = new SimpleDateFormat("dd-MM-yyyy");
  return formatter.parse(strDate);
 }
 
 public Date converStringToTime(String StrTime) throws ParseException {
  SimpleDateFormat formatter = new SimpleDateFormat("HH:mm");
  return formatter.parse(StrTime);
 }
 
}
FlightSearchItemWriter.java: It’s Item Writer, in our program it will act as search engine.
package com.flight.writer;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

import org.springframework.batch.item.ExecutionContext;
import org.springframework.batch.item.ItemStream;
import org.springframework.batch.item.ItemStreamException;
import org.springframework.batch.item.ItemWriter;

import com.flight.dto.FlightDetailsVO;
import com.flight.dto.SearchVO;

public class FlightSearchItemWriter implements ItemWriter<FlightDetailsVO>, ItemStream {
 
 private SearchVO searchVO;

 @Override
 public void write(List<? extends FlightDetailsVO> flightDetailsVOList) throws Exception {
  ArrayList<FlightDetailsVO> flightDetailsList = new ArrayList>FlightDetailsVO<(flightDetailsVOList);
  
  ArrayList<FlightDetailsVO> availFlightsList = new ArrayList>FlightDetailsVO<();
  
  if(getSearchVO() != null) {
   for(FlightDetailsVO currVO : flightDetailsList) {
    if(currVO.getValidTill().compareTo(searchVO.getFlightDate()) >= 0) {
     availFlightsList.add(currVO);
    }
   }
   
   if(availFlightsList != null && availFlightsList.size() > 0) {
    System.out.println("FLIGHT_NUM|DEP_LOC|ARR_LOC|VALID_TILL|FLIGHT_TIME|FLIGHT_DURN|FARE");
    System.out.println("------------------------------------------------------------------");
    
    Collections.sort(availFlightsList, new Comparator<FlightDetailsVO>() {  
        @Override  
        public int compare(FlightDetailsVO o1, FlightDetailsVO o2) {  
            int fareComp = Double.compare(o1.getFare(), o2.getFare());  
            if(searchVO.getSortedBy().equalsIgnoreCase("both")) {
             if (fareComp != 0) {  
                 return fareComp;  
             }  
             int durComp = Double.compare(o1.getFlightDuration(), o2.getFlightDuration());
             return durComp;
            } 
            return fareComp; 
        }  
    });    
    for(FlightDetailsVO availFlightVO : availFlightsList) {
     System.out.println(availFlightVO.toString());
    }
   }
  } else {
   System.out.println("Search criteria is not provided.....");
  }
 }

 @Override
 public void close() throws ItemStreamException {
  
 }

 @Override
 public void open(ExecutionContext arg0) throws ItemStreamException {
  
 }

 @Override
 public void update(ExecutionContext arg0) throws ItemStreamException {
  
 }

 public SearchVO getSearchVO() {
  return searchVO;
 }

 public void setSearchVO(SearchVO searchVO) {
  this.searchVO = searchVO;
 }


}
flight-seaarch-context.xml: It’s a configuration file, which contain all spring beans definitions.

    
 
  
   
    
   
  
 

 
  
  
 
  
 
  
  
  
   
    
     
      
     
    
    
     
    
   
  
 
 
 
  
 
 
 

 
  
 
 
 
  
 



FlightSearchRun.java: Its client program for Flight Search engine.
package com.flight.main;

import java.text.SimpleDateFormat;

import org.springframework.batch.core.Job;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.flight.dto.SearchVO;
import com.flight.writer.FlightSearchItemWriter;

public class FlightSearchRun {
 
 public static void main(String[] args) {
  String[] springConfig  = {"resources/xml/flight-seaarch-context.xml"};
  
  ApplicationContext context = new ClassPathXmlApplicationContext(springConfig);
  
  JobLauncher jobLauncher = (JobLauncher) context.getBean("jobLauncher");
  Job job = (Job) context.getBean("flightSearchJob");
  try {
   SearchVO searchVO = new SearchVO();
   searchVO.setDepLoc("DUB");
   searchVO.setArrLoc("MUC");
   searchVO.setSortedBy("both");
   
   SimpleDateFormat formatter = new SimpleDateFormat("dd-MM-yyyy");
   searchVO.setFlightDate(formatter.parse("21-12-2013"));
   
   FlightSearchItemWriter writer = (FlightSearchItemWriter) context.getBean("flightSearchItemWriter");
   writer.setSearchVO(searchVO);
   
   JobExecution execution = jobLauncher.run(job, new JobParameters());
   System.out.println("Exit Status : " + execution.getStatus());

  } catch (Exception e) {
   e.printStackTrace();
  }
 }

}

Tuesday, 29 October 2013

Spring method Replacement


Need of method replacement: Suppose some bug is identified during last phase of testing, analysed the same and found that the root cause is third party jar/API. To rectify the same we need to look for update on this API. Since our testing is in last phase and there is no time to analyse the impact of using updated API in our system. We can use this feature of spring.

Simple Requirement:
Now there is a requirement to wish a customer saying “Dear Customer, Good Morning/Good Afternoon/Good Evening….”, based on the customer login time.

package com.thirdpartyapi;

import java.util.Calendar;

public class WishCustomer {
   
    public void sayWishes() {
       Calendar cal = Calendar.getInstance();
       int currHour = cal.get(Calendar.HOUR_OF_DAY);
      
       if(currHour < 12) {
           System.out.println("Dear Valued Customer, Good Morning.....");
       } else if(currHour >= 12 && currHour < 18) {
           System.out.println("Dear Valued Customer, Good Afternoon.....");
       } else if(currHour >= 18 && currHour < 24) {
           System.out.println("Dear Valued Customer, Good Evening.....");
       }
    }

}

Spring configuration:
<bean id=”wishCustomer” class="com.thirdpartyapi.WishCustomer" />

The above code given by third party API has clearly satisfied the requirement. Now the business has enhanced and got the client over the world.

Problem: Irrespective of the user time zone, the above class wishes the customer based on the application server time zone.
Solution: Need to override sayWishes() method of WishCustomer class alone without impacting of other logic in this API.
package com.saywish;

import java.lang.reflect.Method;
import java.util.Calendar;
import java.util.TimeZone;

import org.springframework.beans.factory.support.MethodReplacer;

public class WishCustomerReplacer implements MethodReplacer {

public Object reimplement(Object arg0, Method arg1, Object[] arg2) throws Throwable {
      
       Calendar cal = Calendar.getInstance();
       cal.setTimeZone(TimeZone.getTimeZone("IST"));
       //IST is hard coded, can be pass dynamically based on user location
       int currHour = cal.get(Calendar.HOUR_OF_DAY);
      
       if(currHour < 12) {
           System.out.println("Dear Valued Customer, Good Morning.....");
       } else if(currHour >= 12 && currHour < 18) {
           System.out.println("Dear Valued Customer, Good Afternoon.....");
       } else if(currHour >= 18 && currHour < 24) {
           System.out.println("Dear Valued Customer, Good Evening.....");
       }
      
       return null;
    }
}

The yellow highlighted is the customization, setting the time zone value to the Calendar instance based on the user country time zone.

Spring configuration for Method Replacer:
<bean id="wishCustomer" class="com.thirdpartyapi.WishCustomer" >
       <replaced-method name="sayWishes" replacer="wishCustomerReplacer" />
</bean>
      
<bean id="wishCustomerReplacer" calss="com.saywish.WishCustomerReplacer" />

Sunday, 27 October 2013

Setter Injection Vs Constructor Injection


Setter-Injection
The basic-idea is that class have a no argument-constructor which creates the object with “reasonable-defaults”. The user of the object can then call setters on the object to override the collaborators of the object in order to wire the object graph together or to replace the key collaborators with test-doubles.

Constructor-Injection
The basic idea with constructor-injection is that the object has no defaults and instead you have a single constructor where all of the collaborators and values need to be supplied before you can instantiate the object.
Fundamental difference between setter and constructor injection, as their name implies is how dependency is injected.  Setter injection in Spring uses setter methods like setDependency() to inject dependency on any bean managed by Spring's IOC container. On the other hand constructor injection uses constructor to inject dependency on any Spring managed bean.
Constructor based dependency injection forces to set the property. However the property cannot be changed later on. Setter based dependency injection allows reconfiguration later on. We can have both Constructor based dependency injection and Setter based dependency injection also together.

  •   Setter Injection is more readable than constructor injection in Spring configuration file. Since setter method has name e.x. setReporotService() by reading Spring XML configuration file you know which dependency you are setting. While in constructor injection, since it uses index to inject dependency, it’s not as readable as setter injection and you need to refer either Java documentation or code to find which index corresponds to which property.
  •     Another difference between setter vs constructor injection in Spring and one of the drawback of setter injection is that it does not ensures dependency Injection. You cannot guarantee that certain dependency is injected or not, which means you may have an object with incomplete dependency. On other hand constructor Injection does not allow you to construct object, until your dependencies are ready.
  •   One more drawback of setter Injection is Security. By using setter injection, you can override certain dependency which is not possible which is not possible with constructor injection because every time you call constructor, a new object is gets created.
In Summary both Setter Injection and Constructor Injection has their own advantage and disadvantage. Good thing about Spring is that it doesn't restrict to use either Setter Injection or Constructor Injection and free to use both of them in one Spring configuration file. Use Setter injection when number of dependency is more or if readability needed. Use Constructor Injection when Object must be created with all of its dependencies.

Constructor Injection:

ConstructorInjection.java:
package com.scb.spring;

public class ConstructorInjection {
  private int bankid;
  private String name;
  private double salary;
  private SupportingInfo supportingInfo;

  public ConstructorInjection(int bankid, String name, double salary,                                                    SupportingInfo supportingInfo) {
    this.bankid = bankid;
    this.name = name;
    this.salary = salary;
    this.supportingInfo = supportingInfo;
  }

  public String getEmpInfo() {
    return "Bank ID :"+ getBankid() +", Name :"+ name +", Salary :"+salary ;
  }

  public int getBankid() {
    return bankid;
  }
  public String getName() {
      return name;
  }

  public double getSalary() {
      return salary;
  }
   public SupportingInfo getSupportingInfo() {
      return supportingInfo;
  }
}

 

context.xml
<beans>
 <bean id="constructorInjection" class="com.scb.spring.ConstructorInjection">
   <constructor-arg index="0"type=”int” value="1361606" />
   <constructor-arg index="1" type=”java.lang.String” value="Paramesh" />
   <constructor-arg index="2" type=”double” value="1234" />
   <constructor-arg index="3" ref=”supportingInfo” />
  </bean>
  <bean id=”supportingInfo” class=”com.scb.spring.SupportingInfo” />
</beans>

 

Setter Injection:

ConstructorInjection.java:
package com.scb.spring;

public class ConstructorInjection {
 
  private int bankid;
  private String name;
  private double salary;
  private SupportingInfo supportingInfo;

  public String getEmpInfo() {
    return "Bank ID :"+ getBankid() +", Name :"+ name +", Salary :"+salary ;
  }
  public void setBankid(int bankid) {
      this.bankid = bankid;
  }

  public int getBankid() {
    return bankid;
  }
  public void setName(String name) {
        this.name = name;
  }
  public String getName() {
      return name;
  }
  public void setSalary(double salary) {
       this.salary = salary;
  }
  public double getSalary() {
      return salary;
  }
  public SupportingInfo getSupportingInfo() {
      return supportingInfo;
  }
  public void setSupportingInfo(SupportingInfo supportingInfo) {
      this.supportingInfo = supportingInfo;
  }
}

context.xml
<bean id=" constructorInjection" class="com.scb.spring.ConstructorInjection">
   <property name="
bankid" value="1361606" />
   <property name="
name" value="Paramesh" />
   <property name="salary" value="1234" />
   <property name="supportingInfo" ref="supportingInfo" />
  </bean>
  <bean id=”supportingInfo” class=”com.scb.spring.SupportingInfo” />
</beans>

Injecting Collection Objects

TO inject Java Collection types List, Set, Map, and Properties etc, Spring offers four types of collection configuration elements which are as follows:
Data Type
Element
Description
java.util.List/
array
<list>
Helps to inject a list of values, allowing duplicates.
java.util.Set
<set>
Helps to inject a set of values, without duplicates.
java.util.Map
<map>
Helps to inject a collection of name-value pairs where name and value can be of any type.
java.util.Properties
<props>
Helps to inject a collection of name-value pairs where the name and value are both Strings.
Another Bean /
Any java class
<ref>
Helps to inject another bean.