Thursday, October 4, 2007

Phobos - Database - Apache Commons

I have been playing with data access from Phobos for some time now and was looking for a data solution with the following objectives in mind.

1) Data Pooling and Caching should be done within Phobos. This will make a Phobos application application server agnostic. There would be no extra application server configuration.

2) It should be fairly easy to use multiple databases from different vendors or projects.

3) You should be able to control the process all the way down to the connection from Phobos, but also be able to abstract access so pieces of the application do not need to know about the connection.

4) The solution should be simple. The end goal is for a basic web application. For large, enterprise applications, a custom application server specific solution can be achieved easily by an IT department.

After some research, I decided to try the Apache Commons components for database caching. I used a Java connection pooling example and converted it to a Phobos script to do essentially the same thing using the same Java objects. I have tested it and have it working with Derby and the app database included with Netbeans 6.0.

The next step would be to put the data access code in a Phobos module to abstract the database access and include some utility methods as well as error trapping.

I also would like to mention how easy this process was. I am not a Java programmer. I have read some books and written some basic programs, but would not consider myself fluent in Java by any means. However, using Javascript and Phobos, you can utilize the power of these Java objects rather easily.

*******************************************************************

Here is the Phobos script:

// This will set up our writer.
// We could do this with a dynamic script or view as well

response.setStatus(200);
response.setContentType("text/html");
writer = response.getWriter();
writer.println("<html><head><title>Hello</title></head><body>")

// Set up our connection by specifying the driver
java.lang.Class.forName("org.apache.derby.jdbc.ClientDriver");

// First, we'll need a ObjectPool that serves as the actual pool of connections.
var connectionPool = new Packages.org.apache.commons.pool.impl.GenericObjectPool();

// Set up our connect string for our database
var connectURI = 'jdbc:derby://localhost:1527/sample;user=app;password=app';

// Next, we'll create a ConnectionFactory that the pool will use to create Connections.
// We'll use the DriverManagerConnectionFactory

connectionFactory = new Packages.org.apache.commons.dbcp.DriverManagerConnectionFactory(connectURI,null);

// Now we'll create the PoolableConnectionFactory, which wraps
// the "real" Connections created by the ConnectionFactory with
// the classes that implement the pooling functionality.

poolableConnectionFactory = new Packages.org.apache.commons.dbcp.PoolableConnectionFactory(connectionFactory,connectionPool,null,null,false,true);

// Finally, we create the PoolingDriver itself
java.lang.Class.forName("org.apache.commons.dbcp.PoolingDriver");

// ...and register our pool with it.
driver = java.sql.DriverManager.getDriver("jdbc:apache:commons:dbcp:");

// Now we can just use the connect string "jdbc:apache:commons:dbcp:example"
// to access our pool of Connections.

driver.registerPool("example",connectionPool);
// This is the end of the setup code

// Now, we can use JDBC as we normally would.
// Using the connect string
// jdbc:apache:commons:dbcp:example
// The general form being:
// jdbc:apache:commons:dbcp:<name-of-pool>

var conn = null;
var stmt = null;
var rset = null;

writer.println("Creating connection.<br>");
conn = java.sql.DriverManager.getConnection("jdbc:apache:commons:dbcp:example");
writer.println("Creating statement.<br>");
var stmt = conn.createStatement();
writer.println("Executing statement.<br>");
rset = stmt.executeQuery('Select * from customer');
writer.println("Results:<br>");
var numcols = rset.getMetaData().getColumnCount();
while (rset.next()) {
for(i=1;i<=numcols;i++) {
writer.println("<br>" + rset.getString(i));
}
}
writer.println("</body></html>");
writer.flush();

*********************************************************************************

Here is the Apache Commons example:

