Tuesday, February 22, 2011

Semaphores

What is Semaphore?

Semaphore is the new class introduced as a part of concurrency package in jdk 1.5 .It maintains a set of permits which gets acquired by thread before using some functionality.

It is a technique to protect your critical resource from being used by more than 'N' threads simultaneously.Semaphore maintains number of available permits.Whenever a thread wants to use some shared resource,maintained by semaphore.It asks semaphore for the permit.If permit is available thread can use the shared resource,otherwise it will wait till some other thread releases the permit or come out without using the shared resource.

Use cases where it can be used.

Semaphore with number of virtual permits more than 1 can be used as shared critical resource pools like database connection pool,license pool,worker thread pool and others.

Semaphore with number of virtual permits as 1 can be used as a lock.[Though I feel its waste of semaphore as we can use synchronized method/block and other techniques].You use it if you have permit to use it.

Mutex is often referred as Semaphore with number of virtual permits as 1,also called as Binary Semaphore.However there are marked differences between them.Semaphore does not track which thread has taken the permit.Semaphore does not track ownership of permits.However mutex keeps track of it.In case of Semaphore any thread can release the permit.But mutex can release only what it owns.

Important methods of semaphore

Following are important methods of Semaphore.These are:-
  • acquire()- When thread invokes acquire() on semaphore.If a token is available,it acquires the token.Other wise it goes into dormant state till the time it gets the token or some other thread has interrupted it.

  • acquire(int permits) -Its similar to acquire() except we can specify number of tokens to be required.Usually used when we require more than one token.

  • release()-When this method is invoked.It releases the permit and returns it back to Semaphore. Excerpt from Java API
    "There is no requirement that a thread that releases a permit must have acquired that permit by calling
    acquire. Correct usage of a semaphore is established by programming convention in the application."

  • release(int permits)-Similar to release() except that we are specifying number of tokens to be released.

  • tryAcquire()-acquire() method is kind of a blocking call.Calling thread will wait till it aquires the permit or some other thread has interrupted it.In case of tryAcquire() thread does not wait.It checks whether any token is available,if its available it picks the token and return with the value as true.Otherwise it come out with value as false.

  • tryAcquire(long timeUnits,TimeUnit unit)-Similar to tryAcquire() except that it wait for time period mentioned in method parameters before coming out.

Code snippet and license use case

UseCase-Custom product is configured for '2' concurrent users.What will happen if at a particular time more than '2' concurrent users try to use the product.For such conditions semaphore is plugged in the product and on each utility method ,thread requires a token.If token is not there,an exception is thrown stating "Please try the product after sometime".


CustomProduct.java having semaphore implementation for using the product
package com.kunaal.semaphore.product;

import java.util.concurrent.Semaphore;

/**
 * Custom fund transfer product.
 * Product is designed in such a way that number of concurrent user license
 * information is captured during object creation.
 * 
 * Semaphore implementation is plugged at all utility methods 
 * so that if we have more number of concurrent users than 
 * that provided at the object creation.
 * 
 * Other users will wait........
 * 
 * @author Kunaal A Trehan
 */
public class CustomProduct {

 /**
  * Semaphore object acting as a gatekeeper preventing users for using 
  * this product if no of users is more than the allowed 
  * concurrent users 
  */
 private final Semaphore semaphore; 
 
 /**
  * Variable for capturing number of concurrent users
  */
 private int concurrentUserNum;
 
 /**
  * Constructor
  * @param noOfConcurrentUsers
  */
 public CustomProduct(int noOfConcurrentUsers){
  semaphore=new Semaphore(noOfConcurrentUsers, true);
  concurrentUserNum=noOfConcurrentUsers;
 }
 
 /**
  * Method using semaphore preventing users more than concurrent users
  * configured to use this method.
  * 
  * @param threadName
  * @throws Exception
  */
 public void useProduct(String threadName) throws Exception{
  boolean acquireVal = semaphore.tryAcquire();
  String newLine = System.getProperty("line.separator");

  
  if(acquireVal){
   System.out.println("Thread with name-"+ threadName + " is using the product");
   Thread.sleep(2*1000);
   semaphore.release();
  }else{
   throw new Exception("[" +threadName + " can't use this product right now." + newLine+
     concurrentUserNum +" users can use this product concurrently" + newLine+ 
     " Please try after some time ]");
  }
 }
}


Runnable implementation invoking useProduct() method CustomProduct
package com.kunaal.semaphore.runnable;

import com.kunaal.semaphore.product.CustomProduct;


/**
 * Custom runnable invoking methods on custom product.
 * 
 * @author Kunaal A Trehan
 */
public class RunImpl implements Runnable {
 
