Spring + JTA + JPA + JMS

April 8th, 2010 13 comments

I recently worked on a Spring application that used Hibernate as JPA provider and JTA for transaction demarcation. In this post I will create a simple Order Processing Message Driven Bean that showcases this integration. I will be using an Oracle database and deploy the application on a WebLogic 10.3 server.

To keep things manageable, the Message Driven Bean is designed to consume messages from a Queue. When a new message gets added to the Queue, the Application Server delivers it to the MDB. The MDB then uses a Spring Managed Service/Repository to persist a new “Order” JPA Entity in the database. The application is configured such that this entire process runs in a container managed transaction. I will be using the Blog Queue I created in a previous post.

Step 1: The first step is to create an Order Entity and the corresponding database table to store the newly created orders.

 
package com.inflinx.blog.orderprocessing.domain;

import java.util.Date;

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

@Entity
@Table(name="ORDER_TABLE", schema="BLOGDEMO")
public class Order 
{
	@Id
	@Column(name="ORDER_ID")
	private Long id;
	
	@Column(name="NAME")
	private String name;
	
	@Column(name="CREATED_DATE")
	private Date createdDate;

	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 Date getCreatedDate() {
		return createdDate;
	}
	public void setCreatedDate(Date createdDate) {
		this.createdDate = createdDate;
	}
}

I have created the ORDER_TABLE in an Oracle database under the BLOGDEMO schema:

Order Table

Order Table

Step 2: In the WebLogic console, create a XA datasource to the database with the JNDI name jdbc.blgods.

XA Blog Data Source

XA Blog Data Source

The XA datasource is necessary for Hibernate to participate in a JTA global transaction. WebLogic also provides an option to emulate XA with the Emulate Two-Phase Commit.

Step 3: The next step is to create an OrderRepository that deals with persistence logic such as creating a new Order.

package com.inflinx.blog.orderprocessing.repository;

import com.inflinx.blog.orderprocessing.domain.Order;

public interface OrderRepository 
{
	public void createOrder(Order order);
}
package com.inflinx.blog.orderprocessing.repository;

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

import org.springframework.stereotype.Repository;

import com.inflinx.blog.orderprocessing.domain.Order;

@Repository("orderRepository")
public class OrderRepositoryImpl implements OrderRepository 
{
	// Spring injected EntityManager
	@PersistenceContext
    private EntityManager entityManager;
	
	@Override
	public void createOrder(Order order) 
	{
		 entityManager.persist(order);
	}
}

Step 4: Following the principles of layered architecture, the next step is to create an OrderService and its implementation.

package com.inflinx.blog.orderprocessing.service;

import com.inflinx.blog.orderprocessing.domain.Order;

public interface OrderService {
	public void createOrder(Order order);
}
package com.inflinx.blog.orderprocessing.service;

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

import com.inflinx.blog.orderprocessing.domain.Order;
import com.inflinx.blog.orderprocessing.repository.OrderRepository;

@Service("orderService")
@Transactional
public class OrderServiceImpl implements OrderService {

	@Autowired
	@Qualifier("orderRepository")
	private OrderRepository orderRepository;
	
	@Override
	public void createOrder(Order order) {
		// Simply delegate the call to repository layer
		orderRepository.createOrder(order);
	}
}

As you can see, the implementation is annotated with @Transactional to make the service call transactional.

Step 5: The next step is to create a persistence.xml file without any JPA provider information.

 

	
		
			com.inflinx.blog.orderprocessing.domain.Order
		

		

Step 6: The next step is to create a Spring configuration application-context.xml file to hold bean and transaction definition. In addition, the file also holds the JPA provider information.


       
	     
    
     
    
    
    
	
	
	
	
    
	    
    
    			
    				
    					
    					
    				
    			
    			
    				
    					hibernate.transaction.factory_class=org.hibernate.transaction.JTATransactionFactory
					hibernate.transaction.manager_lookup_class=org.hibernate.transaction.WeblogicTransactionManagerLookup
					hibernate.current_session_context_class=jta
					hibernate.transaction.flush_before_completion=true						
					hibernate.connection.release_mode=auto
    				
    			
    			
    	
             

Step 7: With the above configuration in place, the next step is to create a Message Driven Bean that processes incoming messages. The MDB is annotated to use container managed transactions:

 
package com.inflinx.blog.mdb;

import java.util.Date;

