Archive for February, 2016

28
Feb
16

HTML 5 Responsive AJAX based CRUD application using Spring 4, JPA 2, JSF 2

This page shows how to create a transactional AJAX based HTML 5 responsive CRUD application to manage a company addressbook. In addition we will using annotations wherever possible. For ease of use we will use a HSQLDB in memory database that will automatically create a directory called data in your userid’s home directory.

Full downloadable source for this page is available here. Corrections and enhancements are welcome, fork, change and push back to GitHub.

Requirements

  • Java 8
  • Spring 4
  • JPA 2
  • JSF 2.1
  • HSQLDB
  • JAVA EE 6 level containers like JBoss EAP 6.4 or Tomcat 7

Create the pom.xml

First step is to create a pom.xml file. If you are using Tomcat 7 then enable the Glassfish dependency. Since Tomcat is only a servlet engine you will need implementation classes that don’t exist in Tomcat but are provided by other more beefy web application servers.

vi pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.test</groupId>
	<artifactId>JSFJPASpringCRUDBasic</artifactId>
	<version>20160228</version>
	<packaging>war</packaging>
	<name>JSFJPASpringCRUDBasic</name>
	<pluginRepositories>
		<pluginRepository> <!-- Ignore this repository. Its only used for document publication. -->
			<id>numberformat-releases</id>
			<url>https://raw.githubusercontent.com/numberformat/wordpress/master/20130213/repo/</url>
		</pluginRepository>
	</pluginRepositories>
	<!-- It is possible to accidentally mix different versions of Spring JARs 
		when using Maven. For example, you may find that a third-party library, or 
		another Spring project, pulls in a transitive dependency to an older release. 
		If you forget to explicitly declare a direct dependency yourself, all sorts 
		of unexpected issues can arise. To overcome such problems Maven supports 
		the concept of a "bill of materials" (BOM) dependency. You can import the 
		spring-framework-bom in your dependencyManagement section to ensure that 
		all spring dependencies (both direct and transitive) are at the same version. 
		An added benefit of using the BOM is that you no longer need to specify the 
		<version> attribute when depending on Spring Framework artifacts -->
	<dependencyManagement>
	    <dependencies>
	        <dependency>
	            <groupId>org.springframework</groupId>
	            <artifactId>spring-framework-bom</artifactId>
	            <version>4.1.4.RELEASE</version>
	            <type>pom</type>
	            <scope>import</scope>
	        </dependency>
	    </dependencies>
	</dependencyManagement>

	<repositories>
		<repository>
			<id>prime-repo</id>
			<name>PrimeFaces Maven Repository</name>
			<url>http://repository.primefaces.org</url>
			<layout>default</layout>
		</repository>
	</repositories>
	
	<dependencies>
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>4.12</version>
		</dependency>
<!-- Logging -->
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-api</artifactId>
			<version>1.7.1</version>
		</dependency>
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-log4j12</artifactId>
			<version>1.7.1</version>
		</dependency>
<!-- Data Management -->
		<dependency>
			<groupId>org.hibernate.javax.persistence</groupId>
			<artifactId>hibernate-jpa-2.0-api</artifactId>
			<version>1.0.1.Final</version>
		</dependency>
		<dependency>
			<groupId>org.hibernate</groupId>
			<artifactId>hibernate-entitymanager</artifactId>
			<version>4.1.6.Final</version>
		</dependency>
		<dependency>
			<groupId>org.hsqldb</groupId>
			<artifactId>hsqldb</artifactId>
			<version>2.3.3</version>
		</dependency>		
<!-- Spring Framework -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-beans</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-jdbc</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-orm</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-web</artifactId>
		</dependency>
<!-- JSF -->
		<dependency>
			<groupId>net.bootsfaces</groupId>
			<artifactId>bootsfaces</artifactId>
			<version>0.8.0</version>
		</dependency>
		<dependency>
			<groupId>org.primefaces</groupId>
			<artifactId>primefaces</artifactId>
			<version>5.3</version>
		</dependency>
		<dependency>
			<groupId>org.primefaces.themes</groupId>
			<artifactId>bootstrap</artifactId>
			<version>1.0.9</version>
		</dependency>
		<!-- If running this in a Servlet only container like Tomcat 7 enable this dependency 
		<dependency> 
			<groupId>org.glassfish</groupId>
			<artifactId>javax.faces</artifactId>
			<version>2.1.6</version>
		</dependency>		
		-->
<!-- APIs for services provided by the containers -->		
		<dependency>
			<groupId>javax</groupId>
			<artifactId>javaee-web-api</artifactId>
			<version>6.0</version>
			<scope>provided</scope>
		</dependency>
		<dependency>
			<groupId>net.sf.opencsv</groupId>
			<artifactId>opencsv</artifactId>
			<version>2.3</version>
		</dependency>
	</dependencies>
	<build>
		<plugins>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-compiler-plugin</artifactId>
				<version>2.3.2</version>
				<configuration>
					<source>1.8</source>
					<target>1.8</target>
				</configuration>
			</plugin>
			<plugin> <!-- Ignore this plugin. Its only used for document publication. -->
				<groupId>github.numberformat</groupId>
				<artifactId>blog-plugin</artifactId>
				<version>1.0-SNAPSHOT</version>
				<configuration>
					<gitUrl>https://github.com/numberformat/wordpress/tree/master/${project.version}/${project.artifactId}</gitUrl>
				</configuration>
				<executions>
					<execution>
						<id>1</id>
						<phase>site</phase>
						<goals>
							<goal>generate</goal>
						</goals>
					</execution>
				</executions>
			</plugin>
		</plugins>
	</build>
</project>

Web Application Initialization

This app depends on the Servlet 3.0 specification. The spec requires web application containers to search the classpath looking for Initializers. Classes that implement the the spring “WebApplicationInitializer” are the ones that will be picked up.

vi src/main/java/com/test/WebAppInitializer.java

package com.test;  
  
import javax.servlet.ServletContext;
import javax.servlet.ServletException;

import org.springframework.web.WebApplicationInitializer;
import org.springframework.web.context.ContextLoaderListener;
import org.springframework.web.context.request.RequestContextListener;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;  

/**
 * The Servlet 3 containers automatically search the classpath for classes that
 * implement the below interface and call the onStartup() method on initialization.
 */
public class WebAppInitializer implements WebApplicationInitializer {  
      
    @Override  
    public void onStartup(ServletContext servletContext) throws ServletException {  
    	// Initialize the spring framework.
        AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();  
        ctx.register(SpringConfig.class);  
        ctx.setServletContext(servletContext);    
        servletContext.addListener(new ContextLoaderListener(ctx));
        // required to get spring beans injected into JSF managed beans
        servletContext.addListener(new RequestContextListener());
        
        // initialize faces (not working...)
//        servletContext.setInitParameter("primefaces.THEME", "bootstrap");
//        servletContext.setInitParameter("javax.faces.DEFAULT_SUFFIX", ".xhtml");
//        ServletRegistration.Dynamic faces = servletContext.addServlet("Faces Servlet", "javax.faces.webapp.FacesServlet");
//        faces.setLoadOnStartup(1);
//        faces.addMapping("*.xhtml");
        
    }  
  
}  

Spring Configuration (No XML)

vi src/main/java/com/test/SpringConfig.java

package com.test;  
  
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

import javax.faces.context.FacesContext;
import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource;

import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.config.CustomScopeConfigurer;
import org.springframework.beans.factory.config.Scope;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.JpaVendorAdapter;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;  

/**
 * This class replaces the spring xml configuration.
 * 
 */
@Configuration
@ComponentScan("com.test")
@EnableTransactionManagement 
public class SpringConfig {
	
	@Bean
	public DataSource getDataSource() {
		DriverManagerDataSource dm = new DriverManagerDataSource();
		dm.setDriverClassName("org.hsqldb.jdbcDriver");
		dm.setUrl("jdbc:hsqldb:file:"+System.getProperty("user.home") + "/data/JSFJPASpringCRUD"+";shutdown=true");
		dm.setUsername("sa");
		return dm;
	}
	
//	@Bean
//	public DataSource getDataSource() {
//		DriverManagerDataSource dm = new DriverManagerDataSource();
//		dm.setDriverClassName("com.mysql.jdbc.Driver");
//		// change the "/tmp" to be another location on your system.
//		dm.setUrl("jdbc:mysql://localhost:3306/xxxx");
//		dm.setUsername("xxxx");
//		dm.setPassword("xxxx");
//		return dm;
//	}
	
	@Bean
	public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
		LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
		em.setDataSource(getDataSource());
		em.setPackagesToScan(new String[] { "com.test.model" });
		JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
		em.setJpaVendorAdapter(vendorAdapter);
		em.setJpaProperties(additionalProperties());
		return em;
	}

	@Bean
	public PlatformTransactionManager transactionManager(EntityManagerFactory emf) {
		JpaTransactionManager transactionManager = new JpaTransactionManager();
		transactionManager.setEntityManagerFactory(emf);
		return transactionManager;
	}

	@Bean
	public PersistenceExceptionTranslationPostProcessor exceptionTranslation() {
		return new PersistenceExceptionTranslationPostProcessor();
	}

	Properties additionalProperties() {
		Properties properties = new Properties();
		properties.setProperty("hibernate.dialect", "org.hibernate.dialect.HSQLDialect");
//		properties.setProperty("hibernate.dialect", "org.hibernate.dialect.MySQL5InnoDBDialect");
		properties.setProperty("hibernate.flushMode", "FLUSH_AUTO");
		properties.setProperty("hibernate.hbm2ddl.auto", "update");
		properties.setProperty("hibernate.show_sql", "false");
		return properties;
	}  

	@Bean
	public CustomScopeConfigurer customScope () {
	    CustomScopeConfigurer configurer = new CustomScopeConfigurer ();
	    Map<String, Object> viewScope = new HashMap<String, Object>();
	    viewScope.put("view", new ViewScope());
	    configurer.setScopes(viewScope);

	    return configurer;
	}
	
	class ViewScope implements Scope {
		 
	    public Object get(String name, @SuppressWarnings("rawtypes") ObjectFactory objectFactory) {
	        Map<String,Object> viewMap = FacesContext.getCurrentInstance().getViewRoot().getViewMap();
	 
	        if(viewMap.containsKey(name)) {
	            return viewMap.get(name);
	        } else {
	            Object object = objectFactory.getObject();
	            viewMap.put(name, object);
	 
	            return object;
	        }
	    }
	 
	    public Object remove(String name) {
	        return FacesContext.getCurrentInstance().getViewRoot().getViewMap().remove(name);
	    }
	 
	    public String getConversationId() {
	        return null;
	    }
	 
	    public void registerDestructionCallback(String name, Runnable callback) {
	        //Not supported
	    }
	 
	    public Object resolveContextualObject(String key) {
	        return null;
	    }
	}
	
}  

Logging configuration

vi src/main/resources/log4j.xml

<!DOCTYPE log4j:configuration PUBLIC
  "-//APACHE//DTD LOG4J 1.2//EN" "http://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/xml/doc-files/log4j.dtd">
   
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
    
	<appender name="file" class="org.apache.log4j.FileAppender">
	   <param name="maxFileSize" value="1024KB" />
	   <param name="file" value="${user.home}/logs/JSFJPASpringCRUDBasic.log" />
	   <layout class="org.apache.log4j.PatternLayout">
		<param name="ConversionPattern" 
			value="%d %-5p %c:%L - %m%n" />
	   </layout>
	</appender>
	    
    <logger name="com.test">
        <level value="DEBUG"/>
    </logger>
    <logger name="com.springframework">
        <level value="INFO"/>
    </logger>
    <logger name="org.hibernate.SQL">
        <level value="DEBUG"/>
    </logger>
    <root>
        <level value="ERROR"/>
        <appender-ref ref="file"/>
    </root> 
</log4j:configuration>

Data Managers and Entities

Used by JPA to define and interact with the database.

vi src/main/java/com/test/model/Company.java

package com.test.model;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name="company")
public class Company {
	@Id
	@GeneratedValue
	@Column(name="company_id")
	private Long id;
	@Column(name="name")
	private String name;
	@Column(name="shipto_address1")
	private String shipToAddress;
	@Column(name="shipto_address2")
	private String shipToAddress2;
	@Column(name="shipto_city")
	private String shipToCity;
	@Column(name="shipto_provence")
	private String shipToProvence;
	@Column(name="shipto_zip")
	private String shipToZip;
	@Column(name="cosign_address1")
	private String coSignAddress;
	@Column(name="cosign_address2")
	private String coSignAddress2;
	@Column(name="cosign_city")
	private String coSignCity;
	@Column(name="cosign_zip")
	private String coSignZip;
	@Column(name="cosign_country")
	private String coSignCountry;

