Subject Problems with Firebird and JBoss
Author zanardibaffo
I'm experimenting troubles using jboss 3.0.0 and firebird database
with JCA-JDBC Driver "JayBird" version 1.0.0.

I wanted to test an entity bean with bean-managed persistance (source
code
follows). The client should be able to create, find and remove records
on the
DB table 'CLIENTI' using its remote interface.

The problem is that the client can create and find records correctly,
but
receives an error when trying to remove a record (client calls remove
( )
method on the remote interface of the entity bean):

java.rmi.ServerException: RemoteException occurred in server thread;
nested
exception is:
java.rmi.ServerException: EJBException:; nested exception is:
javax.ejb.EJBException: ejbRemove: Attempt to start local
transaction
while xa transaction is active!


I tried the same code using MySql and it works fine, so I don't know
whether
it is a driver configuration problem or a mistake in my code.


It seems to be a problem related to how JBoss manages transactions;
I saw that firebird-service.xml enables XA transactions, so I tried to
turn XA
transactions off and configured the driver to use local transactions,
but in
this case I got a different error when the client calls the 'remove(
)'
method:


java.rmi.ServerException: RemoteException occurred in server thread;
nested
exception is:
java.rmi.ServerException: ejbRemove: You cannot getAutomcommit
on an
unassociated closed connection.; nested exception is:
javax.ejb.EJBException: ejbRemove: You cannot getAutomcommit
on an
unassociated closed connection.

In fact, I discovered that the connection 'con' is closed when the
bean enters
the method 'ejbRemove( )'.



Another thing, Can anyone tell me what version of
Firebird/JBoss/Jaybird driver certainly work without problem, maybe
with a firebird-service.xml configuration file?
Thanks to anyone who wants to help me!



I include the source code of the entity bean and and client, the
firebird-service.xml configuration file. Please tell me if I'm
forgetting
anything.

// HelloEntityBean.java

package test.entity;
import java.sql.*;
import javax.sql.*;
import java.util.*;
import java.math.*;
import javax.ejb.*;
import javax.naming.*;

import javax.rmi.*;

/**
* The Entity bean represents a TestEntity with BMP
*
* @ejb:bean name="test/HelloEntity"
* display-name="TestEntity working on projects to support
clients
(BMP)"
* type="BMP"
* jndi-name="ejb/test/HelloEntity"
*
* @ejb:env-entry name="DataSourceName"
* value="java:/FirebirdDS"
*
* @ejb:transaction type="Required"
*
*
* @ejb:finder signature="java.util.Collection findAll()"
**/