import javax.ejb.ActivationConfigProperty;
import javax.ejb.MessageDriven;
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;
import javax.ejb.TransactionManagement;
import javax.ejb.TransactionManagementType;
import javax.interceptor.Interceptors;
import javax.jms.Message;
import javax.jms.MessageListener;

import org.apache.commons.lang.RandomStringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.ejb.interceptor.SpringBeanAutowiringInterceptor;

import com.inflinx.blog.orderprocessing.domain.Order;
import com.inflinx.blog.orderprocessing.service.OrderService;


@MessageDriven(
		mappedName = "jndi.blogQueue",
		name="orderProcessor",
		activationConfig = { @ActivationConfigProperty(
				propertyName = "destinationType", propertyValue = "javax.jms.Queue"
		)}
)
@Interceptors(SpringBeanAutowiringInterceptor.class)
@TransactionManagement(TransactionManagementType.CONTAINER)
public class OrderProcessor implements MessageListener
{
	@Autowired
	@Qualifier("orderService")
	private OrderService orderService;
	
    @Override
    @TransactionAttribute(TransactionAttributeType.REQUIRED)
	public void onMessage(Message message) 
	{
    	// Create a new Order
    	Order order = new Order();
    	order.setId(System.currentTimeMillis());
    	order.setName(RandomStringUtils.randomAlphabetic(5));
    	order.setCreatedDate(new Date());
    	
    	// Save the order
    	orderService.createOrder(order);
	}
	
}

Step 8: The SpringBeanAutowiringInterceptor by default looks for a beanRefContext.xml file to create a spring context. So the final step in the process is to create a beanRefContext.xml with a reference to the application-context.xml.




			 
		
							
				application-context.xml
			
		 		
	
	 

Now when a new message gets added to the Queue, the onMessage method will run inside a transaction and will create a new Order. Here is a new Order record in the database:

Order Table Populated

Order Table Populated

Categories: JMS, JPA, JTA, Spring Tags: , , ,

JRebel – First Impressions

April 2nd, 2010 No comments

I have been wanting to try JRebel for a long time and finally installed the 30 day trial version on Eclipse. The Windows Installer made the installation very easy. The documentation in the Configuration Wizard was great help to complete IDE integration.

I am using JRebel in a Spring 3.x project and the only problem I see so far is this Exception being thrown once in a while:

JRebel: Reloading class ‘com.blog.inflinx……SearchCommand’.
JRebel: An error occured in a JRebel plugin while handling a class reload event:
java.lang.NoClassDefFoundError: org/springframework/beans/factory/CannotLoadBeanClassException
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanNamesForType(DefaultListableBeanFactory.java:305)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeansOfType(DefaultListableBeanFactory.java:382)

Looks like the package changes made in Spring 3.x is causing this. I will be posting updates on this experimentation.

Update 1: I am using JRebel in a multi module Maven project and it is working great. However, I once noticed that it didn’t update my changes in a Spring bean.

Update 2: I just upgraded to 3.0 and did not run into any issues. I wish there was an upgrade guide with the download.

Categories: JRebel Tags:

Spring EJB 3 Integration

March 23rd, 2010 2 comments

Integrating Spring with EJB allows Session and Message Driven Beans leverage existing Spring infrastructure easily. In this post, I will show how to access Spring beans from a Message Driven Bean. This post builds on my previous “EJB 3 Message Driven Beans in WebLogic 10.3” post.

Step 1: Add the following Spring jar files to the APP-INFlib folder of the EAR project.

org.springframework.aop-3.0.0.RELEASE.jar
org.springframework.asm-3.0.0.RELEASE.jar
org.springframework.aspects-3.0.0.RELEASE.jar
org.springframework.beans-3.0.0.RELEASE.jar
org.springframework.context.support-3.0.0.RELEASE.jar
org.springframework.context-3.0.0.RELEASE.jar
org.springframework.core-3.0.0.RELEASE.jar
org.springframework.expression-3.0.0.RELEASE.jar
org.springframework.jms-3.0.0.RELEASE.jar
commons-logging.jar
log4j-1.2.14.jar

Step 2: The next step is to create a Spring context file in the EJB project to hold the bean definitions. Here is the application-Context.xml file with a simple Spring bean:

	
	

			
							
			

	           

When deployed, the application-Context file should be at the root of the EJB jar.

