Tuesday, December 30, 2008

Hibernate and EnhancerByCGLIB issues

Working with Hibernate can make performing routine database operations a breeze. Every now and then however you hit a snag that causes your brain to drop on the floor. I recently ran across one of these situations where objects were not being updated. After checking my syntax and inspecting objects, I realized the issue had to do with the fact that Hibernate will often create proxy objects as classes of type EnhancerByCGLIB when pulling collections that are marked as FetchType.LAZY. It also occurs when the SessionFactory is asked to load() the object instead of get().


First Case : Lazy Initialization Proxies:

The proxies may often contain all the information in your persistent class, and so they appear to be the real deal. When we query these objects they return all the information in the underlying proxied object. We run into a couple issues however if we use standard implementations of equals in our persistent class:

Lets say your equals() method in the class Noodle looks like this:

public boolean equals(Object obj) {
if(this == obj)
return true;
if(getClass() != obj.getClass() )
return false;
........
}


At this point we see that equals will never succeed - the class Noodle will never equal the Hibernate Proxy class. When you try to update an object from the collection, Hibernate never finds the object it needs to update because the equals implementation always fails. If you need to implement a comparison of class you could use one of the following :

this.getClass().isAssignableFrom(obj.getClass())

or

HibernateProxyHelper.getClassWithoutInitializingProxy(obj)

If you use hibernate annotations in your pojos, this additional Hibernate checking should not be too invasive or out of place. One other mistake people often make when implementing equals is to try to access the fields directly off of the object:


public boolean equals(Object obj) {
....
Noodle other = (Noodle)obj;
if(! this.attributeA.equals(other.attributeA) )
return false;
....
}


Here we run into 2 issues:

1. The cast may throw a ClassCastException.
2. The attributeA may have limited visibility.

A Solution that worked for me and covers the situation where the other object is not a proxy:

public boolean equals(Object other) {

if ( this == obj )
return true;

if( obj == null )
return false;

if( getClass().equals( obj.getClass() ) {
Noodle other = (Noodle)obj;
if( getId().equals( other.getId() )
return true;
}

else if( obj instanceof HibernateProxy ) {
if(HibernateProxyHelper.getClassWithoutInitializingProxy(obj).equals(this.getClass() ) {
Noodle other = (Noolde)obj;
if( getId().equals( other.getId() )
return true;
}
}

return false;
}

Second Case: You used Load() instead of get():

There may be times when you want to use load() - when you are simply adding references to an object in a collection for instance. But when you are pulling an object specifically to use its values, you should switch to using get() - as the attributes are guaranteed to be available.


Monday, December 8, 2008

Notes from a Windows to Mac Conversion Bender

I recently upgraded from my 2 year old pc to a new mac book pro. It was a tough decision because I need to be able to run oracle and the customer I am working with uses WebEx AIM Pro for desktop sharing. (I work remotely 75% of the time.) . The final decision to go mac was heavily influenced by the fact that I could use vmware to run an instance of my windows pc on the mac. The fear of Vista (Vistanoia?), the desire to leave the world of waiting for apps to respond, and the chance to try out a new toy were beckoning.

I pulled the trigger, and jumped on the configuration express.

I've been in config-hell before, and something deep down told me this would be no different. All the smiling faces that I met saying that changing to a mac was like a trip to disney surely had to have an unseemly downside lurking in a hidden log file.

The basic setup was pleasant - eclipse and tomcat installed as on a pc by simply unzipping them into the directory where they would be run from:

%> sudo gunzip apache-tomcat-5.5
%>sudo tar -xvf apache-tomcat-5.5
%>cd apache-tomcat-5.5/bin
%>./startup.sh (You are now running tomcat)

Eclipse was similiar.

One thing to get used to on a mac is that you no longer download jdks from sun. The jdk is preinstalled on your machine. You go to the Apple site and download the jdk for the mac if you need a different version or need an update. Apple doesnt seem to supply options for minor versions.

I ordered up a copy of VMWare fusion and planned to install my old pc on the mac as a resource - the ability to access old email, documents etc without having to go back to an old machine. However I had overlooked one issue - the windows xp had been installed on my pc when I bought it, and when I booted it up on the vmware, windows insisted on an activation key. The old one on the laptop was no longer valid, and so I had three days of use before the activation grace period expired. In that time I copied files over to the mac and made the most of the situation. The VMWare fusion worked greeat - and it was phenomenal to see the old pc running on the mac.

I would suggest taking snapshots of your OS in vmware, and make sure you keep the original so that you can always return to it. After three days however, I said goodbye to my old pc on the mac.

The next step for me was to get Oracle installed on the Mac. This was simple enough by installing Ubuntu on a vm in the vmware fusion. Again, vmware worked like a charm and Ubuntu is a very nice and user friendly. I used Ubuntu 8.10 and downloaded an OracleXE database. Everything went smoothly, and the database runs without issue. With vmware I can actually drag and drop files between desktops, and the database has been running without issue.


On Ubuntu, before you start running all over the net looking for apps or jdks to download, use the Synaptic Package Manager under the System tab on your desktop. Real simple.

The stumbling block for me was that the project I work on uses sqlldr to load the database. This executable processes a flat file and inserts the data in the oracle database based on a template definition. I wanted to run the eclipse and tomcat on the mac, and the oracle instance on the Ubuntu vm. I read that installing the oracle client on the mac was possible, but when I did, I found that it did not come with the sqlldr utility.

My first plan to circumvent the issue ws to run eclipse and tomcat on the Ubuntu instance. This worked, but eclipse had issues - perspectives needed to be configured with each new restart, and I found eclipse crashed in the vm frequently. I needed an environment that was stable and able to handle heavy loads on the processor and manage the memory well. This was not the case on Ubuntu in the vmware virtual machine.

The next idea was to install A Samba server on Ubuntu. Samba allows you to access other machines, in this case the virtual machine on my mac. The install call was

%> sudo apt-get install samba smbfs

This allowed me to access files across machines - however I was not able to execute the sqlldr command from the mac on the Ubuntu machine.

My next plan of attack is to update to the OS/X Server and install the Oracle Client for 10g.

And so it goes...









Monday, December 1, 2008

Tomcat and Eclipse on Ubuntu

The following is a quick review of the steps I took to get eclipse and tomcat up and running and playing well together. Replace "user" with your local user name. I probably opened things up a bit wide security wise. It works. 
  • Download the tar.gz files for linux from the respective web sites. 
  • gunzip the files 
  • tar xvf the files. 
  • Move Tomcat to /usr/share/tomcat
  • sudo chown -R user:user tomcat
  • sudo chmod -R a+rwx tomcat
  • sudo chmod +x `sudo find tomcat -type d`
  • Move eclipse to /opt
  • sudo chown -R  user:user eclipse
  • sudo chmod -R a+rwx eclipse
  • sudo chmod +x `sudo find eclipse  -type d`
The net affect of the above is that the applications are deployed to their respective locations and the user "user" may run them. I was able to run each separately, but was having issues with eclipse trying to run tomcat. These errors were due restrictions in writing logs and deploying to tomcat directories due to permission issues. The above commands allow all users to write to these directories. 

Next, you can create an executable that specifies an ECLIPSE+HOME variable: 
Create an executable for eclipse in usr/bin called eclipse
  • #!/bin/sh
  • export ECLIPSE_HOME="/opt/eclipse"
  • $ECLIPSE_HOME/ECLIPSE $*
Save the file and run clean: 
/opt/eclipse/eclipse -clean

Add the two applications to the menu via System>Preferences>Main Menu. 

In eclipse, add Tomcat as a server, and in the tomcat configuration, under "server Locations", choose "Use Tomcat Installation" radio button and save. 



SQLDeveloper on Ubuntu

To Install SQLDeveloper, download the application from http://www.oracle.com/technology/software/products/sql/index.html. Unzip the archive and move it to the directory of choice. I chose /usr/share. Change the file to be executable: 

  • unzip sqldeveloper.zip
  • sudo mv sqldeveloper /usr/share/sqldeveloper
  • sudo chmod +x /usr/share/sqldeveloper/sqldeveloper.sh 
Then invoke the sqldeveloper.sh : 
  • sh sqldeveloper.sh
You will be asked to specify the path to the java home. You are up and running. 

To add SQLDeveloper to your menu, Go to System?Preferences>Main Menu. Add SQLDeveloper to the menu of choice. The Application is now executable from your menu the next time you log in. 

Install Samba on Ubuntu

I have a vm on my Mac running as a guest with Ubuntu as the OS. In order to access files on the vm guest, I added Samba - a file and print server. To install Samba,  and edit the configuration: 
  • sudo apt-get install samba smbfs (Installs Samba)
  • sudo gedit /etc/samba/smb.conf (Edits the config file)
Then, go to the "Share Definitions" section of the config file that is open in your editor: 

find the lines:
  • ; [homes]
  • ; comment = Home Directories
  • ; browseable = yes

Remove the ";" to uncomment these. This allows you to access the home directories on the guest.  Then find the following line that specifies the read only attribute. It is set for yes by default. To be able to write to your directories, change this to "no" and uncomment it. 
 
  • read only=no  (if you want to be able to write to the directory)

Next you need to find the line "; security = user". Replace it with the following 2 lines: 
  • security = user
  • username map = /etc/samba/smbusers
This specifies that users may authenticate, and that the users are specified in the /etc/samba/smbusers file.  Next we need to add entries to the users file. 
  • sudo gedit /etc/samba/smbusers
The file will be empty. If you want to access your local profile, and lets say the login name is Gus, then add the following line to smbusers: 

  • Gus="Gus" 
Save the file and from the command line create a Samba password: 
  • sudo smbpasswd -a Gus
You will be prompted to enter and confirm the password for this user. Thats it , you are done with the install of Samba. 


To stop | start | restart Samba: 
  • /etc/init.d/samba stop | start | restart
The config file allows you to change the behaviour of the samba server. There are comments in the file that point you to resources for evolving the file and perhaps opening up you system to domains. 

To Access the samba server, go to : 
  • smb://computer_name/Gus
On a Mac, this is under Finder>Go>Connect to Server>.  You will be prompted for a authentication with the password you specified to smbpasswd. If authentication works, the disk will mount on your desktop, and you will be able to browse within your profile on the remote machine.