public abstract class HelloEntityBean implements EntityBean {

private String cod;
private String lastName;
private String firstName;
private EntityContext context;
private Connection con;
private String dbName = "java:/FirebirdDS";
/**
* @return Returns the firstname value
* bean.
*
* @ejb:interface-method view-type="remote"
**/

public String getFirstName() {

return firstName;
}
/**
* @return Returns the lastname
* bean.
*
* @ejb:interface-method view-type="remote"
**/

public String getLastName() {

return lastName;
}
/**
* @return Returns the lastname
* bean.
*
* @ejb:create-method view-type="remote"
**/



public String ejbCreate(String cod, String lastName, String
firstName){

try {
insertRow(cod, lastName, firstName);
} catch (Exception ex) {
throw new EJBException("ejbCreate: " +
ex.getMessage());
}

this.cod = cod;
this.firstName = firstName;
this.lastName = lastName;
return cod;
}

public String ejbFindByPrimaryKey(String primaryKey)
throws FinderException {

boolean result;

try {
result = selectByPrimaryKey(primaryKey);
} catch (Exception ex) {
throw new EJBException("ejbFindByPrimaryKey: " +
ex.getMessage());
}
if (result) {
return primaryKey;
}
else {
throw new ObjectNotFoundException
("Row for cod " + primaryKey + " not found.");
}
}

public Collection ejbFindByLastName(String lastName)
throws FinderException {

Collection result;

try {
result = selectByLastName(lastName);
} catch (Exception ex) {
throw new EJBException("ejbFindByLastName " +
ex.getMessage());
}
return result;
}

public void ejbRemove() throws
RemoveException{

try {
deleteRow(cod);
} catch (Exception ex) {
throw new EJBException("ejbRemove: " +
ex.getMessage());
}
}

public void setEntityContext(EntityContext context) {

this.context = context;
try {
makeConnection();
} catch (Exception ex) {
throw new EJBException("Unable to connect to database. " +
ex.getMessage());
}
}

public void unsetEntityContext() {

try {
con.close();
} catch (SQLException ex) {
throw new EJBException("unsetEntityContext: " +
ex.getMessage());
}
}

public void ejbActivate() {

cod = (String)context.getPrimaryKey();
}

public void ejbPassivate() {

cod = null;
}

public void ejbLoad() {

try {
loadRow();
} catch (Exception ex) {
throw new EJBException("ejbLoad: " +
ex.getMessage());
}
}

public void ejbStore() {

try {
storeRow();
} catch (Exception ex) {
throw new EJBException("ejbStore: " +
ex.getMessage());
}
}


public void ejbPostCreate(String cod, String lastName,
String firstName) { }


/*********************** Database Routines *************************/

private void makeConnection() throws NamingException, SQLException
{

InitialContext ic = new InitialContext();
DataSource ds = (DataSource) ic.lookup(dbName);
con = ds.getConnection();
}

private void insertRow (String cod, String lastName, String
firstName)
throws SQLException {

String insertStatement =
"insert into clienti values ( ? , ? , ?)";
PreparedStatement prepStmt =
con.prepareStatement(insertStatement);

prepStmt.setString(1, cod);
prepStmt.setString(2, lastName);
prepStmt.setString(3, firstName);
prepStmt.executeUpdate();
prepStmt.close();
}

private void deleteRow(String cod) throws SQLException {

String deleteStatement =
"delete from clienti where cod = ? ";
PreparedStatement prepStmt =
con.prepareStatement(deleteStatement);

prepStmt.setString(1, cod);
prepStmt.executeUpdate();
prepStmt.close();
}

private boolean selectByPrimaryKey(String primaryKey)
throws SQLException {

String selectStatement =
"select cod " +
"from clienti where cod = ?";
PreparedStatement prepStmt =
con.prepareStatement(selectStatement);
prepStmt.setString(1, primaryKey);
ResultSet rs = prepStmt.executeQuery();
boolean result = rs.next();
prepStmt.close();
return result;
}

private Collection selectByLastName(String lastName)
throws SQLException {

String selectStatement =
"select cod " +
"from clienti where lastname = ? ";
PreparedStatement prepStmt =
con.prepareStatement(selectStatement);

prepStmt.setString(1, lastName);
ResultSet rs = prepStmt.executeQuery();
ArrayList a = new ArrayList();

while (rs.next()) {
String cod = rs.getString(1);
a.add(cod);
}

prepStmt.close();
return a;
}

private void loadRow() throws SQLException {

String selectStatement = "SELECT lastname, firstname FROM
clienti WHERE
cod = ?";
PreparedStatement prepStmt =
con.prepareStatement(selectStatement);
prepStmt.setString(1, this.cod);

ResultSet rs = prepStmt.executeQuery();

if (rs.next()) {
this.lastName = rs.getString(1);
this.firstName = rs.getString(2);
prepStmt.close();
}
else {
prepStmt.close();
throw new NoSuchEntityException("Row for cod " + cod +
" not found in database.");
}
}

private void storeRow() throws SQLException {

String updateStatement =
"update clienti set lastname = ? ," +
"firstname = ?" +
"where cod = ?";
PreparedStatement prepStmt =
con.prepareStatement(updateStatement);

prepStmt.setString(1, lastName);
prepStmt.setString(2, firstName);
prepStmt.setString(3, cod);
int rowCount = prepStmt.executeUpdate();
prepStmt.close();

if (rowCount == 0) {
throw new EJBException("Storing row for cod " + cod + "
failed.");
}
}

}

//HelloClient.java

package test.client;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.rmi.PortableRemoteObject;
import java.util.Hashtable;
import test.interfaces.HelloEntity;
import test.interfaces.HelloEntityHome;

public class HelloClient {

public static void main(String[] args){
Hashtable env = new Hashtable();
env.put(Context.INITIAL_CONTEXT_FACTORY,
"org.jnp.interfaces.NamingContextFactory");
env.put(Context.PROVIDER_URL, "localhost:1099");
env.put("java.naming.factory.url.pkgs",
"org.jboss.naming:org.jnp.interfaces");
try {
InitialContext lContext = new InitialContext(env);

HelloEntityHome lHome = (HelloEntityHome) lContext.lookup(
"ejb/test/HelloEntity" );
lHome.create( "554433" , "jdfsdfkh" , "jsdfsdfj" );
HelloEntity lSession = lHome.findByPrimaryKey("554433");
System.out.println( "Message is: " + lSession.getLastName()
);
System.out.println( "Message is: " + lSession.getFirstName()
);
lSession.remove();
} catch( Exception e ){
e.printStackTrace();
}
}

}

// firebird-service.xml

<?xml version="1.0" encoding="UTF-8"?>

<!--
====================================================================
-->
<!-- New ConnectionManager setup for firebird dbs using jca-jdbc xa
driver-->
<!-- Build jmx-api (build/build.sh all) and view for config
documentation
-->
<!--
====================================================================
-->

<service>

<!--FBManager can be used to create and drop databases.
Drop is especially useful during testing, since it
assures a clean start next time. -->
<mbean code="org.firebirdsql.management.FBManager"
name="jboss.jca:service=FirebirdManager">
<attribute
name="FileName">/opt/interbase/examples/jbossdb.gdb</attribute>
<!-- The Server attribute was until very recently called URL. The
default is
localhost,you may wish to drop this attribute if not needed -->
<!-- attribute name="Server">localhost</attribute -->
<!-- attribute name="Port">3050</attribute -->
<attribute name="UserName">sysdba</attribute>
<attribute name="Password">masterkey</attribute>
<attribute name="CreateOnStart">true</attribute>
<attribute name="DropOnStop">false</attribute>
</mbean>

<mbean
code="org.jboss.resource.connectionmanager.XATxConnectionManager"
name="jboss.jca:service=XaTxCM,name=FirebirdDS">

<depends optional-attribute-name="ManagedConnectionFactoryName">
<mbean code="org.jboss.resource.connectionma
nager.RARDeployment"
name="jboss.jca:service=XaTxDS,name=FirebirdDS">
<!--more hack-->
<depends
optional-attribute-name="OldRarDeployment">jboss.j
ca:service=RARDeployment,name=Firebird

Database Connector</depends>
<attribute name="ManagedConnectionFactoryProperties">
<properties>
<config-property name="Database"
type="java.lang.String">localhost/3050:/opt/interb
ase/examples/jbossdb.gdb</config-property>
<!-- config-property name="TransactionIsolationName"
type="java.lang.String">TRANSACTION_REPEATABLE_READ</config-property
-->
<!-- config-property name="Encoding"
type="java.lang.String">UNICODE_FSS</config-property -->
<!-- config-property name="BlobBufferLength"
type="int">4096</config-property -->

<!-- These default values for user/pw are not necessary if you are
using
container managed security or application managed security
(i.e. you ask for connections with a user/pw) -->
<config-property name="UserName"
type="java.lang.String">Sysdba</config-property>
<config-property name="Password"
type="java.lang.String">masterkey</config-property>
</properties>
</attribute>
<attribute name="JndiName">FirebirdDS</attribute>
</mbean>

</depends>
<depends optional-attribute-name="ManagedConnectionPool">
<mbean
code="org.jboss.resource.connectionmanager.JBossMa
nagedConnectionPool"
name="jboss.jca:service=XaTxPool,name=FirebirdDS">

<attribute name="MinSize">0</attribute>
<attribute name="MaxSize">50</attribute>
<attribute name="BlockingTimeoutMillis">5000</attribute>
<attribute name="IdleTimeoutMinutes">15</attribute>
<!--criteria indicates if Subject (from security domain) or
app
supplied
parameters (such as from getConnection(user, pw)) are
used to
distinguish
connections in the pool. Choices are
ByContainerAndApplication (use both),
ByContainer (use Subject),
ByApplication (use app supplied params only),
ByNothing (all connections are equivalent, usually if
adapter
supports
reauthentication) OR you are using default security-->
<attribute name="Criteria">ByContainer</attribute>
</mbean>
</depends>
<depends
optional-attribute-name="CachedConnectionManager">
jboss.jca:service=CachedConnectionManager</depends>
<!-- Include a login module configuration named FirebirdDBRealm.
Update your login-conf.xml, here is an example for a
ConfiguredIdentityLoginModule:

<application-policy name = "FirebirdDBRealm">
<authentication>
<login-module code =
"org.jboss.resource.security.ConfiguredIdentityLoginModule" flag =
"required">
<module-option name = "principal">sysdba</module-option>
<module-option name = "userName">sysdba</module-option>
<module-option name = "password">mast
erkey</module-option>
<module-option name =
"managedConnectionFactoryName">jboss.jca:service=X
aTxCM,name=FirebirdDS</module-option>
</login-module>
</authentication>
</application-policy>

NOTE: the application-policy name attribute must match
SecurityDomainJndiName, and the
module-option name = "managedConnectionFactoryName"
must match the object name of the ConnectionManager you are
configuring
here.
-->
<!--comment out this line if you want component managed security
or want
to use the default values in the
ManagedConnectionFactoryProperties
-->
<attribute name="SecurityDomainJndiName">Fireb
irdDBRealm</attribute>

<depends
optional-attribute-name="JaasSecurityManagerServic
e">jboss.security:service=JaasSecurityManager</depends>

<attribute
name="TransactionManager">java:/TransactionManager</attribute>

<!--make the rar deploy - A BIG HACK till xslt based deployment
is
written-->
<depends>jboss.jca:service=RARDeployer</depends>

</mbean>

</service>