Developing a loan processing application using Apache CXF & JPA in NetBeans 6.8By Arulazi DhesiaseelanOverviewThis document is a step-by-step guide on developing a simple loan processing application in NetBeans 6.8 using Apache CXF 2.2 and JPA. My blog entry refers to some of the benefits of using NetBeans 6.8 for Java application development. 1. What's coveredThe following list details all of the various parts of the loan processing application development that are covered over the course of the tutorial.
2. Prerequisite softwareThe following prerequisite software and environment setup is assumed. You should also be reasonably comfortable using the following technologies.
NetBeans IDE 6.8 Web & Java EE edition (http://download.netbeans.org/netbeans/6.8/final/) provides an excellent environment for rapid application development. 3. The application we are buildingOur sample application is a very basic loan processing system. I will be addressing the basic use case where the borrower loan information is entered in the loan system and the loan is decisioned using credit and decisioning services. This use case is just a tip of ice berg in loan processing systems. The domain model is implemented using JPA. Credit and decisions services are implemented using CXF. This loan processing system is severely constrained in terms of scope. The whole purpose of this tutorial is how we leverage the powerful NetBeans infrastructure to rapidly develop, prototype and test Java applications in a productive way. Apache CXF is gaining momentum due to its simple and powerful programming model for services development. It has decent tooling support for web services. It recently graduated from Apache incubation. CXF 2.2 release supports whole lot of features around web services security, addressing, reliability and RESTful web services. We are not considering implementing UI aspects for our loan processing scenario as this aspect is covered in many other tutorials. We build secure web sevices using CXF for external systems such as credit and decisioning. The application can be easily extended to implement more robust and feature rich loan processing system. This application is definitely constrained by some limitations due to the varied implementation options and bandwidth. I will try to improve this tutorail over the course of time. But, this should be help in providing some ground work for building similar applications. Chapter 1. Basic Application and Environment Setup1.1. Create the NetBeans maven project and Create MySQL databaseWe are going to need a place to keep all the source and other files we will be creating, so let's create a maven project named 'LoanProcessing'. Create a new 'Maven Project' project from File -> New Project and click Next Maven -> Maven Project and click Next. Enter 'LoanProcessing' as the project name and click Finish. Project Creation Wizard - Step1 Project Creation Wizard - Step2
Project Creation
Wizard - Step3
MySQL Registration Enter the MySQL server basic properties and then select the admin properties tab within the MySQL server properties window. Provide the location of your MySQL installation in this window. The screen shot below shows settings for my installation. You can also point to the MySQL Admin Tools which provides more management capabilities for the MySQL server. This requires a separate installation of MySQL GUI Tools package. MySQL Basic
Properties Once configuring the MySQL server, you should be able to start the server just by right clicking the MySQL entry in Databases tree, if you have configured the MySQL Admin properties in NetBeans. I would prefer to manage MySQL server using MySQL Workbench which is more reliable and provides better capabilities to manage server instances. Setup of MySQL Workbench is beyond the scope of this tutorial. Here is a screen shot of my instance configured in MySQL Workbench. MySQL Workbench Admin Console Next we will be creating a database for our sample application. Right click the MySQL server node in the databases tree to create a MySQL database 'loandb'. I have created a 'loandb' user which we will be using for our sample application. You must create a test user before creating the 'loandb' database. Here is a sample script:
MySQL Database Creation
MySQL Database Connection
MySQL Database Connection
The following script creates the necessary tables required for our loan processing application. 'setuploandb_ApacheCXFonNetBeans.sql':
You should be able to see the tables in the loandb database. The data types for most of the columns are defined as VARCHAR for simplicity. MySQL Table Creation
Chapter 2. Loan Processing Domain Model Our domain classes are Application, Applicant, and Address. The structure of these model is defined in the above database schema. In addition to these entities, we need to persist the CreditBureauResponse summary received from credit rating agencies. This is persisted in the CBRSummary table. The database schema is self explanatory. 2.1. Create JPA entitiesDeveloping JPA applications is a pleasure with NetBeans as it has a tighter integration with JPA Providers. Right click the project and select 'New' -> 'Other' -> 'Persistence' -> 'Entity Classes from Database'. Choose the MySQL database connection from the dropdown and add all the tables from left column to the right column as shown below. Create Entities from Database Tables Create PU for EclipseLink
Click Next to select the Mapping Options. Select List for the Collection Type. Finish Entity
Creation Click Finish to complete the entity creation process.
Project Structure You may also notice that your POM is automatically updated to include the EclipseLink dependencies.
You need to generate the constructors from NetBeans (Alt + Insert shortcut) for all our domain classes. This will be used for creating and persisting entities in our tests.
2.2. Create DAO for domain classes We need to create DAO for operating on these domain objects. DAO provides CRUD operations on the model. The following classes use the JPA APIs to perform CRUD. AddressDAO, ApplicantDAO, ApplicationDAO, and CBRSummaryDAO. We will be defining one of these DAO in this tutorial and you can use same approach to implement other DAOs. All these classes are defined in the package 'com.mycompany.loanprocessing.dao'. The class AddressDao shown below is self-explanatory.
2.3. Unit testingLet us unit test our domain classes using JUnit. The below test case creates some sample data and performs CRUD operations on these data using the DAO classes. We create three loan applications and each with a residential and property address. Simple enum types are defined for our domain (AddressType, LoanType, ApplicantRole, DecisionType, CreditBureauType). package com.mycompany.loanprocessing;import com.mycompany.loanprocessing.dao.AddressDao;import com.mycompany.loanprocessing.dao.ApplicantDao;import com.mycompany.loanprocessing.dao.ApplicationDao;import com.mycompany.loanprocessing.util.AddressType;import com.mycompany.loanprocessing.util.ApplicantRole;import com.mycompany.loanprocessing.util.LoanType;import java.util.List;import javax.persistence.EntityManager;import javax.persistence.EntityManagerFactory;import javax.persistence.Persistence;import org.junit.After;import org.junit.AfterClass;import org.junit.Before;import org.junit.BeforeClass;import org.junit.Test;import static org.junit.Assert.*;/** * * @author aruld */public class LoanProcessingDAOTest { private static ApplicationDao am; private static AddressDao adm; private static ApplicantDao apm; private static EntityManager em; private static EntityManagerFactory emf; private static final Address address1 = new Address(1, "4209 Van Buren Dr", "Apt # 158", "POLK", AddressType.RESIDENTIAL.toString(), "WEST DES MOINES", "50266", "IA"); private static final Address address2 = new Address(2, "1265 11th ST", "Apt # 208", "POLK", AddressType.RESIDENTIAL.toString(), "WEST DES MOINES", "50265", "IA"); private static final Address address3 = new Address(3, "1660 Country Manor Blvd", "Apt # 226B", "YELLOWSTONE", AddressType.RESIDENTIAL.toString(), "BILLINGS", "59102", "MT"); private static final Address paddress1 = new Address(4, "1120 Jordan Creek PKWY", "Unit # 123", "POLK", AddressType.PROPERTY.toString(), "WEST DES MOINES", "50266", "IA"); private static final Address paddress2 = new Address(5, "1120 Jordan Creek PKWY", "Unit # 213", "POLK", AddressType.PROPERTY.toString(), "WEST DES MOINES", "50266", "IA"); private static final Address paddress3 = new Address(6, "1120 Jordan Creek PKWY", "Unit # 312", "POLK", AddressType.PROPERTY.toString(), "WEST DES MOINES", "50266", "IA"); private static final Application application1 = new Application(1, 120000.00, LoanType.FIXED.toString(), 1); private static final Application application2 = new Application(2, 110000.00, LoanType.ADJUSTABLE.toString(), 2); private static final Application application3 = new Application(3, 90000.00, LoanType.FIXED.toString(), 3); private static final Applicant applicant1 = new Applicant(1, "000-00-0001", "07/11/1960", "KEN", "CUSTOMER", "", ApplicantRole.PRIMARY.toString(), 1, 1); private static final Applicant applicant2Primary = new Applicant(2, "000-00-0002", "08/11/1960", "ALAN", "APPLICANT", "", ApplicantRole.PRIMARY.toString(), 2, 2); private static final Applicant applicant2Secondary = new Applicant(3, "000-00-0003", "09/11/1965", "EMILY", "APPLICANT", "", ApplicantRole.SECONDARY.toString(), 2, 2); private static final Applicant applicant3 = new Applicant(4, "000-00-0004", "10/11/1970", "WILL", "SMITH", "", ApplicantRole.PRIMARY.toString(), 3, 3); public LoanProcessingDAOTest() { } @BeforeClass public static void setUpClass() throws Exception { } @AfterClass public static void tearDownClass() throws Exception { } @Before public void setUp() { emf = Persistence.createEntityManagerFactory("LoanDBPU"); em = emf.createEntityManager(); am = new ApplicationDao(emf); apm = new ApplicantDao(emf); adm = new AddressDao(emf); adm.removeAll(); apm.removeAll(); am.removeAll(); } @After public void tearDown() { //this will remove all the entries in case if there are uncomplete/failed tests. //adm.removeAll(); //apm.removeAll(); //am.removeAll(); am.close(); adm.close(); apm.close(); em.close(); emf.close(); } @Test public void testCRUD() { adm.createAddress(address1); Address address = adm.findById(1); assertEquals(address.getAddressLine1(), "4209 Van Buren Dr"); address.setAddressLine1("4210 Van Buren Dr"); adm.updateAddress(address); address = adm.findById(1); assertEquals(address.getAddressLine1(), "4210 Van Buren Dr"); adm.createAddress(address2); adm.createAddress(address3); adm.createAddress(paddress1); Address paddress = adm.findById(4); assertEquals(paddress.getAddressLine1(), "1120 Jordan Creek PKWY"); paddress.setAddressLine2("AshCreek Apartments Unit # 123"); adm.updateAddress(paddress); paddress = adm.findById(4); assertEquals(paddress.getAddressLine2(), "AshCreek Apartments Unit # 123"); paddress.setAddressLine2("Unit # 123"); adm.updateAddress(paddress); adm.createAddress(paddress2); adm.createAddress(paddress3); am.createApplication(application1); Application application = am.findById(1); assertEquals(application.getLoanAmount(), Double.valueOf(120000.00)); application.setLoanAmount(117000.00); am.updateApplication(application); application = am.findById(1); assertEquals(application.getLoanAmount(), Double.valueOf(117000.00)); am.createApplication(application2); am.createApplication(application3); apm.createApplicant(applicant1); Applicant applicant = apm.findById(1); assertEquals(applicant.getFirstName(), "KEN"); applicant.setFirstName("KENNETH"); apm.updateApplicant(applicant); applicant = apm.findById(1); assertEquals(applicant.getFirstName(), "KENNETH"); apm.createApplicant(applicant2Primary); apm.createApplicant(applicant2Secondary); apm.createApplicant(applicant3); List applicantList = apm.getAll(); assertEquals(applicantList.size(), 4); //uncomment to clean up the database /*apm.removeAll(); applicantList = apm.getAll(); assertEquals(applicantList.size(), 0);*/ List applicationList = am.getAll(); assertEquals(applicationList.size(), 3); //uncomment to clean up the database /*am.removeAll(); applicationList = am.getAll(); assertEquals(applicationList.size(), 0);*/ List addressList = adm.getAll(); assertEquals(addressList.size(), 6); //uncomment to clean up the database /*adm.removeAll(); addressList = adm.getAll(); assertEquals(addressList.size(), 0); */ }}You may want to include test dependencies for MySQL JDBC driver and JUnit in your Maven POM. Click F11 to build the project and run the tests. Build Main Project You can also run the test directly by right clicking on the Test and select 'Test File' (Ctrl + F6) and make sure the simple CRUD test passes. Executing JUnit tests Chapter 3. CXF ServicesThe following CXF dependencies has to be included to our Maven POM: <dependency> <groupId>org.apache.cxf</groupId> <artifactId>cxf-rt-frontend-jaxws</artifactId> <version>2.2.6</version> </dependency> <dependency> <groupId>org.apache.cxf</groupId> <artifactId>cxf-rt-ws-security</artifactId> <version>2.2.6</version> </dependency> <dependency> <groupId>org.apache.ws.security</groupId> <artifactId>wss4j</artifactId> <version>1.5.8</version> </dependency> <dependency> <groupId>org.apache.cxf</groupId> <artifactId>cxf-rt-transports-http-jetty</artifactId> <version>2.2.6</version> </dependency> <dependency> <groupId>org.apache.cxf</groupId> <artifactId>cxf-rt-transports-http-jetty</artifactId> <version>2.2.6</version> </dependency>3.1. Interface definition for credit and decision servicesThe interfaces for credit and decision services are defined in the package 'com.mycompany.loanprocessing.services'.
Let us also define the decision service interface and implementation.
3.2. Securing the CXF services using WS-SecurityIn order to secure the above web services, we will use WSS4J APIs to implement security for these services. This requires callback handlers to be defined for server and the client. These handlers are invoked when ever a request is received from the client and processed at the server. For simplicity, we have hard-coded the credentials in the server handler. The credentials sent by the client should match these server credentials. Once the request is authenticated, the client can access the methods on the web service.
import java.io.IOException;
/**
Next, we need to deploy these services in a web container. CXF comes with an embedded Jetty server which we can use it to run our services and see CXF in action. The Server code looks as shown below. We create instances of credit and decision services and register them with the JaxWsServerFactoryBean. Your services is ready to be accessed with security.
You can access the WSDL for both the services in the following URLs. http://localhost:9000/services/CreditService?wsdl http://localhost:9000/services/DecisionService?wsdl Running the Server
Running the Client
'Credit service request/response':
Project Structure
Querying the CBRSummary table after running the Client 3.3. Testing Web Services using NetBeans IDENetBeans IDE provides built-in support for testing web services. Click the services tab and expand the Web Services node. Add a service group 'LoanProcessing'. Adding a Web Service Group Adding the
Credit Service and Decision Service Now, you are ready to test the web services. Right click the operation getCredit to test the credit service. This fails because it does not provide a way to pass the credentials to the web service. If you remove the security, then it should work just fine. As we are mocking up the response, you should always receive the same response.
Testing Decision Service
3.4. Testing Web Services using soapUI NetBeans pluginsoapUI is a web service testing tool which provides features such as functional/load/mock testing of web services. The soapUI NetBeans plugin is feature rich and can accomplish more testing including secured web services. We need to install the soapUI plugin from the NetBeans plugin manager. Please refer to the soapUI documentation here for details. Once the plugin installation is successful, create a soapUI web service testing project 'WebServiceTestProject'. Create soapUI project Let us provide the WSDL location for our Credit web service. We need to make sure our Server program is running for this step to work. Expand the getCredit operation in the project tree. Open 'Request 1' and copy the SOAP request for our credit service shown in previous section and submit the request to specified endpoint URL. You should see the SOAP response in the right side of the same window. This is basic testing of our secured web service. More testing can be done with soapUI. For instance, you can simulate the requests and do a load testing and measure the response times. This tool is very powerful and rock solid. Test getCredit operation Add request to TestCase Add TestSuite name Add TestCase name TestCase Options Project Structure Running the TestCase Decision service can be tested in the same manner and this is left as an exercise to the readers. You can find the SOAP request for the decision service in the previous section. There are many different ways to test your web services from soapUI plugin. Let us try testing decision service by configuring security headers in soapUI for the getRiskDecision request. The first step is to add the WSDL to our existing soapUI project. Right click the soapUI project node and select 'Add WSDL from URL' option. Specify the Decision service WSDL URL Click yes to create default requests for all the operations in the decision web service. We do not care for changing the input in the request as we hard code the response in the web service. So, we leave the input request generated by soapUI with empty optional elements. Open the 'Request 1' from the 'DecisionServiceServiceSoapBinding' tree. Select the 'Aut' tab below the request and provide the security credentials. Add WSS credentials Right click the request and select 'Add WSS Username Token' option. Select password type as 'PasswordText'. This will generate the WSS header in the the SOAP request. Add WSS Username Token to the SOAP request SOAP request with WSS headers Click 'Submit request to specified endpoint URL'. You may need to enter valid integer for the request parameters. You will find the response on the right side of the window. SOAP response from the Decision Service These are just two ways of testing our web services from soapUI plugin. You can generate the client-side stubs and then invoke the web service programatically. Our Client discussed earlier uses CXF APIs to invoke the service. CXF provides server and client side APIs to develop and test web services. It also comes with embedded Jetty server to see the CXF services in action. In real application scenario, we could deploy these services to any servlet container. Logging can be configured both on the client and server side and it can be configured programmatically or through XML files. In our application, we have programatically configured the logging as shown below. In CXFServer.java, the following code in createCreditService and createDecisionService enables logging for all the incoming SOAP requests and SOAP responses on the server side and the same interceptors are added to the CXFClient.java for logging on the client side. 'Logging configuration in CXFServer.java':
cxfEndpoint.getInInterceptors().add(new LoggingInInterceptor()); 4. References
|





