Step 3: The next step is to modify the Message Driven Bean to use the messageSuffix Spring bean. Here is the modified MDB:

	public class TestMdb implements MessageListener
	{
		@Autowired
		@Qualifier("messageSuffix")
		private String messageSuffix;
		
		public void onMessage(Message message)
		{
			System.out.println(message + ". " + messageSuffix);
		}
	}

Step 4: To next step is to add SpringBeanAutowiringInterceptor to the MDB. The SpringBeanAutowiringInterceptor is an EJB 3 compliant interceptor that injects Spring beans into @AutoWired annotated fields and methods. Here is the complete MDB:


	package com.inflinx.blog.springmdb;

	import javax.ejb.ActivationConfigProperty;
	import javax.ejb.MessageDriven;
	import javax.interceptor.Interceptors;
	import javax.jms.Message;
	import javax.jms.MessageListener;

	import org.springframework.beans.factory.annotation.Autowired;
	import org.springframework.beans.factory.annotation.Qualifier;
	import org.springframework.ejb.interceptor.SpringBeanAutowiringInterceptor;

	@MessageDriven(
			mappedName = "jndi.blogQueue",
			activationConfig = { @ActivationConfigProperty(
					propertyName = "destinationType", propertyValue = "javax.jms.Queue"
			)}
	)
	@Interceptors(SpringBeanAutowiringInterceptor.class)
	public class TestMdb implements MessageListener
	{
		@Autowired
		@Qualifier("messageSuffix")
		private String messageSuffix;

		public void onMessage(Message message)
		{
			System.out.println(message + ". " + messageSuffix);
		}
	}

The SpringBeanAutowiringInterceptor by default looks for a beanRefContext.xml file in the classpath to create an ApplicationContext. Create the beanRefContext.xml file with the following content:


	
	

				 
			 		
		
	

This completes the Spring EJB integration. Deploy the EJB and run the MessageGenerator test class. You should see the message “TextMessage[ID:<655281.1269371402940.0>, null]. Now With Spring!!” in the server console.

Download the source code for this project here.

Categories: How To, Spring Tags:

EJB 3 Message Driven Beans in WebLogic 10.3

March 21st, 2010 2 comments

In this post, I will show how to create and test Message Driven Beans in WebLogic 10.3. Here are the steps:

Step 1: The first step is to create projects to hold Message Driven Beans. Using Eclipse IDE, here are the generated EAR and EJB projects:

MDB Project Layout

MDB Project Layout

EAR is the standard packaging mechanism for Java enterprise applications.

Step 2: The next step is to create the Message Driven Bean. The MDB created in this post will be consuming messages from a Distributed Queue with the JNDI name jndi.blogQueue. Refer to the “Distributed JMS Queue on WebLogic 10” post for details on creating Distributed Queues in WebLogic.

	package com.inflinx.blog.springmdb;

	import javax.ejb.ActivationConfigProperty;
	import javax.ejb.MessageDriven;
	import javax.jms.Message;
	import javax.jms.MessageListener;

	@MessageDriven(
			mappedName = "jndi.blogQueue",
			activationConfig = { @ActivationConfigProperty(
					propertyName = "destinationType", propertyValue = "javax.jms.Queue"
				)}
			)

	public class TestMdb implements MessageListener 
	{
	    public void onMessage(Message message) 
	    {
		System.out.println("Received Message: " + message);
	    }
	}

Step 3: The next step is to package the newly created MDB into an EJB which then gets packaged into a ear file for deployment. During development in Eclipse, simply right click on the mdb-ear and click Run -> Run On Server. This IDE deployment assumes that the Oracle Server Adapter is installed in Eclipse and configured to talk to a WebLogic 10.3 server instance.

Step 4: To make sure that the MDB is deployed properly, let us create a MessageGenerator class that adds a message to the queue:

 
		
	public class MessageGenerator 
	{
		public static void main(String[] args) throws Exception
		{
			Context context = getInitialContext();
			
			ConnectionFactory connectionFactory = (ConnectionFactory)context.lookup("jndi.blogfactory");
			Queue queue = (Queue) context.lookup("jndi.blogQueue");
			Connection connection = connectionFactory.createConnection();
			Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
			
			MessageProducer producer = session.createProducer(queue);
	
			TextMessage message = session.createTextMessage();
			message.setText("Hello World");
			producer.send(message);
			
			connection.close();
		}
		
		private static Context getInitialContext() throws Exception
		{
			Hashtable env = new Hashtable();
			env.put(Context.INITIAL_CONTEXT_FACTORY, "weblogic.jndi.WLInitialContextFactory");
			// TODO: Change the server and port name to suit your environment before running the class
			env.put(Context.PROVIDER_URL, "t3://localhost:9001");
			return new InitialContext(env);
		}
	}

