Java stuff

From Dw0rm's Wiki

Table of contents

Patterns

Great site discussing common patterns giving examples in Java: http://www.fluffycat.com/Java-Design-Patterns/


XML Namespace notes

  • A schema validating parser will check for the correct declaration of namespaces at all levels of an xml document.
  • Errors will be reported if child document namespaces do not match to parent document namespaces.
  • No need to declare namespace in a child document which gets included in a top-level parent document that has a namespace defined.
  • The blitztec namespace is: http://www.blitztec.co.nz/blitztec- in the root xsd schemas this should be the targetNamespace as well as the default (xmlns=) namespace.

JDBC connection details

  • jdbc connection pool url: jdbc:oracle:thin:@isis:10090:oracle_sid
  • jdbc Oracle driver: oracle.jdbc.driver.OracleDriver


Saving binary streams (e.g. jpgs) into Oracle BLOBs

  • Tried to save a jpg into an oracle blob like this:
    Connection conn = dataSource.getConnection();
    PreparedStatement ps = null;
    try
    {
      ps = conn.prepareStatement( 
      "INSERT INTO validation_image (image_id, album_id, image_desc, image_full) VALUES(? , ?, ?, ? )" );

      ps.setInt(1, 1);
      ps.setInt( 2, albumId );
      ps.setString( 3, desc );

      File image = new File( filename );
      System.out.println("image.getAbsolutePath():" +image.getAbsolutePath());
      FileInputStream fis = new FileInputStream( image );

      ps.setBinaryStream( 4, fis, ( int )image.length() );

      // Execute the INSERT
      int count = ps.executeUpdate();
      System.out.println( "Rows inserted: " + count );

but trying to save the FileInputStream like this returned me this exception on files larger than 4k:

java.sql.SQLException: No more data to read from socket
	at oracle.jdbc.dbaccess.DBError.throwSqlException(DBError.java:134)
	at oracle.jdbc.dbaccess.DBError.throwSqlException(DBError.java:179)
	at oracle.jdbc.dbaccess.DBError.check_error(DBError.java:1160)
	at oracle.jdbc.ttc7.MAREngine.unmarshalUB1(MAREngine.java:963)
	at oracle.jdbc.ttc7.MAREngine.unmarshalSB1(MAREngine.java:893)
	at oracle.jdbc.ttc7.Oall7.receive(Oall7.java:375)
	at oracle.jdbc.ttc7.TTC7Protocol.doOall7(TTC7Protocol.java:1983)
	at oracle.jdbc.ttc7.TTC7Protocol.parseExecuteFetch(TTC7Protocol.java:1141)
	at oracle.jdbc.driver.OracleStatement.executeNonQuery(OracleStatement.java:2149)
	at oracle.jdbc.driver.OracleStatement.doExecuteOther(OracleStatement.java:2032)
	at oracle.jdbc.driver.OracleStatement.doExecuteWithTimeout(OracleStatement.java:2894)
	at oracle.jdbc.driver.OraclePreparedStatement.executeUpdate(OraclePreparedStatement.java:608)
	at nz.co.blitztec.pictureapp.poller.test.LoadImage.main(LoadImage.java:58)
java.sql.SQLException: No more data to read from socket
	at oracle.jdbc.dbaccess.DBError.throwSqlException(DBError.java:134)
	at oracle.jdbc.dbaccess.DBError.throwSqlException(DBError.java:179)
	at oracle.jdbc.dbaccess.DBError.check_error(DBError.java:1160)
	at oracle.jdbc.ttc7.MAREngine.unmarshalUB1(MAREngine.java:963)
	at oracle.jdbc.ttc7.MAREngine.unmarshalSB1(MAREngine.java:893)
	at oracle.jdbc.ttc7.Oclose.receive(Oclose.java:101)
	at oracle.jdbc.ttc7.TTC7Protocol.close(TTC7Protocol.java:720)
	at oracle.jdbc.driver.OracleStatement.close(OracleStatement.java:717)
	at oracle.jdbc.driver.OraclePreparedStatement.privateClose(OraclePreparedStatement.java:489)
	at oracle.jdbc.driver.OraclePreparedStatement.close(OraclePreparedStatement.java:396)
	at nz.co.blitztec.pictureapp.poller.test.LoadImage.main(LoadImage.java:69)

So I looked around and somebody mentioned that one needs to

  • first create a temporary BLOB in your java app (using BLOB tempBlob = BLOB.createTemporary(conn, true, BLOB.DURATION_SESSION);)
  • you then put the data into that blob via stream or byte arrays or whatever you prefer.
  • finally you can use the stm.setBlob(1, tblob); method to put the blob into your database.

This way any sized binary streams can be saved in the db. The final code looks like so:

    Connection conn = ds.getConnection();
    PreparedStatement ps = null;
    try
    {
    	BLOB tempBlob = BLOB.createTemporary(conn, true, BLOB.DURATION_SESSION);
        OutputStream bos = tempBlob.getBinaryOutputStream();
        int bufSize = tempBlob.getBufferSize();
        int b = 0;
    	
      ps = conn.prepareStatement( 
        "INSERT INTO validation_image (image_id, album_id, image_desc, image_full) VALUES(? , ?, ?, ? )" );

      ps.setInt(1, 1);
      ps.setInt( 2, albumId );
      ps.setString( 3, desc );

      // Insert the image into the second Blob
      File image = new File( filename );
      System.out.println("image.getAbsolutePath():" +image.getAbsolutePath());
      FileInputStream fis = new FileInputStream( image );
      
      while ((b=fis.read()) != -1)
      {
        bos.write(b);
      }
      bos.flush();
      bos.close();
      ps.setBlob(4, tempBlob);
 
      // Execute the INSERT
      int count = ps.executeUpdate();
      System.out.println( "Rows inserted: " + count );


The question remains - WHY DOES ONE HAVE TO USE TEMPORARY BLOBS?

<updated 18 May 2006>
  • Tried doing the same in MySql - interestingly enough Mysql had a 64K limit on the blob datatype but apart from that setting a byte[] into the pStmt parameter worked fine. Also, Mysql actually has a mediumblob (up to 16MB) and a longblob (up to several gigs I believe) datatypes. Again simply setting a byte[] in the pStmt worked first time for me.

Servlet Notes

JSP Notes

Oracle Thin Client Driver How-To

Here is the Oracle link:
http://otn.oracle.com/tech/java/jroadmap/jdbc/listing.htm


Looks like you have to have the files unzipped to the following directory inside your project directory. So e.g. if you are working on a project inside folder connection then you need to unzip the oracle directory from classes12.zip into ./connection/oracle so that the OracleDriver.class file is in the following location:
./connection/oracle/jdbc/OracleDriver.class
here is sample java code for the actual connection: (ConnectMe.java located in folder ./connection/)
import java.sql.*;
public class ConnectMe {
    static String url = "jdbc:oracle:thin:@n009.blitztec.co.nz:10001:loa";
    static String user = "energydb";
    static String passwd = "energydb";
    static String driver = "oracle.jdbc.driver.OracleDriver";
    static Connection con = null;
    static Statement stmt = null;

    public static void main (String args[]) {
        try {
            Class.forName(driver).newInstance();
            con = DriverManager.getConnection(url,user,passwd);
            stmt = con.createStatement();

            ResultSet rs = stmt.executeQuery("select count(*) from en_sysdates");
            while (rs.next()) {
                int aCount = rs.getInt(1);
                System.out.println("# of records in en_sysdates should be 1: "+aCount);
            }
        } catch (SQLException sqle) {
              sqle.printStackTrace();
        } catch (Exception e) {
              e.printStackTrace();
        } finally {
            try {
                if (stmt != null) stmt.close();
                if (con != null) con.close();
            } catch (SQLException ex) { /* don't care */ }
        }
    }
}

Webservices Stuff

My notes on webservices as well as setup guidelines for a web services server and client using Websphere (Websphere 5.1 runtime).

JUnit Testing

DbUnit Testing


HttpProxy

Websphere Server stuff

Code Snippets

Jetty Notes

See my [Quick Links] for jetty website and eclipse plug-in details.

How to solve "Unable to find a javac compiler" error when launching Jetty from Eclipse?

The Error I encountered was one like this:

2005-05-27 21:35:54,421 ERROR [SocketListener0-1] compiler.Compiler (Compiler.java:412) - Javac exception 
Unable to find a javac compiler;
com.sun.tools.javac.Main is not on the classpath.
Perhaps JAVA_HOME does not point to the JDK
 at org.apache.tools.ant.taskdefs.compilers.CompilerAdapterFactory.getCompiler(CompilerAdapterFactory.java:105)
 at org.apache.tools.ant.taskdefs.Javac.compile(Javac.java:929)
 at org.apache.tools.ant.taskdefs.Javac.execute(Javac.java:758)
 at org.apache.jasper.compiler.Compiler.generateClass(Compiler.java:407)
        .....

Error message is misleading as there is nothing wrong with the JAVA_HOME variable.

Solution: Add tools.jar (from lib directory of Java SDK) to the classpath given to the Jetty instance. There's a tab to set the Jetty instance's classpath as part of the Jetty Launcher.


Hibernate Notes

What to do about a StaleObjectStateException?

This is hibernate's "normal" way of handling concurrency issues when versioning is used for optimistic concurrency control. (In my opinion it is not the perfect solution as hibernate is using exceptions for a non-exceptional situation which goes against the exception handling mantra. Also these exceptions unnecessary pollute application error log files)

What to do:

  • Catch this exception and provide a meaningful response to the user - this response should indicate that the failed operation must be retried as the errored out session is discarded.

Here is an example that shows this within a Struts action (http://forums.hibernate.org/viewtopic.php?p=2196352&highlight=&sid=6601a5c413379b144a8a198a0f9fd8bc)


Sessions and transactions

Article discussing how Hibernate associates transactions with Sessions: http://www.hibernate.org/42.html


Session in a View

Continuation of the sessions & transactions topic: http://www.hibernate.org/43.html


Hibernate sesion managemenet in unit tests (using Spring)

Great article on writing unit tests for Hibernate including useful info on session management, lazy loading, etc. http://today.java.net/pub/a/today/2005/10/11/testing-hibernate-mapping.html?page=last

We had the above implemented in our last project in a helper class. The sessionFactory (retrieved via Spring) was the org.springframework.orm.hibernate3.LocalSessionFactoryBean containing a dataSource, mappingResources (hbm's) and hibernateProperties.

public class TestHelper implements Constants
{
	public static void transactionSetUp(SessionFactory sessionFactory) throws Exception
	{
		Session s = sessionFactory.openSession();
		TransactionSynchronizationManager.bindResource(sessionFactory, new SessionHolder(s));
	}
	
	public static void transactionTearDown(SessionFactory sessionFactory) throws Exception
	{
		SessionHolder holder = (SessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);
		Session s = holder.getSession(); 
		s.flush();
		TransactionSynchronizationManager.unbindResource(sessionFactory);
		SessionFactoryUtils.closeSessionIfNecessary(s, sessionFactory); 
	}
}

The above would be called in the unit tests' setUp() and tearDown() methods.


Hibernate problems and solutions

  1. Hibernate not retrieving entity and throwing (what seems) a jdbc driver exception.

I have a simple entity mapping and attempt to save and then retrieve the object by the generated id. Hibernate throws the following exception:

org.springframework.jdbc.UncategorizedSQLException: Hibernate operation: could not load an entity: 
[nz.co.client.blitztecapp.model.MessageArchiveImpl#41]; uncategorized SQLException for SQL [/* load nz.co.client.blitztecapp.model.MessageArchiveImpl */ select messagearc0_.MESSAGE_ARCHIVE_ID as MESSAGE1_0_0_, messagearc0_.MESSAGE_TYPE as MESSAGE2_0_0_, messagearc0_.CONTENTS as CONTENTS0_0_, messagearc0_.MESSAGE_ID as MESSAGE4_0_0_, messagearc0_.PACKAGE_ID as PACKAGE5_0_0_, messagearc0_.CREATED_BY as CREATED6_0_0_, messagearc0_.CREATED_DATE as CREATED7_0_0_ from MESSAGE_ARCHIVE messagearc0_ where messagearc0_.MESSAGE_ARCHIVE_ID=?]; SQL state [null]; error code [-99999]; executeQuery method cannot be used for update.; nested exception is com.ibm.db2.jcc.a.SqlException: executeQuery method cannot be used for update.
Caused by: com.ibm.db2.jcc.a.SqlException: executeQuery method cannot be used for update.
	at com.ibm.db2.jcc.a.co.a(co.java:2079)
	at com.ibm.db2.jcc.a.cp.d(cp.java:1528)
	at com.ibm.db2.jcc.a.cp.K(cp.java:316)
	at com.ibm.db2.jcc.a.cp.executeQuery(cp.java:299)
	at org.hibernate.jdbc.AbstractBatcher.getResultSet(AbstractBatcher.java:186)
	at org.hibernate.loader.Loader.getResultSet(Loader.java:1787)
	at org.hibernate.loader.Loader.doQuery(Loader.java:674)
	at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:236)
	at org.hibernate.loader.Loader.loadEntity(Loader.java:1860)
	at org.hibernate.loader.entity.AbstractEntityLoader.load(AbstractEntityLoader.java:48)
	at org.hibernate.loader.entity.AbstractEntityLoader.load(AbstractEntityLoader.java:42)
	at org.hibernate.persister.entity.AbstractEntityPersister.load(AbstractEntityPersister.java:3042)
	at org.hibernate.event.def.DefaultLoadEventListener.loadFromDatasource(DefaultLoadEventListener.java:395)
	at org.hibernate.event.def.DefaultLoadEventListener.doLoad(DefaultLoadEventListener.java:375)
	at org.hibernate.event.def.DefaultLoadEventListener.load(DefaultLoadEventListener.java:139)
	at org.hibernate.event.def.DefaultLoadEventListener.proxyOrLoad(DefaultLoadEventListener.java:195)
	at org.hibernate.event.def.DefaultLoadEventListener.onLoad(DefaultLoadEventListener.java:103)
	at org.hibernate.impl.SessionImpl.fireLoad(SessionImpl.java:878)
	at org.hibernate.impl.SessionImpl.get(SessionImpl.java:815)
	at org.hibernate.impl.SessionImpl.get(SessionImpl.java:808)
	at org.springframework.orm.hibernate3.HibernateTemplate$1.doInHibernate(HibernateTemplate.java:467)
	at org.springframework.orm.hibernate3.HibernateTemplate.execute(HibernateTemplate.java:369)
	at org.springframework.orm.hibernate3.HibernateTemplate.get(HibernateTemplate.java:461)
	at org.springframework.orm.hibernate3.HibernateTemplate.get(HibernateTemplate.java:455)
.
.

Problem:
The problem is a hibernate property that I had set : hibernate.use_sql_comments=true causes the failure. This seems to be a bug in hibernate - Unset the property and things should work fine.



  1. Hibernate not saving children and throwing this error:
Hibernate: select max(item_id) from item
Hibernate: insert into item (item_title, item_detail, item_id) values (?, ?, ?)
Hibernate: update item_picture set item_id=?, file_name=?, item_picture_data=? where item_picture_id=?
00:09:24,683 ERROR AbstractBatcher:61 - Exception executing batch: 
org.hibernate.StaleStateException: Batch update returned unexpected row count from update: 0 actual row count: 0 expected: 1
	at org.hibernate.jdbc.BatchingBatcher.checkRowCount(BatchingBatcher.java:93)
	at org.hibernate.jdbc.BatchingBatcher.checkRowCounts(BatchingBatcher.java:79)
	at org.hibernate.jdbc.BatchingBatcher.doExecuteBatch(BatchingBatcher.java:58)
.
.
.

Problem:
I have a normal parent-child association backed by a Set on the parent object. I have created the parent and added a child to it and am attepting to save the parent. The problem as we can see above is that instead of doing the second insert for the child hibernate performs the update.
Solution:
My problem was that I was setting the id of the child even though I had a generator column for the id of the child. With the id there, Hibernate was thinking that the child already exists and instead of creating it again would just update it.