 /**
  * Variable for custom product
  */
 private CustomProduct customProd;

 /**
  * Constructor
  * @param customProd
  */
 public RunImpl(CustomProduct customProd){
  this.customProd=customProd;
 }
 
 /**
  * Overridden run method
  */
 @Override
 public void run() {
  try{
   String name = Thread.currentThread().getName();
   customProd.useProduct(name);
  }catch(Exception e){
   System.out.println("Exception message is-" +e.getMessage());
  }
 }

 /**
  * @return the customProd
  */
 public CustomProduct getCustomProd() {
  return customProd;
 }

 /**
  * @param customProd the customProd to set
  */
 public void setCustomProd(CustomProduct customProd) {
  this.customProd = customProd;
 }
 
}


Main class showing what will happen when more than 2 threads try to use the product
package com.kunaal.semaphore;

import com.kunaal.semaphore.product.CustomProduct;
import com.kunaal.semaphore.runnable.RunImpl;

/**
 * This example states the use of semaphore.
 * Here thread tries to acquire token from semaphore.If token is available
 * thread acquires it and uses the product.
 * Otherwise it throws the exception that token is not available
 * 
 * In the main we create 4 threads.After starting 3 threads.
 * Current thread is put to sleep for 4 sec so that during that 
 * time one of threads using the token releases the token 
 * and thread no04 can use the product.
 * 
 * @author Kunaal A Trehan
 *
 */
public class NonWaitingExample {

 /**
  * @param args
  * @throws InterruptedException 
  */
 public static void main(String[] args) throws InterruptedException {
  CustomProduct product=new CustomProduct(2);
  RunImpl runImpl=new RunImpl(product);
  
  Thread t1=new Thread(runImpl,"User-1");
  Thread t2=new Thread(runImpl,"User-2");
  Thread t3=new Thread(runImpl,"User-3");
  Thread t4=new Thread(runImpl,"User-4");
  
  t1.start();
  t2.start();
  t3.start();
  System.out.println("Time before main thread sleeps-"+System.currentTimeMillis());
  Thread.currentThread().sleep(4*1000);
  System.out.println("Time after main thread finishes sleep-"+System.currentTimeMillis());
  t4.start();
  
 } 
}


Output of the main programm
Thread with name-User-1 is using the product
Thread with name-User-2 is using the product
Time before main thread sleeps-1298361121828
Exception message is-[User-3 can't use this product right now.
2 users can use this product concurrently
 Please try after some time ]
Time after main thread finishes sleep-1298361125828
Thread with name-User-4 is using the product

Friday, February 18, 2011

Logger information

What is Logger?

Logger is kind of custom auditing tool through which you can trace back the execution flow.In application we can use it the way we want as a debugger during development mode and as a info during production mode.
Moving from one level to another does not allow binary changes .It can be controlled by external properties/configuration file.

Log4j Infrastructure

In order to define logging we need following objects.These are:-

i)Logger is an entity identified by a name and follows the hierarchy
   i.e
   Logger for com.kunaal.X is parent of logger for com.kunaal.sub.Y
   Logger can customize the priority level.Since all loggers are child of root logger.Properties defined here over ride the inherited settings of the root logger.

e.g. Lets suppose we have 3 packages in our project
  -com.kunaal.domain
  -com.kunaal.service
  -com.kunaal.dao

   We have defined root logger with priority level as INFO.
   We have defined logger for com.kunaal.dao with priority level as DEBUG.

    So in our application following logging will happen
   -For all classes in com.kunaal.dao. all logging information with priority level as DEBUG and above it will be logged in the appender.
   -For all classes in com.kunaal.domain and com.kunaal.service.There is no specific logging criteria defined.So it will inherit the information from root logger.So logging information with priority level as INFO and above will be logged.
  
ii)Appenders is an object which sends the logging information to the final destination.Log4J has provided many appenders.Some of the frequently used appenders are as follows.


   -File Appender lets you log the information in a file.
   -Console Appender lets you log the information to the console.By default its System.out.
   -JDBC Appender lets you log the information to the database table.
   -JMS Appender lets you publish the information to JMS topic
   -Rolling File Appender lets you log the information ina file.Once file reaches up the max size.It backs up the file and create a new file.

iii)Layout lets you customize the format in which you log the information.

Root Logger

Root logger is at the top of logger hierarchy and all other loggers are child loggers of the root logger.If we donot specify any custom settings for the child logger,child logger inherit the setting information from the root logger or the logger nearest to it.

Root logger always exits and there is a utility method Logger.getRootLogger() to fetch it.

Inheritance in logger