Running the above class will add a new TextMessage to the Queue. Once a new message is available, the TestMDB’s onMessage() method gets invoked and you should see the message “Received Message: TextMessage[ID:, null]” in the WebLogic console logs.

For the above class to run from Eclipse, make sure that you remove “WebLogic System Libraries” and add weblogic.jar to the MessageGenerator “Run Configurations…”.

Message Generator Run Configuration

Message Generator Run Configuration

Otherwise you will end up getting the error:

Exception in thread “main” java.lang.NoClassDefFoundError: weblogic/kernel/KernelStatus
at weblogic.jndi.Environment.(Environment.java:78)
at weblogic.jndi.WLInitialContextFactory.getInitialContext(WLInitialContextFactory.java:117)
at javax.naming.spi.NamingManager.getInitialContext(NamingManager.java:667)
at javax.naming.InitialContext.getDefaultInitCtx(InitialContext.java:288)
at javax.naming.InitialContext.init(InitialContext.java:223)
at javax.naming.InitialContext.(InitialContext.java:197)
at com.inflinx.blog.test.MessageGenerator.getInitialContext(MessageGenerator.java:40)
at com.inflinx.blog.test.MessageGenerator.main(MessageGenerator.java:18)

 

Download the source code for this post here.

Optional Packages in Weblogic 10.3

March 18th, 2010 3 comments

Optional Packages provide a great way to share individual jar files among multiple applications. Reusable java classes and third party framework classes are good candidates for deploying as optional packages. In this post I will show how to install and use Optional Packages in Weblogic 10.3.

I. Create Jar file
The first step in the process is to bundle a reusable Java class as a Jar file. To keep things simple, I will create a simple String Utility class.

 	
	public class StringUtil
	{
		public static boolean isEmpty(String text)
		{
			return null == text || "".equals(text);
		}
		
	}

The next step is to create a MANIFEST.MF file with the following information:

Manifest-Version: 1.0
Extension-Name: StringLib
Specification-Version: 1.0.0.0
Implementation-Version: 1.0.0.0

As you can see, the MANIFEST.MF file holds the information about the library such as name and version. The next step is to package the application into a jar file.

 

II. Install Optional Packages
Launch WebLogic console and click on Deployments:

Domain Structure

Domain Structure

Click on Install and on the next screen, browse to the folder containing the jar and select the jar:

Select Jar

Select Jar

Select the defaults on the next page and hit Finish. Restart the server for the changes to take effect.

 

III. Using Optional Packages in Web Application
Open the application’s MANIFEST.MF under META-INF folder and add the following entries:

Manifest-Version: 1.0
Extension-List: strlib
strlib-Extension-Name: StringLib
strlib-Specification-Version: 1.0.0.0
strlib-Implementation-Version: 1.0.0.0

Modify application’s code to start using the StringLib library. Even though Optional Packages are not deployed along with the application, you still might need them in your application’s class path during development. Maven users can do this by changing the jar dependency to provided in their pom.xml file.

 

Download stringlib.jar and demo war file along with source code here.

Categories: Solutions Log Tags:

JSR 303 Bean Validation Using Spring 3

March 10th, 2010 27 comments

The JSR 303 specification provides a metadata model for JavaBean validation. The validation API provides a variety of annotations that makes validation easy at any layer. In this post I will demo Spring 3’s support for JSR 303 in the web layer. You can download the code here (eclipse project).

The first part of the post talks about creating a simple bookstore admin application using Spring MVC. The second part of the post will add JSR 303 validation. If you are an experienced Spring developer, you can safely skip to the second part. The final part will show how to customize the validation error messages.

1. Online Bookstore Admin Application

The admin application will be designed to allow administrators enter information about new books. Here are the steps that implement this requirement:

Step 1. Create a web project and add all the required jars
I will be using Eclipse’s and here is a list of the required jars that need to be in the classpath:

org.springframework.asm-3.0.1.RELEASE.jar
org.springframework.beans-3.0.1.RELEASE.jar
org.springframework.context-3.0.1.RELEASE.jar
org.springframework.context.support-3.0.1.RELEASE.jar
org.springframework.core-3.0.1.RELEASE.jar
org.springframework.expression-3.0.1.RELEASE.jar
org.springframework.test-3.0.1.RELEASE.jar
org.springframework.web-3.0.1.RELEASE.jar
org.springframework.web.servlet-3.0.1.RELEASE.jar
commons-collections-3.1.jar
hibernate-validator-4.0.2.GA.jar
jcl-over-slf4j-1.5.10.jar
joda-time-1.6.jar
jstl-1.2.jar
log4j.jar
slf4j-api-1.5.8.jar
slf4j-log4j12.jar
validation-api-1.0.0.GA.jar

Step 2. Create a web-Context.xml file under WEB-INF folder to hold web layer spring bean configuration. Here are the contents of the file:

		
		<?xml version="1.0" encoding="UTF-8"?>
		<beans xmlns="http://www.springframework.org/schema/beans"
			xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
			xmlns:context="http://www.springframework.org/schema/context"
			xmlns:mvc="http://www.springframework.org/schema/mvc"
			xsi:schemaLocation="
				http://www.springframework.org/schema/beans	http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
				http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
				http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">
			
			<!-- Enable annotation driven controllers, validation etc... -->
			<mvc:annotation-driven />
			
			<!-- Controllers package -->
			<context:component-scan base-package="com.inflinx.blog.bookstore.web.controller" />
		
			<!-- JSP page location -->
			<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
				<property name="suffix" value=".jsp"/>
			</bean>
			 	
		</beans>
			
	

Step 3. Add Spring MVC Dispatcher Servlet to web.xml file. In the declaration below, Spring MVC will handle all URL requests to html pages.

		
		<?xml version="1.0" encoding="UTF-8"?>
		<web-app version="2.5" 
			xmlns="http://java.sun.com/xml/ns/javaee" 
			xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
			xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
			http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
			  
		  <servlet>
				<servlet-name>bookstore</servlet-name>
				<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
				<init-param>
					<param-name>contextConfigLocation</param-name>
					<param-value>/WEB-INF/web-Context.xml</param-value>
				</init-param>
				<load-on-startup>1</load-on-startup>
			</servlet>
		
			<servlet-mapping>
				<servlet-name>bookstore</servlet-name>
				<url-pattern>*.html</url-pattern>
			</servlet-mapping>
		
		</web-app>
		
	

Step 4. Create a Book domain object to hold information about a book:

		package com.inflinx.blog.bookstore.domain;

		public class Book
		{
			private String name;
			private String description;

			public String getName()
			{
				return name;
			}
			public void setName(String name)
			{
				this.name = name;
			}
			public String getDescription()
			{
				return description;
			}
			public void setDescription(String description)
			{
				this.description = description;
			}

			@Override
			public String toString()
			{
				return "Name: " + name + ", Description: " + description ;
			}
		}	
	

Step 5. Create a BookFormController to prepare the form and process form submission

		package com.inflinx.blog.bookstore.web.controller;
		
		import java.util.Map;
		
		import org.springframework.stereotype.Controller;
		import org.springframework.web.bind.annotation.RequestMapping;
		import org.springframework.web.bind.annotation.RequestMethod;
		
		import com.inflinx.blog.bookstore.domain.Book;
		
		@Controller
		@RequestMapping("/book.html")
		public class BookFormController
		{
			// Display the form on the get request
			@RequestMapping(method=RequestMethod.GET)
			public String showForm(Map model)
			{
				Book book  = new Book();
				model.put("book", book);
				return "form";
			}
			
		}
	
	

Step 6. Create form.jsp page with a form to capture book details:

	
		<%@ taglib uri="http://www.springframework.org/tags/form" prefix="form" %>
		<html>
			<head></head>
			<body>
				<form:form method="post" action="book.html" commandName="book">
					<table>
						<tr>
							<td>Name:</td> <td><form:input path="name" /></td>
						</tr> 
						<tr>
							<td>Description:</td> <td><form:textarea path="description"/></td>
						</tr>
						</table>
					<input type="submit" value="Create" />
				</form:form>
			</body>
		</html>
	
	
	

The form will be submitted to the book.html page.

Step 7. Modify BookFormController to read the submitted data and process it.

		
		// Process the form. 
		@RequestMapping(method=RequestMethod.POST)
		public String processForm(Book book, Map model)
		{
			// Persistence logic to save the book will go here

			// Add the saved book to the model
			model.put("book", book);
			return "result";
		}	
	