/*
* Copyright 1999-2004 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import java.sql.DriverManager;
import java.sql.Connection;
import java.sql.Statement;
import java.sql.ResultSet;
import java.sql.SQLException;

//
// Here are the dbcp-specific classes.
// Note that they are only used in the setupDriver
// method. In normal use, your classes interact
// only with the standard JDBC API
//
import org.apache.commons.pool.ObjectPool;
import org.apache.commons.pool.impl.GenericObjectPool;
import org.apache.commons.dbcp.ConnectionFactory;
import org.apache.commons.dbcp.PoolingDriver;
import org.apache.commons.dbcp.PoolableConnectionFactory;
import org.apache.commons.dbcp.DriverManagerConnectionFactory;

//
// Here's a simple example of how to use the PoolingDriver.
// In this example, we'll construct the PoolingDriver manually,
// just to show how the pieces fit together, but you could also
// configure it using an external conifguration file in
// JOCL format (and eventually Digester).
//

//
// To compile this example, you'll want:
// * commons-pool.jar
// * commons-dbcp.jar
// in your classpath.
//
// To run this example, you'll want:
// * commons-collections.jar
// * commons-pool.jar
// * commons-dbcp.jar
// * the classes for your (underlying) JDBC driver
// in your classpath.
//
// Invoke the class using two arguments:
// * the connect string for your underlying JDBC driver
// * the query you'd like to execute
// You'll also want to ensure your underlying JDBC driver
// is registered. You can use the "jdbc.drivers"
// property to do this.
//
// For example:
// java -Djdbc.drivers=oracle.jdbc.driver.OracleDriver \
// -classpath commons-collections.jar:commons-pool.jar:commons-dbcp.jar:oracle-jdbc.jar:. \
// ManualPoolingDriverExample
// "jdbc:oracle:thin:scott/tiger@myhost:1521:mysid"
// "SELECT * FROM DUAL"
//
public class ManualPoolingDriverExample {

public static void main(String[] args) {
//
// First we load the underlying JDBC driver.
// You need this if you don't use the jdbc.drivers
// system property.
//
System.out.println("Loading underlying JDBC driver.");
try {
Class.forName("oracle.jdbc.driver.OracleDriver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
System.out.println("Done.");

//
// Then we set up and register the PoolingDriver.
// Normally this would be handled auto-magically by
// an external configuration, but in this example we'll
// do it manually.
//
System.out.println("Setting up driver.");
try {
setupDriver(args[0]);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("Done.");

//
// Now, we can use JDBC as we normally would.
// Using the connect string
// jdbc:apache:commons:dbcp:example
// The general form being:
// jdbc:apache:commons:dbcp:
//

Connection conn = null;
Statement stmt = null;
ResultSet rset = null;

try {
System.out.println("Creating connection.");
conn = DriverManager.getConnection("jdbc:apache:commons:dbcp:example");
System.out.println("Creating statement.");
stmt = conn.createStatement();
System.out.println("Executing statement.");
rset = stmt.executeQuery(args[1]);
System.out.println("Results:");
int numcols = rset.getMetaData().getColumnCount();
while(rset.next()) {
for(int i=1;i<=numcols;i++) {
System.out.print("\t" + rset.getString(i));
}
System.out.println("");
}
} catch(SQLException e) {
e.printStackTrace();
} finally {
try { rset.close(); } catch(Exception e) { }
try { stmt.close(); } catch(Exception e) { }
try { conn.close(); } catch(Exception e) { }
}

// Display some pool statistics
try {
printDriverStats();
} catch (Exception e) {
e.printStackTrace();
}

// closes the pool
try {
shutdownDriver();
} catch (Exception e) {
e.printStackTrace();
}
}

public static void setupDriver(String connectURI) throws Exception {
//
// First, we'll need a ObjectPool that serves as the
// actual pool of connections.
//
// We'll use a GenericObjectPool instance, although
// any ObjectPool implementation will suffice.
//
ObjectPool connectionPool = new GenericObjectPool(null);

//
// Next, we'll create a ConnectionFactory that the
// pool will use to create Connections.
// We'll use the DriverManagerConnectionFactory,
// using the connect string passed in the command line
// arguments.
//
ConnectionFactory connectionFactory = new DriverManagerConnectionFactory(connectURI,null);

//
// Now we'll create the PoolableConnectionFactory, which wraps
// the "real" Connections created by the ConnectionFactory with
// the classes that implement the pooling functionality.
//
PoolableConnectionFactory poolableConnectionFactory = new PoolableConnectionFactory(connectionFactory,connectionPool,null,null,false,true);

//
// Finally, we create the PoolingDriver itself...
//
Class.forName("org.apache.commons.dbcp.PoolingDriver");
PoolingDriver driver = (PoolingDriver) DriverManager.getDriver("jdbc:apache:commons:dbcp:");

//
// ...and register our pool with it.
//
driver.registerPool("example",connectionPool);

//
// Now we can just use the connect string "jdbc:apache:commons:dbcp:example"
// to access our pool of Connections.
//
}

public static void printDriverStats() throws Exception {
PoolingDriver driver = (PoolingDriver) DriverManager.getDriver("jdbc:apache:commons:dbcp:");
ObjectPool connectionPool = driver.getConnectionPool("example");

System.out.println("NumActive: " + connectionPool.getNumActive());
System.out.println("NumIdle: " + connectionPool.getNumIdle());
}

public static void shutdownDriver() throws Exception {
PoolingDriver driver = (PoolingDriver) DriverManager.getDriver("jdbc:apache:commons:dbcp:");
driver.closePool("example");
}
}

3 comments:

Timmy said...

Glad I found this site...

Phobos looks really great, yet still imho it lacks some documentation - so hopefully you'll be able to guide me into the right direction.

first of all, is there any forum/chat for phobos? I found the maling lists, but I'm looking for something more "interactive" *g*

The JPA-Generator looks wonderful, but how do I use it? I tried what the tutorial described, but no phobos-scripts were generated.

How do i connect glassfish and phobos? is there a way to get the phobos system application running inside glassfish (so the database-page of the system-application isn't empty any more)?

Can I call sessionbeans from phobos?

Are there any other resources/docs/communities/etc. about phobos except for phobos.dev.java.net?

Thanks!

Tony Zakula said...

Hi Timmy,

Right now, as far as I know, the email lists are the only Phobos community. That being said, sometime by the end of the year, I do hope to launch a separate website devoted to Phobos running on Phobos with a forum. It is just a matter of time to complete it and work on it.

Tony Zakula said...

Phobos runs as a web app on any app server. If you have problems with the JPA generator, email the users list at the java.net web site. Roberto and Ludo always answer questions quite quickly.