Log4j incorporates inheritance among loggers.If we have not specified any logger for the particular class,it fetches logger information from immediate parent going upto root logger.
This is illustrated by testInheritance() method in LoggerExample.There we have not defined any logger for the Logger for LoggerExample.So it inherits the information from the immediate parent which is root logger in this case.More information is available in method comments.



Logging output to multiple appenders

Logging lets us write the log information to multiple appenders if reqd.All we need is to define the appender information in the logger.

In the Logger Example mentioned below we have added a file appender.So for logger "com.kunaal.logger.domain" logging information will go to file appender besides the console appender specified in root logger.

What is LogManager and how logger is created

LogManager is the internal class  used by log4j for getting loggers and other operations.LogManager has a data structure of type map to persist logger against classname.When we do Logger.getLogger(Class....).It checks in the internal data structure whether any logger for this class is created or not.If its created it returns the same,otherwise creates a new logger and persist it in the internal map. This is illustrated by testLogMgr() in LoggerExample

Different log levels

There are six log levels supported by log4j.These are as follows
  • Trace
  • Debug                           
  • Info
  • Warn
  • Error
  • Fatal
These log levels determine which log information is actually logged.Logging rule states that information corresponding to log level of the logger or higher than that is logged to the appender i.e.
If logging level for a logger is defined as INFO,then except debug and trace rest all information will be logged.

Why logging is preferred over SOP?

Reason being its not possible with System.out.println(SOP)  to turn it off/on.It can be either totally on or off.Moreover once placed inside the code it will always be run.Its not possible to let few statements run and few does not run.Moreover this setting can't externalized and controlled as we can do with priority level in log4j using configuration /properties file.

With log4j we can customize the layout and information can be sent to different appenders like file appender/jms appender/mail appender /console appender which is not possible with SOP.


In log4j there is a hierarchy among loggers which does not exist in SOP.

Logging example

log4j.xml-Configuration file for logging
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">

<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
        
  <!-- Console appender for logging -->       
 <appender name="appender" class="org.apache.log4j.ConsoleAppender">
     <layout class="org.apache.log4j.PatternLayout">
        <param name="ConversionPattern" value="[%t] %-5p %c{2} %x - %m%n"/>
     </layout>
   </appender>
   
   <!-- File appender for logging.This is used currently for logger com.kunaal.logger.domain -->
   <appender name="fileAppender" class="org.apache.log4j.FileAppender">
    <param name="file" value="example.log"/>
    <param name="additivity" value="false"/>
     <layout class="org.apache.log4j.PatternLayout">
        <param name="ConversionPattern" value="[%t] %-5p %c{2} %x - %m%n"/>
     </layout>    
   </appender>

 <!-- 
  Creating the child logger for all classes under service package
  with debug level as info.
  So all the messages with priority level less than 
  info i.e debug and trace won't be logged
  -->
 <logger name="com.kunaal.logger.service">
        <level value="info"/>
    </logger>

 <!-- 
  Creating the child logger for all classes under domain package
  with priority level as trace.
  So all the messages will be logged as trace is at the lowest rank.
  -->
 <logger name="com.kunaal.logger.domain">
        <level value="trace"/>
        <appender-ref ref="fileAppender"/>
    </logger>
    
    <!-- 
     Root logger with appender information
     All corresponding loggers are child of root logger
     So they will inherit the properties of root logger
     unless over ridden as in case of service and domain logger
     -->
   <root>
     <priority value ="debug"/>
     <appender-ref ref="appender"/>
     
   </root>

</log4j:configuration>


Domain class for EducationalInfo
package com.kunaal.logger.domain;

/**
 * Class for capturing educational details of an employee
 * 
 * @author Kunaal A Trehan
 *
 */
public class EducationalInfo {
 
 //Boolean variable capturing whether person is under graduate or not
 private boolean isUG;
 
 //Boolean variable capturing whether person is post graduate or not
 private boolean isPG;
 
 //String variable capturing specialization for under graduation
 private String ugSpecialization;
 
 //String variable capturing specialization for post graduation
 private String pgSpecialization;
 
 //String variable capturing other specialization details
 private String others;

 /**
  * Default constructor
  */
 public EducationalInfo() {
  super();
 }

 /**
  * @return the isUG
  */
 public boolean isUG() {
  return isUG;
 }

 /**
  * @param isUG the isUG to set
  */
 public void setUG(boolean isUG) {
  this.isUG = isUG;
 }

 /**
  * @return the isPG
  */
 public boolean isPG() {
  return isPG;
 }

 /**
  * @param isPG the isPG to set
  */
 public void setPG(boolean isPG) {
  this.isPG = isPG;
 }

 /**
  * @return the ugSpecialization
  */
 public String getUgSpecialization() {
  return ugSpecialization;
 }