Step 8. Create a result.jsp page to display the form submission result:

		<html>
			<body>
				${book.name} is stored in the database
			</body>
		</html>
	

Deploy the application and go to http://<yourserver:port>/bookstore/book.html. The page should present a form shown below:

Form

Form

Enter the book name “Test Book” and submit the form. You should see a screen that looks similar to image below:

Form Submission Result

Form Submission Result

 

2 Adding JSR 303 validation

Consider the following validation requirements to the book class:
a. Name and Description cannot be null or blank
b. Description cannot be more than 50 characters

Here are the steps to add the above validation requirements:

Step 1. Add @NotEmpty and @Size annotations to the name and description files of the Book class

		public class Book
		{
			@NotEmpty
			private String name;
			
			@Size(min=1, max=50)
			private String description;
			
			// Getters and setters here	
		}
	

NotEmpty annotation is not part of JSR 303 specification. It is part of Hibernate Validator framework which is the reference implementation for JSR 303.

Step 2. Modify BookFormController to have Spring validate the submitted data. This is done by adding a @Valid annotation before the book method parameter. The result of the validation can be accessed using the BindingResult method parameter.

		// Process the form. 
		@RequestMapping(method=RequestMethod.POST)
		public String processForm(@Valid Book book, BindingResult result, Map model)
		{
			if(result.hasErrors())
			{
				return "form";
			}
			// Persistence logic to save the book will go here

			// Add the saved book to the model
			model.put("book", book);
			return "result";
		} 	
 	

Step 3. Modify the form.jsp so that error messages can be displayed:

 		<form:form method="post" action="book.html" commandName="book">
					<table>
						<tr>
							<td>Name:</td> <td><form:input path="name" /></td> <td><form:errors path="name" /></td>
						</tr> 
						<tr>
							<td>Description:</td> <td><form:textarea path="description"/></td> <td><form:errors path="description" /></td>
						</tr>
						</table>
					<input type="submit" value="Create" />
		</form:form>
 	

Redeploy the application and when you submit an empty form, you should see validation failure messages next to the fields:

Validation Failed

Validation Failed

 

3 Customizing error messages

The validation failure messages in the image above are default to the framework. To make custom error messages appear follow these steps:

Step 1. Create a messages.properties file under the WEB-INF folder. Add the following messages:

   		NotEmpty.book.name=Name is a required field
		Size.book.description=Description must be between 1 and 50 characters
   	

Each message above follows this convention:

<CONSTRAINT_NAME>.<COMMAND_NAME>.<FIELD_NAME>

Step 2. Let Spring know about the newly created properties file. This is done by modifying the web-Context.xml file to include this bean declaration:

   	
			
	
    

Redeploy the application and submit an empty form. This time you should see custom error messages appear next to the fields:

Custom Validation Failed Messages

Custom Validation Failed Messages

 

Download the source code for this post here. The application is tested on Glassfish 3.0 and WebLogic 10.3.

 

weblogic.xml deployment descriptor

December 31st, 2009 1 comment

If you are using 10.3.0 version of Weblogic here is a sample weblogic.xml file with correct schema information:




			
	YOUR_APPLICATION_CONTEXT_NAME
	


And here is a sample weblogic.xml file for version 10.3.2 version:





     	YOUR_APPLICATION_CONTEXT_NAME



If you use the wrong version file, you might end up with exceptions like this:

com.bea.xml.XmlException: failed to load java type corresponding to e=weblogic-web-app@http://xmlns.oracle.com/weblogic/weblogic-web-app
at com.bea.staxb.runtime.internal.UnmarshalResult.getPojoBindingType(UnmarshalResult.java:361)
at com.bea.staxb.runtime.internal.UnmarshalResult.determineTypeForGlobalElement(UnmarshalResult.java:316)
at com.bea.staxb.runtime.internal.UnmarshalResult.determineTypeForGlobalElement(UnmarshalResult.java:326)
at com.bea.staxb.runtime.internal.UnmarshalResult.determineRootType(UnmarshalResult.java:307)
at com.bea.staxb.runtime.internal.UnmarshalResult.unmarshalDocument(UnmarshalResult.java:158)
Truncated. see log file for complete stacktrace

Categories: Solutions Log Tags: