Archive for the ‘Java Programing’ Category

Calling Java in PL/SQL

Sunday, March 6th, 2011

I recently had a situation where I needed to execute the same exact business logic in PL/SQL as in a Java based service. The business logic had already been implemented in Java. The logic took a person’s full name and parsed it out into it’s components, first name, middle name, last name, salutation (Mr., Mrs., Ms., Dr., etc.) and suffix, (Sr., Jr., III, IV, Esq., etc.).

I knew that I could implement a PL/SQL function that was written in Java and import it into the database. However an example was hard to find. I found examples where the function returned a primitive or a String, but nothing that returned a more complex data structure.

I’ve done similar things with Java calling a Stored Procedure that accepted a PL/SQL structure or table. To do this you define a PL/SQL type and then run JPublisher to create classes that represented the type in Java. The type could then be used to bind to JDBC SQL statements that passed Objects. I thought that perhaps the reverse would work as well.

The basic steps are as follows:

  1. Identify the existing Java code that will be needed.
  2. Create the PL/SQL Interface.
  3. Create Java code that implements the PL/SQL Interface.
  4. Define the PL/SQL.
  5. Load Java code into the Database.

Existing Java Code

So let’s start with the Java code that implemented the business logic. I’m not going to reproduce the name parsing code here. I’ll simply define the interface.

package com.company.nameparse;
public class NameParser {

	public static NameComponentsDTO parse(String rawName) { … }

}

The DTO Object

package com.company.nameparse;
public class NameComponentsDTO implements Serializable {
	private String salutation;
	private String firstName;
	private String middleInitialOrName;
	private String lastName;
	private String suffix;
	private String originalNameString;

// Getters and Setters for each of the private members.
// …
}

You need to be careful as to what version of the JRE is running in your version of Oracle. The target Oracle version I was working with is 10g (10.0.2). This version used the 1.4.2 JRE. So the Name Parsing class had to be stripped down of things like Annotations, Prototypes on collections and even some newer methods on java.lang.String. I believe that 11g uses the JRE 1.5. So any 1.6 features will not be available.

Well maybe you can upgrade the JRE? Well I asked that question of Tom Kyte on AskTom.Oracle.com. His reply was that you couldn’t change the JRE. That Oracle uses it internally and it needs to have known behavior.

PL/SQL Interface

Now lets define what we want the PL/SQL Function to look like. I like to define things in Packages. So here is the package definition:

CREATE OR REPLACE PACKAGE NAMEPARSEPACKAGE AS
	function nameparse( fullname VARCHAR2 ) return NAME_COMPONENT_STRUCT;
END NAMEPARSEPACKAGE;
/

Notice the function returns a PL/SQL Structure. Here the definition of the Structure.

CREATE OR REPLACE TYPE NAME_COMPONENT_STRUCT AS object (
   firstName varchar2(40),
   lastname varchar2(40),
   middle varchar2(40),
   salutation varchar2(40),
   suffix varchar2(40),
   originalName varchar2(80)
);
/

Java Implementation

Now for creating the Java code that implements this PL/SQL interface. We need to create a Java class that implements the Structure that is returned by the function nameparse. The easiest way to do this is to use the JPublish command line utility to create a Java class from the Struct. The JPublish command line is included with the Oracle Companion CD.

First you need to make sure you define your JAVA_HOME. This JAVA_HOME must point to a JDK, not a JRE.

Then you need to make sure you have your CLASSPATH set properly for the JPublish command line.

set CLASSPATH=%ORACLE_HOME%\sqlj\lib\translator.jar;%ORACLE_HOME%\sqlj\lib\runtime12.jar;%ORACLE_HOME%\jdbc\lib\ojdbc14.jar;%ORACLE_HOME%\sqlj\lib\dbwsa.jar;

Then you can run JPublish. You need to pass the user/id and TNSName as well as the SQL type you want to create. You can optionally provide a Java package.

jpub -user=scott/tiger@DEV -numbertypes=oracle -builtintypes=oracle -sql=NAME_COMPONENT_STRUCT -package=com.company.nameparser.oracle

Your going to get 2 classes NAME_COMPONENT_STRUCT and NAME_COMPONENT_STRUCTRef. In these classes, there are constants that define the schema name and Oracle type name.


public static final String _SQL_NAME = "SCOTT.NAME_COMPONENT_STRUCT";
public static final String _SQL_BASETYPE = "SCOTT.NAME_COMPONENT_STRUCT";

You might want to hand modify these constants remove the schema name so this Java this code can call on schemas other than the schema you ran Jpublisher with.

The code to wrap the original Java code and return the Struct is very simple. The method that implements the function must be public and static.


package com.company.nameparser.oracle;

import java.sql.SQLException;

import oracle.sql.CHAR;