 /**
  * @param ugSpecialization the ugSpecialization to set
  */
 public void setUgSpecialization(String ugSpecialization) {
  this.ugSpecialization = ugSpecialization;
 }

 /**
  * @return the pgSpecialization
  */
 public String getPgSpecialization() {
  return pgSpecialization;
 }

 /**
  * @param pgSpecialization the pgSpecialization to set
  */
 public void setPgSpecialization(String pgSpecialization) {
  this.pgSpecialization = pgSpecialization;
 }

 /**
  * @return the others
  */
 public String getOthers() {
  return others;
 }

 /**
  * @param others the others to set
  */
 public void setOthers(String others) {
  this.others = others;
 }

 /**
  * Overridden toString() method
  */
 @Override
 public String toString() {
  return "EducationalInfo [isUG=" + isUG + ", isPG=" + isPG
    + ", ugSpecialization=" + ((ugSpecialization==null)?"NA":ugSpecialization)
    + ", pgSpecialization=" + ((pgSpecialization==null)?"NA":pgSpecialization)
    + ", others=" + ((others==null)?"NA":others) + "]";
 }

 /**
  * Overridden hash code method
  */
 @Override
 public int hashCode() {
  final int prime = 31;
  int result = 1;
  result = prime * result + (isPG ? 1231 : 1237);
  result = prime * result + (isUG ? 1231 : 1237);
  result = prime * result + ((others == null) ? 0 : others.hashCode());
  result = prime
    * result
    + ((pgSpecialization == null) ? 0 : pgSpecialization.hashCode());
  result = prime
    * result
    + ((ugSpecialization == null) ? 0 : ugSpecialization.hashCode());
  return result;
 }

 /**
  * Overridden equals method
  */
 @Override
 public boolean equals(Object obj) {
  if (this == obj)
   return true;
  if (obj == null)
   return false;
  if (getClass() != obj.getClass())
   return false;
  EducationalInfo other = (EducationalInfo) obj;
  if (isPG != other.isPG)
   return false;
  if (isUG != other.isUG)
   return false;
  if (others == null) {
   if (other.others != null)
    return false;
  } else if (!others.equals(other.others))
   return false;
  if (pgSpecialization == null) {
   if (other.pgSpecialization != null)
    return false;
  } else if (!pgSpecialization.equals(other.pgSpecialization))
   return false;
  if (ugSpecialization == null) {
   if (other.ugSpecialization != null)
    return false;
  } else if (!ugSpecialization.equals(other.ugSpecialization))
   return false;
  return true;
 }

}

Domain class for Employee
package com.kunaal.logger.domain;

import org.apache.log4j.Logger;

/**
 * @author Kunaal A Trehan
 *
 */
public class Employee {

 //Logger variable
 private static final Logger LOGGER=Logger.getLogger(Employee.class);
 
 //Variable for employee's personal info
 private PersonalInfo personalInfo;
 
 //Variable for employee's family info
 private FamilyInfo familyInfo;
 
 //Variable for employee's educational info
 private EducationalInfo eduInfo;

 /**
  * @return the personalInfo
  */
 public PersonalInfo getPersonalInfo() {
  return personalInfo;
 }

 /**
  * @param personalInfo the personalInfo to set
  */
 public void setPersonalInfo(PersonalInfo personalInfo) {
  this.personalInfo = personalInfo;
 }

 /**
  * @return the familyInfo
  */
 public FamilyInfo getFamilyInfo() {
  return familyInfo;
 }

 /**
  * @param familyInfo the familyInfo to set
  */
 public void setFamilyInfo(FamilyInfo familyInfo) {
  this.familyInfo = familyInfo;
 }

 /**
  * @return the eduInfo
  */
 public EducationalInfo getEduInfo() {
  return eduInfo;
 }

 /**
  * @param eduInfo the eduInfo to set
  */
 public void setEduInfo(EducationalInfo eduInfo) {
  this.eduInfo = eduInfo;
 }

 /**
  * Overridden toString() method
  */
 @Override
 public String toString() {
  return "Employee [personalInfo=" + personalInfo + ", familyInfo="
    + familyInfo + ", eduInfo=" + eduInfo + "]";
 }

 /**
  * Overridden hash code method
  */
 @Override
 public int hashCode() {
  final int prime = 31;
  int result = 1;
  result = prime * result + ((eduInfo == null) ? 0 : eduInfo.hashCode());
  result = prime * result
    + ((familyInfo == null) ? 0 : familyInfo.hashCode());
  result = prime * result
    + ((personalInfo == null) ? 0 : personalInfo.hashCode());
  return result;
 }

