Showing posts with label ruby. Show all posts
Showing posts with label ruby. Show all posts

Wednesday, March 17, 2010

JDBC/JNDI Pooling with a JRuby/Rails app

In working on the transition of this rails app over to the JRuby/Glassfish camp, one of the things I needed to take advantage of was using jdbc/jndi database pooling and configurations.

Of course, using rails, the application was using ActiveRecord for it's DB/ORB interactions, and in researching, the steps needed to get this setup were:
  1. Create the JDBC connection pool
  2. Create the resource with a JNDI name
  3. Update the database.yml
  4. Configure ActiveRecord for disconnects
I also needed to download and put the postgresql jdbc driver in place ($domain/lib/ext/ - using the JDBC version 4 driver).

In looking at the jdbc templates included with Glassfish (glassfish/lib/install/templates/resources/jdbc), I attempted to use the template for my driver (postgresql_type4_datasource.xml). This was causing issues using the 'url' property, so ended up using asadmin to create, using serverName and databaseName as properties.

Starting up the asadmin interactive utility:


asadmin> create-jdbc-connection-pool
--datasourceclassname org.postgresql.ds.PGConnectionPoolDataSource
--restype javax.sql.ConnectionPoolDataSource
--property user=XXX:password=XXX:serverName=localhost:databaseName=extension_dev extensionDevPool

Command create-jdbc-connection-pool executed successfully.
asadmin> create-jdbc-resource --connectionpoolid extensionDevPool jndiExtensionDev

Command create-jdbc-resource executed successfully.
asadmin> list-jdbc-connection-pools
__TimerPool
DerbyPool
extensionDevPool

Command list-jdbc-connection-pools executed successfully.

asadmin> list-jdbc-resources
jdbc/__TimerPool
jdbc/__default
jndiExtensionDev

Command list-jdbc-resources executed successfully.

asadmin> ping-connection-pool extensionDevPool

Command ping-connection-pool executed successfully.
I created separate pools/resources for dev/test/production - which will be commented accordingly for now in the domain.xml file.

Next came setting up the database.yml file to use jndi instead of the regular ActiveRecord drivers. There are some excellent resources on the web for getting this done, but had to do some digging to get this correct.

One of the issues in setting this up correctly was, that especially during development and testing, we are using the jruby console (jruby -S script/console) to create and activate objects and events, which in turn looks at the database.yml file to get its connection.

This was hurting me because once set to use jndi, none of these settings were setup correctly and I would continue to get connection issues as well as the jms missing class issues.

So, to fix, in the database.yml file, we not only test for the RAILS_ENV to be java (or the JRUBY_VERSION to be set), but we needed to test to see if we were in a servlet context (ala Glassfish) as well as to use jndi based connections or regular connections.

So, our database.yml file ended up looking like:
defaults: &defaults
<% jdbc = defined?(JRUBY_VERSION) ? 'jdbc' : '' %>
<% if defined?($servlet_context) %>
adapter: jdbc
driver: org.postgresql.Driver
<% else %>
adapter: <%= jdbc %>postgresql
<% end %>
username: xxx
password: xxx
host: localhost

development:
<% if defined?($servlet_context) %>
jndi: jndiExtensionDev
<% end %>
database: extension_dev
<<: *defaults

test:
<% if defined?($servlet_context) %>
jndi: jndiExtensionTest
<% end %>
database: extension_test
<<: *defaults

production:
<% if defined?($servlet_context) %>
jndi: jndiExtensionProd
<% end %>
database: extension_prod
<<: *defaults

This enabled us to access the db in our app as well as run the console and connect correctly.

We now need to configure ActiveRecord to disconnect after every query - which was not needed before since we are now using JDBC to manage the connection persistence. (See resources below for links to some of the sites that were used to research all of this).
# config/initializers/close_connections.rb
if defined?($servlet_context)
require 'action_controller/dispatcher'

ActionController::Dispatcher.after_dispatch do
ActiveRecord::Base.clear_active_connections!
end
end
Some of the excellent resources I used:

RoR App to run under JRuby

After deciding on the platform, one of the big things to get accomplished was getting the current application to run inside a JRuby container inside Glassfish.

This was a bit challenging at first, and it still isn't all quite there, but the main core of the app is now running in there, with a few changes.

We are not deploying via a war file (yet) - since we are just building this, but deploying as a directory (for development) from my git workspace to build up the configuration steps.

There were quite a few challenges (and more to come) - some of which were based on the gem compatibilities between ruby and jruby - more notably libxml and libxslt - which rely on native libraries.

Before I got here, this was tackled a little bit by another engineer here, Rich, who created an xml_lib.rb that wrapped what we needed via the libxml and libxslt libraries to utilize their java counterparts - so that was a big boost for this process.

We will probably replace those with more robust ones if we need in the future, but this library works great for what we are using it for at the moment. There is a port of libxml-ruby called libxml-jruby, written by Dylan Vaughn, which will do what we need - and we will probably pull out the XSLT functions that Rich wrote and separate the lib this way.

A big part of this was getting the correct gems installed and being used for jruby, and adjustments in the configurations for the current app to separate what to load if running under jruby as opposed to ruby.

Example - in 'conf/environment.rb' - the config gems were separated out:
if RUBY_PLATFORM =~ /java/
config.gem 'jdbc-postgres', :lib => 'jdbc/postgres'
config.gem 'activerecord-jdbc-adapter', :lib => 'jdbc_adapter'
config.gem 'activerecord-jdbcpostgresql-adapter',
:lib => 'active_record/connection_adapters/jdbcpostgresql_adapter'
else
config.gem 'pg', :version => '0.8.0'
config.gem 'libxml-ruby', :lib => 'xml/libxml', :version => '1.1.3'
config.gem 'libxslt-ruby', :lib => 'libxslt', :version => '0.9.2'
end
After installing and setting up Glassfish with a new domain and installing jruby, needed to make the jruby container available to Glassfish:
% asadmin create-domain --adminport 4848 extension
...
...
Command create-domain executed successfully.

% asadmin configure-jruby-container --jruby-home=/usr/local/jruby
and then deployed my current app (being in the parent dir of the rails app directory)
% asadmin deploy --property jruby.rackEnv=development core/

Starting my domain here, I was able to access the app successfully via the context of the app name (http://localhost:8080/core/). Adding 'context-root="/"' to the <application ... section allowed me to access without adding '/core/' to my URL.

Notice the setting of:

--property jruby.rackEnv=development

This is basically the equivalent of setting RAILS_ENV=development in your environment.

Next step was getting the db to work with jdbc/jndi pooling.

*Note: One issue I was having was the complaining of missing the class javax.jms.MessageListener:
/usr/local/jruby/lib/ruby/site_ruby/shared/builtin/javasupport/core_ext/object.rb:37:
in `get_proxy_or_package_under_package':
NameError: cannot load Java class javax.jms.MessageListener
Following the directions on http://wiki.glassfish.java.net/Wiki.jsp?page=OpenMQJRuby, I created and moved the appropriate jms/imq jar files to my domain/lib/ext directory and these are no longer an issue. I will have to see why this was when I work with the MQ issues (and converting from ActiveMQ to OpenMQ).
  1. .../mq/lib/jms.jar
  2. .../mq/lib/imq.jar
  3. .../mq/lib/imqjmsra.jar
imqjmsra.jar is created by extracting it from imqjmsra.rar:
jar xvf imqjmsra.rar imqjmsra.jar

Move these into the domain/lib/ext directory. These will also need to be included in your classpath when using the jruby console.