	public Long getId() {
		return id;
	}
	public void setId(Long id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getShipToAddress() {
		return shipToAddress;
	}
	public void setShipToAddress(String shipToAddress) {
		this.shipToAddress = shipToAddress;
	}
	public String getShipToAddress2() {
		return shipToAddress2;
	}
	public void setShipToAddress2(String shipToAddress2) {
		this.shipToAddress2 = shipToAddress2;
	}
	public String getShipToCity() {
		return shipToCity;
	}
	public void setShipToCity(String shipToCity) {
		this.shipToCity = shipToCity;
	}
	public String getShipToProvence() {
		return shipToProvence;
	}
	public void setShipToProvence(String shipToProvence) {
		this.shipToProvence = shipToProvence;
	}
	public String getShipToZip() {
		return shipToZip;
	}
	public void setShipToZip(String shipToZip) {
		this.shipToZip = shipToZip;
	}
	public String getCoSignAddress() {
		return coSignAddress;
	}
	public void setCoSignAddress(String coSignAddress) {
		this.coSignAddress = coSignAddress;
	}
	public String getCoSignAddress2() {
		return coSignAddress2;
	}
	public void setCoSignAddress2(String coSignAddress2) {
		this.coSignAddress2 = coSignAddress2;
	}
	public String getCoSignCity() {
		return coSignCity;
	}
	public void setCoSignCity(String coSignCity) {
		this.coSignCity = coSignCity;
	}
	public String getCoSignZip() {
		return coSignZip;
	}
	public void setCoSignZip(String coSignZip) {
		this.coSignZip = coSignZip;
	}
	public String getCoSignCountry() {
		return coSignCountry;
	}
	public void setCoSignCountry(String coSignCountry) {
		this.coSignCountry = coSignCountry;
	}
	
}

Spring DataManager Interface.

vi src/main/java/com/test/model/CompanyDataManager.java

package com.test.model;

import java.util.List;

public interface CompanyDataManager {
	// C
	Company create(Company selectedCompany);
	// R
	List<Company> getAll();
	// U
	Company update(Company selectedCompany);
	// D
	void delete(Company selectedCompany);
}

Spring DataManager Implementation

vi src/main/java/com/test/model/CompanyDataManagerImpl.java

package com.test.model;

import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

import org.springframework.stereotype.Component;

@Component("companyDataManager")
public class CompanyDataManagerImpl implements CompanyDataManager {
	@PersistenceContext 
	private EntityManager em;

	@Override
	public List<Company> getAll() {
		return em.createQuery("SELECT c FROM Company c", Company.class).getResultList();
	}

	@Override
	public Company update(Company selectedCompany) {
		Company mergedCompany = em.merge(selectedCompany);
		return mergedCompany;
	}

	@Override
	public void delete(Company selectedCompany) {
		em.remove(em.contains(selectedCompany) ? selectedCompany : em.merge(selectedCompany));
	}
	
	public Company create(Company selectedCompany) {
		em.persist(selectedCompany);
		return selectedCompany;
	}
	
}

JSF Page and the JSF Managed Bean

The managed bean hold user entered data. This particular bean is in the View Scope. This ia scope between request and session. It stays active as long as the uesr is on the page. We could not use the Request Scope for this because the page makes AJAX call in the background.

vi src/main/java/com/test/CompanyMB.java

package com.test;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.List;
import java.util.Random;

import javax.faces.bean.ManagedBean;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Controller;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;

import com.test.model.Company;
import com.test.model.CompanyDataManager;

import au.com.bytecode.opencsv.CSVReader;


/**
 * The following is a JSF Managed Bean that is actually managed by Spring.
 */

@Controller
@Scope(value="view") // you can not use JSF Scoped values here since its a spring managed bean
@ManagedBean	// use this to indicate that its a JSF Bean (managed by spring)
public class CompanyMB {

	private static final Logger logger = LoggerFactory.getLogger(CompanyMB.class);

	@Autowired
	private CompanyDataManager companyDataManager;
	
	private Company selectedCompany;
	private List<Company> all;
	private Boolean addState = false;
	
	public List<Company> getAll() { // getters should NEVER implement DB fetch logic.
		if(all==null) loadAll();
		return all;
	}

	/**
	 * This method will be called by the page during preRenderView. Example:
	 * <f:event type="preRenderView" listener="#{companyMB.loadAll}"/>
	 */
	@Transactional(isolation=Isolation.SERIALIZABLE)
	public void loadAll() {
    	logger.info("getting All");
		if(companyDataManager==null) throw new NullPointerException("dataManager is null");
		all = companyDataManager.getAll();
	}

	public CompanyDataManager getCompanyDataManager() {
		return companyDataManager;
	}


	public Company getSelectedCompany() {
		return selectedCompany;
	}


	public void setSelectedCompany(Company selectedCompany) {
		this.selectedCompany = selectedCompany;
	}
	