import com. company.nameparse.NameComponentsDTO;
import com. company.nameparse.NameParser;

public class NameParserOracleWrapper {
	public static NAME_COMPONENT_STRUCT parse(String rawName) {
		NAME_COMPONENT_STRUCT struct = new NAME_COMPONENT_STRUCT();

		NameComponentsDTO dto = NameParser.parse(rawName);

		try {
			struct.setFirstname(new CHAR(dto.getFirstName(),
					CHAR.DEFAULT_CHARSET));
			struct.setLastname(new CHAR(dto.getLastName(), CHAR.DEFAULT_CHARSET));
			struct.setMiddle(new CHAR(dto.getMiddleInitialOrName(),
					CHAR.DEFAULT_CHARSET));
			struct.setSalutation(new CHAR(dto.getSalutation(),
					CHAR.DEFAULT_CHARSET));
			struct.setSuffix(new CHAR(dto.getSuffix(), CHAR.DEFAULT_CHARSET));
			struct.setOriginalname(new CHAR(dto.getOriginalNameString(),
					CHAR.DEFAULT_CHARSET));
		} catch (SQLException e) {
		}
		return struct;
	}
}


PL/SQL Implementation

Now to implement the PL/SQL Function.

CREATE OR REPLACE PACKAGE BODY NAMEPARSEPACKAGE IS
  function nameparse(fullname VARCHAR2)
  return NAME_COMPONENT_STRUCT as
  language java
    name 'com.company.nameparser.oracle.NameParserOracleWrapper.parse(java.lang.String) return com.company.nameparser.oracle.NAME_COMPONENT_STRUCT';
END NAMEPARSEPACKAGE;
/


Load the Java Code

The last thing to be done is to load the Java code into Oracle. You need to make sure you have all of the classes necessary loaded. There is a Maven plugin comes in handy in situations like this. It is called the Shade plugin. It pulls in all the classes from all of the dependencies for a Maven project. Below is the pom.xml file that implements this. Notice that Oracle is a provided dependency. The Shade plugin will not include classes from this project.

<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.company.nameparse</groupId>
	<artifactId>nameparse-oracle</artifactId>
	<version>1.0.0-SNAPSHOT</version>
	<packaging>jar</packaging>
	<name>nameparse-oracle</name>
	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<java.version>1.4</java.version>
	</properties>
	<build>
		<plugins>
			<plugin>
				<artifactId>maven-compiler-plugin</artifactId>
				<configuration>
					<source>${java.version}</source>
					<target>${java.version}</target>
				</configuration>
			</plugin>
 			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-shade-plugin</artifactId>
				<executions>
					<execution>
						<phase>package</phase>
						<goals>
							<goal>shade</goal>
						</goals>
					</execution>
				</executions>
			</plugin>
		</plugins>
	</build>
	<dependencies>
		<dependency>
			<groupId>ojdbc</groupId>
			<artifactId>ojdbc</artifactId>
			<version>14</version>
			<scope>provided</scope>
		</dependency>
		<dependency>
			<groupId>com.company.nameparse</groupId>
			<artifactId>nameparse</artifactId>
			<version>1.0.0-SNAPSHOT</version>
		</dependency>
	</dependencies>
</project>

When you build this project you will get a Jar file nameparse-oracle-1.0.0-SNAPSHOT.jar

Now that you have the jar file you need to load it into Oracle. Included in the Oracle client is the loadjava. You can load the entire jar file with this tool.


loadjava -user=scott/tiger@DEV nameparse-oracle-1.0.0-SNAPSHOT.jar

You should not be able to run your PL/SQL function.


declare
   dto NAME_COMPONENT_STRUCT;
begin
   DBMS_OUTPUT.ENABLE;
   dto := NAMEPARSEPACKAGE.NAMEPARSE('Mr. Joseph E. Kampf Jr.');
   DBMS_OUTPUT.PUT_LINE(dto.originalName);
   DBMS_OUTPUT.PUT_LINE(dto.saluation);
   DBMS_OUTPUT.PUT_LINE(dto.firstname);
   DBMS_OUTPUT.PUT_LINE(dto.middle);
   DBMS_OUTPUT.PUT_LINE(dto.lastname);
   DBMS_OUTPUT.PUT_LINE(dto.suffix);
end;

You should see the following DBMS_OUTPUT:


Mr. Joseph E. Kampf Jr.
Mr.
Joseph
E.
Kampf
Jr.

Mock Data Testing with Spring and HSQLDB part 2

Tuesday, December 22nd, 2009

This is my 2nd post about Mock Data Testing using Spring and HSQLDB.  If you missed my first post you can find it here.

I know that this concept is not really bleeding edge. The whole purpose of these two posts are to share an approach that I have used in the past to help me with my Test Driven Continuous development. I have not tried it with Hibernate or any other ORM. I have only used it with Spring JDBC Template and straight JDBC programing.

Architecture
First let me explain the requirements for your architecture to be able to support this type of mock tests.  A good architecture allows components to be unit tested.  If you need an entire environment stack to be able to unit test your code then your architecture is just a big mess.  Business logic layer needs to be separate from your data access.  Your interface layer needs to be seperate from your business logic layer.   This is true if you are doing a MVC web application or even a back end SOA implementation.  I’m not just talking about encapsulating these different layers in different classes, but also have these layers be loosely coupled.

  1. Your Database queries should be externalized.  Do not hard code database queries or stored procedure calls.  I know that Hibernate allows you to annotate your code.  But can be a barrier to having your database access be portable.    However, you can get around this.  Configuration of your Database access should be loaded at run time.  Spring Beans do a nice job of handling this.  The Bean XML file gives you a great way to capture configuration information and load it at run time.  The Application Configuration allows you to load these files external to your actual business logic.
  2. Your modules should be able to be run independent of any other infrastructure.  Make your modules Pojos.  Even if you plan to expose them as EJBs, MDBs or Web Services.  You can code your business logic as a Pojo.  This will allow simple clean unit tests that can run right out of the command line with no other application running.

Unit Test Implementation
So lets start with the Java code.. Below is an example of a simple Pojo that does some sort of business logic. (I left out the get/setters and import statements.)

public class BusinessPojo {

    private String sql;

    private DataSource dataSource;
    private JdbcTemplate jdbcTemplate;

    public String doSomething(String id) {
	SqlRowSet rowset = getJdbcTemplate().queryForRowSet(getSql(), new Object[] { id });

	return rowset.getString("Field1");
    }
}

This example defines 2 bean properties. The first is the SQL string that is executed, the second is the JDBC Datasource that the SQL is being executed against. The Pojo also uses the JDBC Template from the Spring framework. This is only to hide a lot of the messy JDBC code.

This is a very simple example. But it can easily be extended to take any number of input parameters and any number of returned values.
Also, this approach can be used with any framework that you can pass a JDBC Datasource and a SQL statement to.

Now let’s look at our test class:

package com.josephkampf.example;

import java.io.InputStream;
import java.net.URL;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Statement;

import org.apache.commons.io.IOUtils;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.testng.Assert;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;

/**
 * Unit test for the BusinessPojo.
 *

 *
 * @author Joseph Kampf (me@josephkampf.com)
 */
public class BusinessPojoTest {

    private ApplicationContext ctx;

    @BeforeClass
    public void setup() throws Exception {
	setUpHSQLDB("BusinessPojoTest", "/TestBusinessPojoSchemaSetup.sql");
	setUpHSQLDB("BusinessPojoTest", "/TestBusinessPojoDataSetup.sql");
	this.ctx = new ClassPathXmlApplicationContext(
		"spring-TestBusinessPojo.xml");
    }

    /**
     * Sets up the in-memory HSQLDB using the database name and initial SQL
     * script to populate the database.
     */
    public void setUpHSQLDB(String databaseName, String initSqlScript)
	    throws Exception {

	Class.forName("org.hsqldb.jdbcDriver");

	final URL url = this.getClass().getResource(initSqlScript);
	final String sql = IOUtils.toString((InputStream) url.getContent());

	final Connection connection = DriverManager.getConnection(
		"jdbc:hsqldb:mem:" + databaseName, "sa", "");
	final Statement s = connection.createStatement();
	s.execute(sql);

	s.close();
	connection.close();

    }

    @Test
    public void testProcess() throws Exception {
	BusinessPojo pojo = (BusinessPojo) this.ctx.getBean("pojo");
	Assert.assertEquals(pojo.doSomething("ID1"), "Expected");
    }
}

I use TestNG. I Like TestNG because it is annotation driven. Let’s take a look at what is going on in this test outside of actually testing the Pojo.

Take a look at the setUpHSQLDB method.  This method takes a name and looks up via a JDBC URL an in memory HSQLDB instance.  If the instance does not exist, it is created.  So we can always reference this instance again by using the same JDBC URL.  We will see this later when we create our BusinessPojo Test Spring context file.  The method also executes some SQL scripts in the in Memory Database.  The first sets up the table structures.  The second addes the data.  I’m not going to go into the details of what is in these scripts.  I’ll leave some examples as an attachment to this posting.  I refer you to the HSQLDB online documentation for details.  But what is happening here is the database is being set up in a known state in the same JVM that our unit test is running in.  No extra processes are required.

Note that we could take the setUpHSQLDB method and create a Test Fixture.  This is actually how I have implemented it in my code.  However I leave it all in one class for convenience.  Another option would be to add the setUpHSQLDB method in your base class. (Not the way I would go.)

Now lets connect our Pojo to the HSQLDB instance we have created in our Unit Test.  Below is the Spring Configuration that is loaded by the Unit Test.

spring config:
<beans>
 <bean id="pojo" class="com.josephkampf.example.BusinessPojo">
   <property name="dataSource">
     <ref bean="dbcpDataSource" />
   </property>
   <property name="sql">
     <value>SELECT f1 as Field1 from sample_table where id = ?</value>
   </property>
 </bean>

 <bean id="dbcpDataSource" destroy-method="close">
   <property name="driverClassName" value="org.hsqldb.jdbcDriver" />
   <property name="url" value="jdbc:hsqldb:mem:BusinessPojoTest" />
   <property name="username" value="sa" />
   <property name="password" value="" />
 </bean>
</beans>

Notice the url of the DataSource.  It has the form of jdbc:hsqldb:mem:BusinessPojoTest.  Notice that this is the same JDBC URL we used in our Unit Test to load up the database.  So when the Spring template obtains a connection, it will be obtaining a connection to the in memory HSQLDB instance.

Non-Unit Test Implementation
Now when you actually go to a non-unit test environment, you will need a diffrent Spring Application Context file.  Below is an example of an Application Context that defines this Pojo to connect to an Oracle Database. This is an example of what you might use in an environment that is beyond Unit test.(Integration, QA, Stage, Production, etc.)

<beans>
  <bean id="processor" class="com.josephkampf.example.BusinessPojo">
    <property name="dataSource">
      <ref bean="oracleDataSource" />
    </property>
    <property name="sql">
      <value>SELECT oracleField1 as Field1 from oracle_sample_table where id = ?</value>
    </property>
  </bean>

  <bean id="oracleDataSource">
    <!-- Insert Oracle datasource configuration here. -->
  </bean>
</beans>

Notice that the SQL has changed to conform to the Oracle database. All that matters is that both SQL values have the same structure for the Select clause have the same fields by name and the Where clause have the same number and order of the bind variables.

Mock Data Testing is an important step to delivering on a Test driven and continuous development effort. You really should not need a running external database to be able to run your Unit Tests. Your Unit Tests should be using a known set of data so that you can do meaningful Assertions in your Unit Test code. You also need to be able to spend less time coding that Mock Data. Using SQL to define this test data is a more intuitive and faster mechanism than other solutions. Spring allows you to separate your concerns so that you can create testable units.

Mock Data Testing with Spring and HSQLDB part 1

Tuesday, November 24th, 2009

I wanted to share some interesting things that myself and my colleague Emil “Bud” Lefkof have done recently in the realm of Mock Testing using Spring and HSQLDB.

I expect this to be the first of threetwo posts on this topic. First I’ll explain the problem that this approach solves. In the second post I’ll explain the approach. In the last post I’ll discuss some additional concerns such as having to deal with Stored Procedures.

For an explanation of Mock Object Testing see Wikipedia who actually does a nice job of explaining what it is all about.

The reason that Mock Testing is important is that Unit tests should be repeatable and automated. Developers should be able to check out code and unit tests from a source control system and be able to run a script or command that builds and tests. Unit tests shouldn’t need to rely on databases, or other external dependencies. Plus the state of a database can become unknown. When doing test driven development you need to be able to run a test over and over again. You can’t worry about the data already being in the database.

I was first introduced to the concept of Mock Testing by my old friend and colleague Howard Spector during my time at FirstUSA/Bank One Card Services.  It was my first experience with Test Driven Development.  Back in those days we had our own Data Services Framework.  This was a framework that provided an interface that was an abstraction over Relational Database.

Because this framework was an interface we could use test implementations that would return mock data objects.  However it always felt clumsy.  Creating the test data was a manual time consuming task.  There were 2 ways to create the Mock Data objects. 

The first way is to hand code a factory that would set the values of the properties of data objects.

public List getDataPojos(String arg){
  List returnList = new LinkedList();

  DataPojo dataPojo1 = new DataPojo();
  dataPojo1.setString1("String 11");
  dataPojo1.setString2("String 12");
  returnList.add(dataPojo1);

  DataPojo dataPojo2 = new DataPojo();
  dataPojo2.setString1("String 21");
  dataPojo2.setString2("String 22");
  returnList.add(dataPojo2);

  return returnList;
}

Another option that has a little bit more flexibility is to parse a properties file or XML file.

With either approach there needs to be some sort of translation from the source to the Mock Data object that is essentially throw away code. This will cause the developer to spend more time writing code to create Mock Data objects than writing the actual tests or even the actual code.

In my next post I’ll detail how we can solve the issue of spending too much time creating Mock Data objects.