 /**
  * Overridden equals method
  */
 @Override
 public boolean equals(Object obj) {
  if (this == obj)
   return true;
  if (obj == null)
   return false;
  if (getClass() != obj.getClass())
   return false;
  Employee other = (Employee) obj;
  if (eduInfo == null) {
   if (other.eduInfo != null)
    return false;
  } else if (!eduInfo.equals(other.eduInfo))
   return false;
  if (familyInfo == null) {
   if (other.familyInfo != null)
    return false;
  } else if (!familyInfo.equals(other.familyInfo))
   return false;
  if (personalInfo == null) {
   if (other.personalInfo != null)
    return false;
  } else if (!personalInfo.equals(other.personalInfo))
   return false;
  return true;
 }
 
 /**
  * Method which in turn uses toString() of composite element classes
  * and uses debug level of logger for printing
  */
 public void printEmployee(){
  LOGGER.trace("***********PRINT EMPLOYEE CALLED********************");
  LOGGER.trace("Employee details are as follows:-" );
  LOGGER.trace("Personal Information of employee is"+ personalInfo);
  LOGGER.trace("Family Information of employee is"+ familyInfo);
  LOGGER.trace("Educational Information of employee is"+ eduInfo);
  
  LOGGER.trace("*************************************************************************");
 }
 
 
}


Domain class for FamilyInfo
package com.kunaal.logger.domain;

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

/**
 * Class for capturing family information 
 * 
 * @author Kunaal A Trehan
 *
 */
public class FamilyInfo {
 //Boolean flag for capturing whether person is married or not
 private boolean isMarried;
 
 //String variable for capturing name of spouse
 private String spouseName;
 
 //Boolean flag for capturing whether person has any kids or not
 private boolean anyKids;
 
 //List containing kids name
 private List<String> kidList=new ArrayList<String>();

 /**
  * @return the isMarried
  */
 public boolean isMarried() {
  return isMarried;
 }

 /**
  * @param isMarried the isMarried to set
  */
 public void setMarried(boolean isMarried) {
  this.isMarried = isMarried;
 }

 /**
  * @return the spouseName
  */
 public String getSpouseName() {
  return spouseName;
 }

 /**
  * @param spouseName the spouseName to set
  */
 public void setSpouseName(String spouseName) {
  this.spouseName = spouseName;
 }

 /**
  * @return the kidList
  */
 public List<String> getKidList() {
  return kidList;
 }

 /**
  * @param kidList the kidList to set
  */
 public void setKidList(List<String> kidList) {
  this.kidList = kidList;
 }

 /**
  * @return the anyKids
  */
 public boolean isAnyKids() {
  return anyKids;
 }

 /**
  * @param anyKids the anyKids to set
  */
 public void setAnyKids(boolean anyKids) {
  this.anyKids = anyKids;
 }
 
 /**
  * Overridden toString() method
  */
 @Override
 public String toString() {
  if(isMarried && anyKids){  
   return "FamilyInfo [Spouse name is -" + spouseName+",Kids name-"+ kidList+"]";
  }else if(isMarried){
   return "FamilyInfo [Spouse name is -" + spouseName+"]";
  }else{
   return "FamilyInfo [Enjoying bachelor hood]";
  }
 }

 /**
  * Overridden hash code method
  */
 @Override
 public int hashCode() {
  final int prime = 31;
  int result = 1;
  result = prime * result + (anyKids ? 1231 : 1237);
  result = prime * result + (isMarried ? 1231 : 1237);
  result = prime * result + ((kidList == null) ? 0 : kidList.hashCode());
  result = prime * result
    + ((spouseName == null) ? 0 : spouseName.hashCode());
  return result;
 }

 /**
  * Overridden equals method
  */
 @Override
 public boolean equals(Object obj) {
  if (this == obj)
   return true;
  if (obj == null)
   return false;
  if (getClass() != obj.getClass())
   return false;
  FamilyInfo other = (FamilyInfo) obj;
  if (anyKids != other.anyKids)
   return false;
  if (isMarried != other.isMarried)
   return false;
  if (kidList == null) {
   if (other.kidList != null)
    return false;
  } else if (!kidList.equals(other.kidList))
   return false;
  if (spouseName == null) {
   if (other.spouseName != null)
    return false;
  } else if (!spouseName.equals(other.spouseName))
   return false;
  return true;
 }
 

}


Domain class for PersonalInfo
package com.kunaal.logger.domain;

import java.util.Date;

/**
 * Class for capturing personal information about an employee
 * 
 * @author Kunaal A Trehan
 *
 */
public class PersonalInfo {

 //Private variable for name
 private String name;
 
 //Private variable for age
 private Integer age;
 
 //Private variable for date of birth
 private Date dob;
 
 /**
  * Default constructor
  */
 public PersonalInfo() {
 }

 /**
  * @return the name
  */
 public  String getName() {
  return name;
 }

 /**
  * @param name the name to set
  */
 public  void setName(String name) {
  this.name = name;
 }

 /**
  * @return the age
  */
 public  Integer getAge() {
  return age;
 }

 /**
  * @param age the age to set
  */
 public  void setAge(Integer age) {
  this.age = age;
 }

 /**
  * @return the dob
  */
 public  Date getDob() {
  return dob;
 }

 /**
  * @param dob the dob to set
  */
 public  void setDob(Date dob) {
  this.dob = dob;
 }

 /**
  * Overridden toString() method
  */
 @Override
 public String toString() {
  return "PersonalInfo [name=" + name + ", age=" + age + ", dob=" + dob
    + "]";
 }

 /**
  * Overridden hash code method
  */
 @Override
 public int hashCode() {
  final int prime = 31;
  int result = 1;
  result = prime * result + ((age == null) ? 0 : age.hashCode());
  result = prime * result + ((dob == null) ? 0 : dob.hashCode());
  result = prime * result + ((name == null) ? 0 : name.hashCode());
  return result;
 }

 /**
  * Overridden equals method
  */
 @Override
 public boolean equals(Object obj) {
  if (this == obj)
   return true;
  if (obj == null)
   return false;
  if (getClass() != obj.getClass())
   return false;
  PersonalInfo other = (PersonalInfo) obj;
  if (age == null) {
   if (other.age != null)
    return false;
  } else if (!age.equals(other.age))
   return false;
  if (dob == null) {
   if (other.dob != null)
    return false;
  } else if (!dob.equals(other.dob))
   return false;
  if (name == null) {
   if (other.name != null)
    return false;
  } else if (!name.equals(other.name))
   return false;
  return true;
 }

}


DomainService class
package com.kunaal.logger.service;

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

import org.apache.log4j.Logger;

import com.kunaal.logger.domain.EducationalInfo;
import com.kunaal.logger.domain.Employee;
import com.kunaal.logger.domain.FamilyInfo;
import com.kunaal.logger.domain.PersonalInfo;

/**
 * This class has utility methods for inserting,updating,deleting the employee information
 * 
 * @author Kunaal A Trehan
 *
 */
public class DomainService {
 //Creating a logger variable
 private static final Logger LOGGER=Logger.getLogger(DomainService.class);
 
 //List data structure containing employee information
 private static final List<Employee> empList=new ArrayList<Employee>();

 /**
  * Utility method for adding employee
  * 
  * @param employee
  */
 public static void insertEmployee(Employee employee){
  if(empList.contains(employee)){
   LOGGER.warn("Employee-" + employee + " already exists,use update method for updating the information");
  }else{
   empList.add(employee);
   LOGGER.debug("Employee-" + employee.getPersonalInfo().getName() + " added succesfully");
  }
 }
 
 /**
  * Utility method for updating personal information of an employee
  * 
  * @param emp
  * @param personalInfo
  */
 public static void updatePersonalInfo(Employee emp,PersonalInfo personalInfo){
  if(!empList.contains(emp)){
   LOGGER.warn("Employee-" + emp + " does not exists,use insert method");
  }else{
   int index = empList.indexOf(emp);
   Employee employee = empList.get(index);
   employee.setPersonalInfo(personalInfo);
   LOGGER.debug("Personal information for employee-"+ employee.getPersonalInfo().getName()
     + " updated successfully");
  }
 }
 
 /**
  * Utility method for updating family information of an employee
  * 
  * @param emp
  * @param familyInfo
  */
 public static void updateFamilyInfo(Employee emp,FamilyInfo familyInfo){
  if(!empList.contains(emp)){
   LOGGER.warn("Employee-" + emp + " does not exists,use insert method");
  }else{
   int index = empList.indexOf(emp);
   Employee employee = empList.get(index);
   employee.setFamilyInfo(familyInfo);
   LOGGER.debug("Family information for employee-"+ employee.getPersonalInfo().getName()
     + " updated successfully");
  }
 }
 
 /**
  * Utility method for updating educational information of an employee
  * 
  * @param emp
  * @param eduInfo
  */
 public static void updateEducationalInfo(Employee emp,EducationalInfo eduInfo){
  if(!empList.contains(emp)){
   LOGGER.warn("Employee-" + emp + " does not exists,use insert method");
  }else{
   int index = empList.indexOf(emp);
   Employee employee = empList.get(index);
   employee.setEduInfo(eduInfo);
   LOGGER.debug("Educational information for employee-"+ employee.getPersonalInfo().getName()
     + " updated successfully");
  }
 }
 