	private int countLines(InputStream file) {
		if(file==null) throw new NullPointerException("file stream should not be null.");
		int lines = 0;
		try (BufferedReader reader = new BufferedReader(new InputStreamReader(file))) {
			while (reader.readLine() != null) lines++;
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
		return lines;
	}
	@Transactional
	public void newCompany() {
		Company c = getRandomCompany();
		selectedCompany = companyDataManager.create(c);
		addState = true;
	}

	private Company getRandomCompany() {
		final String DUMMY_COMPANY_LIST_CSV = "/dummyCompanyList.csv";

		Company c = new Company();
		InputStream is = getClass().getResourceAsStream(DUMMY_COMPANY_LIST_CSV);
		if(is==null) return c;
		int lines = countLines(is);
		int randomLine = new Random().nextInt(lines - 2) + 1;
		int index = 0;
		is = getClass().getResourceAsStream(DUMMY_COMPANY_LIST_CSV);
		try (CSVReader reader = new CSVReader(new InputStreamReader(is))) {
			String[] nextLine = null;
			while((nextLine=reader.readNext()) != null) {
				if(index >= randomLine) {
					c.setName(nextLine[0]);
					c.setShipToAddress(nextLine[3]);
					c.setShipToCity(nextLine[4]);
					c.setShipToProvence(nextLine[5]);
					c.setShipToZip(nextLine[6]);
					break;
				}
				index++;
			}
		} catch (Exception ex) {
			throw new RuntimeException("unable to read random address CSV file.", ex);
		}
		return c;
	}
	
	@Transactional
	public void updateSelectedCompany() {
		logger.debug("update Selected Company called.");
		companyDataManager.update(selectedCompany);
	}
	@Transactional
	public void deleteSelectedCompany() {
		logger.debug("delete Selected Company called.");
		companyDataManager.delete(selectedCompany);
	}
	
	@Transactional
	public void cancel() {
		if(addState) {
			deleteSelectedCompany();
			addState=false;
		}
	}
}

This is the only UI page the users will see. Single page HTML5 applications use javascript to show dialogs and submit requests in the background.

vi src/main/webapp/company.xhtml

<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
	xmlns:b="http://bootsfaces.net/ui"
	xmlns:f="http://java.sun.com/jsf/core"
	xmlns:h="http://java.sun.com/jsf/html"
	xmlns:p="http://primefaces.org/ui">
<h:head>
	<title>Acme Distribution</title>
</h:head>
<h:body>
	<b:container>
		<b:navBar brand="Acme Wholesale Distributor" brandHref="#"
			inverse="true">
			<b:navbarLinks>
				<b:dropMenu value="Home" href="index.xhtml">
					<b:navLink value="Companies" href="company.xhtml"></b:navLink>
				</b:dropMenu>
			</b:navbarLinks>
		</b:navBar>

		<b:panel title="Company Address List" look="primary">

			<h:form id="form">
				<p:dataTable id="companyDt" var="company" value="#{companyMB.all}">
					<f:event type="preRenderView" listener="#{companyMB.loadAll}" />
					<p:column headerText="Name">
						<h:outputText value="#{company.name}" />
					</p:column>

					<p:column headerText="Address" priority="2">
						<h:outputText value="#{company.shipToAddress}" />
					</p:column>
					<p:column headerText="City" priority="3" width="160">
						<h:outputText value="#{company.shipToCity}" />
					</p:column>
					<p:column headerText="State" priority="4" width="160">
						<h:outputText value="#{company.shipToProvence}" />
					</p:column>
					<p:column headerText="Zip" priority="5" width="80">
						<h:outputText value="#{company.shipToZip}" />
					</p:column>
					<p:column width="55">
						<f:facet name="header">
						<p:commandButton action="#{companyMB.newCompany}" update=":form:companyDetail"
							oncomplete="PF('companyDialog').show()" icon="ui-icon-plusthick"
							title="Add"/>
						</f:facet>					
						<p:commandButton update=":form:companyDetail"
							oncomplete="PF('companyDialog').show()" icon="ui-icon-search"
							title="View">
							<f:setPropertyActionListener value="#{company}"
								target="#{companyMB.selectedCompany}" />
						</p:commandButton>
					</p:column>

				</p:dataTable>
				<!-- AJAX like Pop-up Dialog -->
				<p:dialog header="Company Info" widgetVar="companyDialog"
					modal="true" showEffect="fade" hideEffect="fade" resizable="false" closable="false" >
					
					<p:outputPanel id="companyDetail" style="text-align:center;">
						<p:panelGrid columns="2"
							rendered="#{not empty companyMB.selectedCompany}"
							columnClasses="label,value">
							<h:outputText value="Id:" />
							<h:outputText value="#{companyMB.selectedCompany.id}" />
							<h:outputText value="Name" />
							<h:inputText value="#{companyMB.selectedCompany.name}" />
							<h:outputText value="Address 1:" />
							<h:inputText value="#{companyMB.selectedCompany.shipToAddress}" />
							<h:outputText value="City:" />
							<h:inputText value="#{companyMB.selectedCompany.shipToCity}" />
							<h:outputText value="State:" />
							<h:inputText value="#{companyMB.selectedCompany.shipToProvence}" />
							<h:outputText value="Zip:" />
							<h:inputText value="#{companyMB.selectedCompany.shipToZip}" />
						</p:panelGrid>
						<!-- Save Button -->
						<p:commandButton update="companyDt" 
							action="#{companyMB.updateSelectedCompany}" value="Save"
							oncomplete="PF('companyDialog').hide()" icon="ui-icon-disk" />
						<!-- Delete Button -->
						<p:commandButton update="companyDt"
							action="#{companyMB.deleteSelectedCompany}" value="Delete"
							oncomplete="PF('companyDialog').hide()" icon="ui-icon-trash" />
						<p:commandButton action="#{companyMB.cancel}" update="companyDt" value="Cancel"
							oncomplete="PF('companyDialog').hide()"
							icon="ui-icon-arrowreturn-1-w" />
					</p:outputPanel>
				</p:dialog>
			</h:form>
		</b:panel>
		<b:panel>
		 <span class="subitem">Responsive</span>
                        <span class="defaultText dispTable">
        DataTable has two responsive modes. In priority mode, visibility of columns are toggled based on the screen size and their priority (1-6). On the other hand
        in reflow mode, columns are displayed as stacked on smaller screens.
    
                        </span>
		</b:panel>
	</b:container>
</h:body>
</html>

Sample Data (optional)

You can find the file under src/main/resources in the github repository for this page.

21
Feb
16

Changing Font Size in Eclipse Debian Jessie

Tried to change the font size of the File explorer on the left side but didnt have any luck until I tried the following:

create a file in your home directory.

vi .gtkrc-eclipse

style "eclipse" {
        font_name = "Sans Condensed 9"
}
class "GtkWidget" style "eclipse"

Save and run eclipse by typing the following:

GTK2_RC_FILES=/usr/share/themes/Clearlooks/gtk-2.0/gtkrc:$HOME/.gtkrc-eclipse “$HOME/.local/usr/local/eclipse-mars/eclipse”

Replace the path to your eclipse install.

20
Feb
16

using JPA 2.x with spring 3.x transactions

This page demonstrates how to use JPA EntityManager within a Spring Transaction using HSQLDB a Java in memory database. We will use a Spring DataManager responsible for database interaction, Spring service class for transaction management.

This is a update for a blog post I written back in 2010. That post dealt with the same topic using plain JdbcTemplate.

Highlights

  • Spring Pure Annotation based configuration (ZERO XML files) with an exception of the pom.xml file
  • Java JPA
  • HSQLDB

Background

Based on the default behavior, when the application throws a run time exception any method that is defined to be transactional issues a roll-back on whatever resource is currently participating in the transaction. To define a method as transactional just put an @Transactional annotation before it.

Full downloadable source for this page is available here. Corrections and enhancements are welcome, fork, change and push back to GitHub.

Requirements

  • Java 8

Implementation

The following example will demonstrate the transaction capabilities of the spring framework. Consider it a very basic “hello world” program for transactional code.

Create the pom.xml

vi pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.test</groupId>
	<artifactId>springJPATXTest</artifactId>
	<packaging>jar</packaging>
	<version>20160220</version>
	<url>http://maven.apache.org</url>
	
	<pluginRepositories>
		<pluginRepository> <!-- Ignore this repository. Its only used for document publication. -->
			<id>numberformat-releases</id>
			<url>https://raw.githubusercontent.com/numberformat/wordpress/master/20130213/repo/</url>
		</pluginRepository>
	</pluginRepositories>
		
	<properties>
		<spring.version>4.1.4.RELEASE</spring.version>
	</properties>
	<dependencies>
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>4.12</version>
		</dependency>

		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-api</artifactId>
			<version>1.7.1</version>
		</dependency>
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-log4j12</artifactId>
			<version>1.7.1</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-beans</artifactId>
			<version>${spring.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context</artifactId>
			<version>${spring.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-jdbc</artifactId>
			<version>${spring.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-orm</artifactId>
			<version>${spring.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.data</groupId>
			<artifactId>spring-data-jpa</artifactId>
			<version>1.7.2.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>org.hsqldb</groupId>
			<artifactId>hsqldb</artifactId>
			<version>2.3.3</version>
		</dependency>
		<dependency>
			<groupId>org.hibernate.javax.persistence</groupId>
			<artifactId>hibernate-jpa-2.1-api</artifactId>
			<version>1.0.0.Final</version>
		</dependency>
		<dependency>
			<groupId>org.hibernate</groupId>
			<artifactId>hibernate-entitymanager</artifactId>
			<version>4.3.7.Final</version>
		</dependency>		
	</dependencies>
	<build>
		<plugins>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-compiler-plugin</artifactId>
				<version>2.3.2</version>
				<configuration>
					<source>1.8</source>
					<target>1.8</target>
				</configuration>
			</plugin>

			<plugin> <!-- Ignore this plugin. Its only used for document publication. -->
				<groupId>github.numberformat</groupId>
				<artifactId>blog-plugin</artifactId>
				<version>1.0-SNAPSHOT</version>
				<configuration>
					<gitUrl>https://github.com/numberformat/wordpress/tree/master/${project.version}/${project.artifactId}</gitUrl>
				</configuration>
				<executions>
					<execution>
						<id>1</id>
						<phase>site</phase>
						<goals>
							<goal>generate</goal>
						</goals>
					</execution>
				</executions>
			</plugin>

		</plugins>
	</build>	
</project>

The following class uses Spring Annotation based configuration that sets up the data source and transaction manager.

vi src/main/java/com/test/SpringConfig.java

package com.test;

import java.util.Properties;

import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.JpaVendorAdapter;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;

@Configuration
@ComponentScan("com.test")
@EnableJpaRepositories("com.test")
@EnableTransactionManagement
public class SpringConfig {

	@Bean
	public DataSource getDataSource() {
		DriverManagerDataSource dm = new DriverManagerDataSource();
		dm.setDriverClassName("org.hsqldb.jdbcDriver");
		// change the "/tmp" to be another location on your system.
		dm.setUrl("jdbc:hsqldb:file:/tmp/springTXTestDB;shutdown=true");
		dm.setUsername("sa");
		return dm;
	}

	@Bean
	public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
		LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
		em.setDataSource(getDataSource());
		em.setPackagesToScan(new String[] { "com.test" });
		JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
		em.setJpaVendorAdapter(vendorAdapter);
		em.setJpaProperties(additionalProperties());
		return em;
	}

	@Bean
	public PlatformTransactionManager transactionManager(EntityManagerFactory emf) {
		JpaTransactionManager transactionManager = new JpaTransactionManager();
		transactionManager.setEntityManagerFactory(emf);
		return transactionManager;
	}

	@Bean
	public PersistenceExceptionTranslationPostProcessor exceptionTranslation() {
		return new PersistenceExceptionTranslationPostProcessor();
	}

	Properties additionalProperties() {
		Properties properties = new Properties();
		properties.setProperty("hibernate.flushMode", "FLUSH_AUTO");
		properties.setProperty("hibernate.hbm2ddl.auto", "update");
		properties.setProperty("hibernate.show_sql", "true");
		return properties;
	}
}

The following is just an interface.

vi src/main/java/com/test/TestTransactionDataManager.java

package com.test;

public interface TestTransactionDataManager {
	public abstract Number writeData(String data);
}

vi src/main/java/com/test/TestTransactionDataManagerImpl.java

package com.test;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

import org.springframework.stereotype.Repository;

/**
 * This data manager is used to test the transactional capabilities
 * of the spring framework. The code below is not marked as transactional
 * however it participates in one when called from a transactional method.
 */
@Repository("testTransactionDataManager")
public class TestTransactionDataManagerImpl implements TestTransactionDataManager {

	@PersistenceContext 
	private EntityManager em;

	/**
	 * Responsible for writing data to a transactional resource.
	 * @param data
	 */
	public Number writeData(String data) {
		DBRecord record = new DBRecord();
		record.setData(data);
    	em.persist(record);
    	em.flush();
    	return record.getId();
	}
}

Just an interface…

vi src/main/java/com/test/TestTransactionModel.java

package com.test;

public interface TestTransactionModel {
	void writeDataWithOutTx(String message);
	void writeDataWithMultipleCalls(String message);
	void writeDataWithTx(String message);
	void writeDataWithTxSuccess();
}

This is our Model class. The model layer is a natural place to define unit of work. Model methods are called from “control layer” in the MVC architecture. (Struts actions, SpringMVC Command, or Servlets) THERE SHOULD BE AT MOST ONE call to a transaction enabled model method to get a the “unit of work” behavior. Otherwise if you have more than one call to these methods and one succeeds and the other fails you will not get the “unit of work” behavior. Please keep this in mind… Only one call to a transactional method from the “control layer”.

vi src/main/java/com/test/TestTransactionModelImpl.java

package com.test;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service("testTransactionModel")
public class TestTransactionModelImpl implements TestTransactionModel {

	@Autowired
	private TestTransactionDataManager testTransactionDataManager;
	
	@Transactional
	@Override
	public void writeDataWithTxSuccess() {
		testTransactionDataManager.writeData("Writing data within a transactional method. You WILL see this record.");
	}

	public void writeDataWithOutTx(String message) {
		testTransactionDataManager.writeData("Writing data within a NON transactional method. You WILL NOT see this record.");
	}

	/**
	 * This method will throw a runtime exception and the data will be rolled
	 * back.
	 */
	@Transactional  // remember @Transaction for public methods only!
	public void writeDataWithTx(String message) {
		if(message == null) {
			message = "Writing data within a transactional method with an exception. You will NOT see this record.";
		}
		Number number = testTransactionDataManager.writeData(message);
		System.out.println("wrote item: " + number);
		throw new RuntimeException();
	}

	/**
	 * This method tests the capability of calling another method that is
	 * declared to be transactional where the second method throws an exception
	 * but this method still commits. This is important to know that calling nested
	 * transactional methods will not result in a commit.
	 */
	@Transactional
	public void writeDataWithMultipleCalls(String message) {
		String msg = "Suppressing runtimeexception and committing anyway you will see this record.";
		try {
			writeDataWithTx(msg);
		} catch(RuntimeException ex) {
			System.out.println(msg);
		}
	}
}

The following is the JPA Entity we will be writing to the database.
vi src/main/java/com/test/DBRecord.java

package com.test;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name="test_table")
public class DBRecord {
	@Id
	@GeneratedValue
	private Integer id;
	
	private String data;	
	
	public Integer getId() {
		return id;
	}
	public void setId(Integer id) {
		this.id = id;
	}
	public String getData() {
		return data;
	}
	public void setData(String data) {
		this.data = data;
	}
	@Override
	public String toString() {
		return "DBRecord [id=" + id + ", data=" + data + "]";
	}
}

The App.java JUnit Test doIt() method will call methods on the model layer.

vi src/test/java/com/test/App.java

package com.test;

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class App {
//	private ApplicationContext context = new ClassPathXmlApplicationContext("com/test/applicationContext.xml");
	static private ApplicationContext context = null;
	
	static {
		context = new AnnotationConfigApplicationContext(com.test.SpringConfig.class);
	}
		
	@Test
	public void doIt() {
		TestTransactionModel model = (TestTransactionModel) context.getBean("testTransactionModel");
		
		try {
			model.writeDataWithTxSuccess();
		} catch(Exception ex) {
		}
		try {
			model.writeDataWithOutTx(null);
		} catch (RuntimeException ex) { // do nothing
		}

		try {
			model.writeDataWithTx(null);
		} catch (RuntimeException ex) { // do nothing
		}

		try {
			model.writeDataWithMultipleCalls(null);
		} catch (RuntimeException rex) { // do nothing
		}
	}

	public App() {
		doIt();
	}


}

Results

The results get saved to the HSQLDB database location that is specified in the SpringConfig.java file. It defaults to a file in the /tmp dir. Open the .script file and you will find the data saved to the table towards the bottom.

15
Feb
16

Managed Bean scope for a AJAX JSF Page @RequestScoped vs @ViewScoped

This page will demonstrate the difference between a @RequestScope and @ViewScoped managed bean in a AJAX enabled JSF application.

Background

The @RequestScoped bean lives as long as a single HTTP request-response cycle. This includes AJAX request and responses. At the end of the cycle the bean will be no longer available.

The @ViewScoped managed bean lives as long as you are on the same view like action calls with post-backs that don’t result in the page changing. This includes actions that result in output being sent to another component on the page.

To demonstrate the difference between @ViewScoped and @RequestScoped we will define 2 managed beans each with a different Scope and use them within a AJAX enabled JSF page. We will use the Java EE @PostConstruct and @PreDestroy annotations to print out when each bean is created and destroyed.

Full downloadable source for this page is available here. Corrections and enhancements are welcome, fork, change and push back to GitHub.

Requirements

  • Java 8 or above
  • Maven 3 or above
  • Tomcat 7

Create the pom.xml

vi pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.test</groupId>
	<artifactId>JSFTest</artifactId>
	<version>20160215</version>
	<packaging>war</packaging>
	<name>JSFTest</name>

	<pluginRepositories>
		<pluginRepository> <!-- Ignore this repository. Its only used for document publication. -->
			<id>numberformat-releases</id>
			<url>https://raw.githubusercontent.com/numberformat/wordpress/master/20130213/repo/</url>
		</pluginRepository>
	</pluginRepositories>
	
	<repositories>
		<repository>
			<id>prime-repo</id>
			<name>PrimeFaces Maven Repository</name>
			<url>http://repository.primefaces.org</url>
			<layout>default</layout>
		</repository>
	</repositories>

	<dependencies>
		<dependency>
			<groupId>javax</groupId>
			<artifactId>javaee-web-api</artifactId>
			<version>6.0</version>
			<scope>provided</scope>
		</dependency>
		<dependency>
			<groupId>org.glassfish</groupId>
			<artifactId>javax.faces</artifactId>
			<version>2.1.6</version>
		</dependency>
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>servlet-api</artifactId>
			<version>3.0-alpha-1</version>
		</dependency>
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>jsp-api</artifactId>
			<version>2.0</version>
		</dependency>

		<dependency>
			<groupId>net.bootsfaces</groupId>
			<artifactId>bootsfaces</artifactId>
			<version>0.8.0</version>
			<scope>compile</scope>
		</dependency>
		<dependency>
			<groupId>org.primefaces</groupId>
			<artifactId>primefaces</artifactId>
			<version>5.3</version>
		</dependency>
		<dependency>
			<groupId>org.primefaces.themes</groupId>
			<artifactId>bootstrap</artifactId>
			<version>1.0.9</version>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-compiler-plugin</artifactId>
				<version>2.3.2</version>
				<configuration>
					<source>1.8</source>
					<target>1.8</target>
				</configuration>
			</plugin>
			
			<plugin> <!-- Ignore this plugin. Its only used for document publication. -->
				<groupId>github.numberformat</groupId>
				<artifactId>blog-plugin</artifactId>
				<version>1.0-SNAPSHOT</version>
				<configuration>
					<gitUrl>https://github.com/numberformat/wordpress/tree/master/${project.version}/${project.artifactId}</gitUrl>
				</configuration>
				<executions>
					<execution>
						<id>1</id>
						<phase>site</phase>
						<goals>
							<goal>generate</goal>
						</goals>
					</execution>
				</executions>
			</plugin>
						
		</plugins>
	</build>

</project>

The following is the Request Scoped Managed bean.

vi src/main/java/com/test/AddressBeanRequestScoped.java

package com.test;
 
import java.io.Serializable;
 
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ManagedProperty;
import javax.faces.bean.RequestScoped;
 
@ManagedBean
@RequestScoped
public class AddressBeanRequestScoped implements Serializable {
 
    private static final long serialVersionUID = 1L;
     
    private Long id;
    private String name;
    private String address;
    private String city;
    private String state;
    private String zip;
 
    @PostConstruct
    void init() {
        System.out.println("Initializing " + this.getClass().getName());
    }
     
    @PreDestroy
    void destroy() {
        System.out.println("Destroying " + this.getClass().getName());
    }
 
    public String getCity() {
        return city;
    }
    public void setCity(String city) {
        this.city = city;
    }
    public String getAddress() {
        return address;
    }
    public void setAddress(String address) {
        this.address = address;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public void handleKeyEvent() {
        address = address.toUpperCase();
    }
    public Long getId() {
        return id;
    }
    public void setId(Long id) {
        this.id = id;
    }
    public String getState() {
        return state;
    }
    public void setState(String state) {
        this.state = state;
    }
    public String getZip() {
        return zip;
    }
    public void setZip(String zip) {
        this.zip = zip;
    }   
}

The following is the View Scoped Managed bean.

vi src/main/java/com/test/AddressBeanViewScoped.java

package com.test;
 
import java.io.Serializable;
 
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;
 
@ManagedBean
@ViewScoped
public class AddressBeanViewScoped implements Serializable {
 
    private static final long serialVersionUID = 1L;
     
    private Long id;
    private String name;
    private String address;
    private String city;
    private String state;
    private String zip;
 
    @PostConstruct
    void init() {
        System.out.println("Initializing Address Bean.");
    }
     
    @PreDestroy
    void destroy() {
        System.out.println("Destroying Address Bean.");
    }
 
    public String getCity() {
        return city;
    }
    public void setCity(String city) {
        this.city = city;
    }
    public String getAddress() {
        return address;
    }
    public void setAddress(String address) {
        this.address = address;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public void handleKeyEvent() {
        address = address.toUpperCase();
    }
    public Long getId() {
        return id;
    }
    public void setId(Long id) {
        this.id = id;
    }
    public String getState() {
        return state;
    }
    public void setState(String state) {
        this.state = state;
    }
    public String getZip() {
        return zip;
    }
    public void setZip(String zip) {
        this.zip = zip;
    }   
}

The following AJAX enabled JSF page uses both beans.
vi src/main/webapp/ajaxExample.xhtml

<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
    xmlns:b="http://bootsfaces.net/ui"
    xmlns:f="http://java.sun.com/jsf/core"
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:p="http://primefaces.org/ui">
<h:head>
    <title>BootsFaces Basic Page</title>
</h:head>
<h:body>
    <b:container>
        <b:navBar brand="JSF Demo" brandHref="index.xhtml" inverse="true">
            <b:navbarLinks>
                <b:navLink value="More about Ajax"
                    href="http://www.primefaces.org/showcase/ui/ajax/basic.xhtml"></b:navLink>
            </b:navbarLinks>
        </b:navBar>
        <h:form>
 
            <b:panel title="Ajax Listener (on keyUp) View Scoped Bean" look="primary">
                <h:outputText value="Keyup: " />
                <p:inputText value="#{addressBeanViewScoped.address}">
                    <p:ajax event="keyup" update="out"
                        listener="#{addressBeanViewScoped.handleKeyEvent}" />
                </p:inputText>
                <h:outputText id="out" value="#{addressBeanViewScoped.address}" />
            </b:panel>
            <b:panel title="Ajax Listener (on keyUp) Request Scoped Bean" look="primary">
                <h:outputText value="Keyup: " />
                <p:inputText value="#{addressBeanRequestScoped.address}">
                    <p:ajax event="keyup" update="out2"
                        listener="#{addressBeanRequestScoped.handleKeyEvent}" />
                </p:inputText>
                <h:outputText id="out2" value="#{addressBeanRequestScoped.address}" />
            </b:panel>
 
        </h:form>
 
    </b:container>
</h:body>
</html>

And finally the web.xml file.

vi src/main/webapp/WEB-INF/web.xml

<?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"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
	id="WebApp_ID" version="3.0">
	<display-name>JSFTest</display-name>

	<welcome-file-list>
		<welcome-file>ajaxExample.xhtml</welcome-file>
	</welcome-file-list>

	<context-param>
		<param-name>primefaces.THEME</param-name>
		<param-value>bootstrap</param-value>
	</context-param>

	<context-param>
		<param-name>javax.faces.DEFAULT_SUFFIX</param-name>
		<param-value>.xhtml</param-value>
	</context-param>
	<servlet>
		<servlet-name>Faces Servlet</servlet-name>
		<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
		<load-on-startup>1</load-on-startup>
	</servlet>
	<servlet-mapping>
		<servlet-name>Faces Servlet</servlet-name>
		<url-pattern>*.xhtml</url-pattern>
	</servlet-mapping>

</web-app>

Start tomcat 7 and navigate to the following page:
http://localhost:8080/JSFTest/ajaxExample.xhtml

You should be able to view the difference on the eclipse console or Tomcat 7 console log while you type within the text boxes on the JSF page.

14
Feb
16

Log4j configuration with Java CDI (Contexts and Dependency Injection)

This page describes how to configure log4j in a Java CDI stand-alone application.

All of us seen code that looks like this over and over again.

private Logger logger = Logger.getLogger(this.getClass().getName());

We will use Java Contexts and Dependency Injection to create a custom log4j Logger for a service class.

Modify the pom.xml

vi pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.test</groupId>
	<artifactId>log4jCDI</artifactId>
	<version>20160214</version>

	<pluginRepositories>
		<pluginRepository> <!-- Ignore this repository. Its only used for document publication. -->
			<id>numberformat-releases</id>
			<url>https://raw.githubusercontent.com/numberformat/wordpress/master/20130213/repo/</url>
		</pluginRepository>
	</pluginRepositories>
	
	<dependencies>
		<dependency>
			<groupId>javax.enterprise</groupId>
			<artifactId>cdi-api</artifactId>
			<version>1.2</version>
		</dependency>
		<dependency>
			<groupId>org.jboss.weld.se</groupId>
			<artifactId>weld-se</artifactId>
			<version>2.2.6.Final</version>
		</dependency>
		<dependency>
			<groupId>log4j</groupId>
			<artifactId>log4j</artifactId>
			<version>1.2.17</version>
		</dependency>

		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>4.8.2</version>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-compiler-plugin</artifactId>
				<version>2.3.2</version>
				<configuration>
					<source>1.8</source>
					<target>1.8</target>
				</configuration>
			</plugin>
		
			<plugin> <!-- Ignore this plugin. Its only used for document publication. -->
				<groupId>github.numberformat</groupId>
				<artifactId>blog-plugin</artifactId>
				<version>1.0-SNAPSHOT</version>
				<configuration>
					<gitUrl>https://github.com/numberformat/wordpress/tree/master/${project.version}/${project.artifactId}</gitUrl>
				</configuration>
				<executions>
					<execution>
						<id>1</id>
						<phase>site</phase>
						<goals>
							<goal>generate</goal>
						</goals>
					</execution>
				</executions>
			</plugin>
		</plugins>
	</build>

</project>

Setup the log4j configuration files. We will be using a very basic file that outputs to the console.
vi src/main/resources/log4j.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
   
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
    <appender name="CONSOLE" class="org.apache.log4j.ConsoleAppender">
        <param name="Target" value="System.out"/>
        <param name="Threshold" value="INFO" />
        <layout class="org.apache.log4j.PatternLayout">
            <param name="ConversionPattern" value="%d %-5p %c - %m%n"/>
        </layout>
    </appender>
    <logger name="com.test" additivity="true">
        <level value="DEBUG"/>
    </logger>
    <root>
        <level value="ERROR"/>
        <appender-ref ref="CONSOLE"/>
    </root> 
</log4j:configuration>

Producer (Factory)

The following CDI Producer creates a log4j Logger whenever it encounters a @Inject. Java keeps track of all the methods that are annotated with @Producer. If it comes across an inject
that requires a type produced by one of these producers then it calls the producer method to get this instance. The producer method here accepts a InjectionPoint metadata object that helps identify the location of where the injection is occuring. We will use this information to create a log4j logger custom to the class where its being injected into.

vi src/main/java/com/test/LoggerProducer.java

package com.test;

import javax.enterprise.inject.Produces;
import javax.enterprise.inject.spi.InjectionPoint;

import org.apache.log4j.Logger;

public class LoggerProducer {
	@Produces
	public Logger produceLogger(InjectionPoint injectionPoint) {
		return Logger.getLogger(injectionPoint.getMember().getDeclaringClass().getName());
	}
}

Service Class

The following service class is responsible for printing out the different levels of log messages.

vi src/main/java/com/test/TestLogger.java

package com.test;

import javax.inject.Inject;

import org.apache.log4j.Logger;

public class TestLogger {	
	@Inject
	private transient Logger logger;
	
	private static final String THIS_IS = "Method: ";
	
	public void writeLogAllLevels() {
		logger.fatal(THIS_IS + new Object(){}.getClass().getEnclosingMethod().getName());
		logger.error(THIS_IS + new Object(){}.getClass().getEnclosingMethod().getName());
		logger.warn(THIS_IS + new Object(){}.getClass().getEnclosingMethod().getName());
		logger.info(THIS_IS + new Object(){}.getClass().getEnclosingMethod().getName());
		logger.debug(THIS_IS + new Object(){}.getClass().getEnclosingMethod().getName());
	}
}

Junit Test Case

Finally test the logging with a simple junit test case.

vi src/test/java/log4jCDI/TestApp.java

package log4jCDI;

import org.jboss.weld.environment.se.Weld;
import org.jboss.weld.environment.se.WeldContainer;
import org.junit.Test;

import com.test.TestLogger;

public class TestApp {
	static WeldContainer weld=null;
	static {
        weld = new Weld().initialize();
	}
	@Test
	public void test() {
        TestLogger testLogger = weld.instance().select(TestLogger.class).get();
        testLogger.writeLogAllLevels();
	}

}

Log4j xml file

This bare bones log4j file will print all log levels to the console.

vi src/main/resources/log4j.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
   
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
    <appender name="CONSOLE" class="org.apache.log4j.ConsoleAppender">
        <param name="Target" value="System.out"/>
        <param name="Threshold" value="INFO" />
        <layout class="org.apache.log4j.PatternLayout">
            <param name="ConversionPattern" value="%d %-5p %c - %m%n"/>
        </layout>
    </appender>
    <logger name="com.test" additivity="true">
        <level value="DEBUG"/>
    </logger>
    <root>
        <level value="ERROR"/>
        <appender-ref ref="CONSOLE"/>
    </root> 
</log4j:configuration>

beans.xml

Finally create a blank beans.xml file in your src/main/java/META-INF folder.

Run the Junit Test Case

At this time you will see output of each of the messages to the console similar to this:

2016-02-14 15:58:18,840 FATAL com.test.TestLogger - Method: writeLogAllLevels
2016-02-14 15:58:18,841 ERROR com.test.TestLogger - Method: writeLogAllLevels
2016-02-14 15:58:18,841 WARN  com.test.TestLogger - Method: writeLogAllLevels
2016-02-14 15:58:18,841 INFO  com.test.TestLogger - Method: writeLogAllLevels
Full downloadable source for this page is available here. Corrections and enhancements are welcome, fork, change and push back to GitHub.



Enter your email address to subscribe to this blog and receive notifications of new posts by email.

Join 74 other followers

February 2016
S M T W T F S
« Dec   Mar »
 123456
78910111213
14151617181920
21222324252627
2829  

Blog Stats

  • 801,397 hits