 /**
  * Utility method for deleting an employee
  * 
  * @param emp
  */
 public static void deleteEmployee(Employee emp){
  if(!empList.contains(emp)){
   LOGGER.warn("Employee-" + emp + " does not exists");
  }else{
   empList.remove(emp);
   LOGGER.debug("Employee-" + emp.getPersonalInfo().getName() + " deleted succesfully");
  }
 }
 
 /**
  * Utility method for printing employee personal information
  * as logger INFO
  */
 public static void printPersonalInfo(){
  LOGGER.info("*****************PRINT PERSONAL INFO OF ALL EMP*******************");
  for(Employee emp:empList){
   LOGGER.info(emp.getPersonalInfo());
  }
  LOGGER.info("******************************************************************");
 }
 
 
}


Util Class
package com.kunaal.logger.util;

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

import com.kunaal.logger.domain.EducationalInfo;
import com.kunaal.logger.domain.Employee;
import com.kunaal.logger.domain.FamilyInfo;
import com.kunaal.logger.domain.PersonalInfo;

/**
 * @author Kunaal A Trehan
 *
 */
public class Util {

 /**
  * Utility method for creating employee
  * 
  * @param personalInfo
  * @param eduInfo
  * @param familyInfo
  * @return
  */
 public static Employee createEmployee(PersonalInfo personalInfo,EducationalInfo eduInfo,
        FamilyInfo familyInfo){
  Employee emp=new Employee();
  
  emp.setEduInfo(eduInfo);
  emp.setFamilyInfo(familyInfo);
  emp.setPersonalInfo(personalInfo);
  
  return emp;
 }
 
 /**
  * Utility method for creating personal information
  * 
  * @param age
  * @param name
  * @param dob
  * @return
  */
 public static PersonalInfo getPersonalInfo(Integer age,String name,Date dob){
  PersonalInfo personalInfo=new PersonalInfo();
  personalInfo.setAge(age);
  personalInfo.setDob(dob);
  personalInfo.setName(name);
  
  return personalInfo;
 }
 
 /**
  * Utility method for creating educational information.
  * 
  * @param isUg
  * @param ugSpl
  * @param isPg
  * @param pgSpl
  * @param others
  * @return
  */
 public static EducationalInfo getEduInfo(boolean isUg,String ugSpl,boolean isPg,String pgSpl,String others){
  EducationalInfo eduInfo=new EducationalInfo();
  eduInfo.setUG(isUg);
  eduInfo.setPG(isPg);
  eduInfo.setOthers(others);
  eduInfo.setPgSpecialization(pgSpl);
  eduInfo.setUgSpecialization(ugSpl);
  
  return eduInfo;
 }
 
 /**
  * Utility methods for creating family information.
  * 
  * @param anyKids
  * @param kidList
  * @param isMarried
  * @param spouse
  * @return
  */
 public static FamilyInfo getFamilyInfo(boolean anyKids,List<String> kidList,boolean isMarried,String spouse){
  FamilyInfo familyInfo=new FamilyInfo();
  familyInfo.setAnyKids(anyKids);
  familyInfo.setKidList(kidList);
  familyInfo.setMarried(isMarried);
  familyInfo.setSpouseName(spouse);
  
  return familyInfo;
 }
}

LoggerExample class showing inheritance,multiple appenders,logmanager datastructure and others
package com.kunaal.logger;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.List;

import org.apache.log4j.Logger;
import org.apache.log4j.xml.DOMConfigurator;

import com.kunaal.logger.domain.EducationalInfo;
import com.kunaal.logger.domain.Employee;
import com.kunaal.logger.domain.FamilyInfo;
import com.kunaal.logger.domain.PersonalInfo;
import com.kunaal.logger.service.DomainService;
import com.kunaal.logger.util.Util;

/**
 * @author Kunaal A Trehan
 *
 */
public class LoggerExample {
 //LOGGER for this class.Since there is not customization done.
 //So it will inherit the properties from root logger.
 private static final Logger LOGGER=Logger.getLogger(LoggerExample.class);
 
 private static final Logger DUP_LOGGER=Logger.getLogger(LoggerExample.class);

 /**
  * @param args
  */
 public static void main(String[] args) {
  DOMConfigurator.configure("src/com/kunaal/logger/conf/log4j.xml");
  testInheritance();
  testLogMgr();
  
  //Here we are persisting same employee twice.
  //Since for domain package we have set logging level to INFO
  //So only warning gets printed when we insert again.
  //Debug information regarding insertion successful is not printed.
  Employee emp=insertEmp();
  Employee emp1=insertEmp();
  
  //Delete non existing employee .Warning will be printed on console
  delNonExistEmployee();
  
  //Here we will see all details of employee
  //Even though root logger is at DEBUG.We will see all TRACE messages
  //As custom logger can override the settings which we did for com.kunaal.logger.domain
  emp.printEmployee();
  
  //Log Level is set to INFO for com.kunaal.logger.service.
  //So all Info messages will be printed
  DomainService.printPersonalInfo();
 }
 
 /**
  * This method shows the inheritance among the loggers
  * Here there is no customization for the logger of this class.
  * So it will inherit the properties from root logger
  * which says log level is debug.
  * 
  * So trace information won't be printed.
  */
 private static void testInheritance(){
  LOGGER.info("This is info level information");
  LOGGER.trace("This is trace level information");
 }
 
 /**
  * This method shows that logger is created only once.
  * Then its instance is persisted in map of LogManager
  * which is used to get the logger when we do 
  * Logger.getLogger(....)
  */
 private static void testLogMgr(){
  LOGGER.info("Logger information-" +LOGGER.toString());
  LOGGER.info("Duplicate logger information-" +DUP_LOGGER.toString());
  LOGGER.info("Is logger and duplicate logger point to same instance-" +(LOGGER==DUP_LOGGER));
 }
 
 /**
  * Method for inserting employee
  * @return
  */
 private static Employee insertEmp(){
  EducationalInfo eduInfo=Util.getEduInfo(true, "Engg.", false, null, null);
  Calendar cal=new GregorianCalendar(1978, 2, 18);
  PersonalInfo personalInfo=Util.getPersonalInfo(new Integer(30), "Kunaal A Trehan", cal.getTime());
  List<String> kidList=new ArrayList<String>();
  kidList.add("Annika");
  FamilyInfo familyInfo=Util.getFamilyInfo(true, kidList, true, "Mrs X");
  
  Employee emp=Util.createEmployee(personalInfo, eduInfo, familyInfo);
  DomainService.insertEmployee(emp);
  
  return emp;
  
 }
 
 /**
  * Method for deleteing non existing employee
  * It will print warning on console
  */
 private static void delNonExistEmployee(){
  Employee emp=new Employee();
  DomainService.deleteEmployee(emp);  
 }
 
 

}

Console Output
[main] INFO  logger.LoggerExample  - This is info level information
[main] INFO  logger.LoggerExample  - Logger information-org.apache.log4j.Logger@2bbd86
[main] INFO  logger.LoggerExample  - Duplicate logger information-org.apache.log4j.Logger@2bbd86
[main] INFO  logger.LoggerExample  - Is logger and duplicate logger point to same instance-true
[main] WARN  service.DomainService  - Employee-Employee [personalInfo=PersonalInfo [name=Kunaal A Trehan, age=30, dob=Sat Mar 18 00:00:00 GMT+05:30 1978], familyInfo=FamilyInfo [Spouse name is -Mrs X,Kids name-[Annika]], eduInfo=EducationalInfo [isUG=true, isPG=false, ugSpecialization=Engg., pgSpecialization=NA, others=NA]] already exists,use update method for updating the information
[main] WARN  service.DomainService  - Employee-Employee [personalInfo=null, familyInfo=null, eduInfo=null] does not exists
[main] TRACE domain.Employee  - ***********PRINT EMPLOYEE CALLED********************
[main] TRACE domain.Employee  - Employee details are as follows:-
[main] TRACE domain.Employee  - Personal Information of employee isPersonalInfo [name=Kunaal A Trehan, age=30, dob=Sat Mar 18 00:00:00 GMT+05:30 1978]
[main] TRACE domain.Employee  - Family Information of employee isFamilyInfo [Spouse name is -Mrs X,Kids name-[Annika]]
[main] TRACE domain.Employee  - Educational Information of employee isEducationalInfo [isUG=true, isPG=false, ugSpecialization=Engg., pgSpecialization=NA, others=NA]
[main] TRACE domain.Employee  - *************************************************************************
[main] INFO  service.DomainService  - *****************PRINT PERSONAL INFO OF ALL EMP*******************
[main] INFO  service.DomainService  - PersonalInfo [name=Kunaal A Trehan, age=30, dob=Sat Mar 18 00:00:00 GMT+05:30 1978]
[main] INFO  service.DomainService  - ******************************************************************

Best Practices

When we do LOGGER.debug("......" + "....");,the operation becomes expensive if logger priority level is above debug as the expression will be evaluated and only when logger tries to put the information in the appender.Then it will check and figure out that this information can't be logged.

So its better to write the above statement as follows:-

if(LOGGER.isDebugEnabled()){
       LOGGER.debug("......" + "....");
}

In this way we avoid unnecessary creation of log message if its not going to be logged.