Understanding Hibernate Delete Orphan issues

Posted in Bob Hedlund on February 5th, 2009 by admin

admin originally posted this on Eldorado Software.

Ever wonder how Hibernate knows to follow the annotation "CascadeType.DELETE_ORPHAN"?

The short answer is that Hibernate keeps track of removals from these collections. When you remove an object from the collection, the status of that object is set to Status.DELETED or Status.GONE if the object is still in the persistence context. When hibernate saves the object tree that contained these ex-collection objects, it sees that the object has been dereferenced, marked for deletion, and is then removed from persistent storage and the persistence context.

Thats how it is supposed to happen.

Ever wonder why you are getting the error :
org.hibernate.HibernateException: A collection with cascade="all-delete-orphan" was no longer referenced by the owning entity ?

Lets say that you have an object with a collection that is cascade type delete orphan, and it has some child entities in it. And lets say that you change this collection simply by calling the setter on the parent object:

parent.setChildren(someDifferentSet);

or

parent.setChildren(null);

The children that were in the collection are still in the persistent context. However, they are no longer referenced by the parent object, and Hibernate never said goodbye to them. In the Collections class (org.hibernate.engine.Collections) , the method processDereferencedCollection(Collection, Session) throws a hibernate exception when it finds these items in the persistence context, and their Status is not set to GONE or DELETED. The ensuing error message: 'A collection with cascade="all-delete-orphan" was no longer referenced by the owning entity: ' followed by the parent entity name is shown.

The proper way to handle this situation where you want to replace an existing collection would be:

parent.getChildren().clear();
parent.getChildren().addAll(someDifferentSet);

Similiarly, if you just wanted to remove a child from the collection:

parent.getChildren().remove(child);

By calling the collections manipulations, hibernate is kept up to date with the status of the objects within and without the collection. It then manages the objects in the persistence session properly.



Dynamically generated Trinidad Component Trees and Input Values

Posted in Bob Hedlund on January 11th, 2009 by admin

admin originally posted this on Eldorado Software.

I recently generated JSF pages dynamically with components from Apache Trinidad. The results are wonderful - and saved a great deal of iterative development time by allowing business analysts to specify the UI for various business scenarios. (For those interested I explain the basics of doing this later in this blog.)

We of course find a few issues:

1. When Using PPR, values do not always match bindings.
2. Components sometimes disappear!

Some of the components used PPR on the front side to update other components. I ran into a few issues while validating these fields - the values did not match the values shown in the UI. I scratched my head and rolled up the sleeves.

The issue is that JSF has 2 notions of values for an input component:

1. The component binding.
2. The component value.

The component value is typically the value of the binding. However, if you perform an XmlHttpRequest (PPR), these values may be out of sync. The scenario I used was that I generate the components and specify bindings to backing beans. This works fine until a PPR request is made and you update the model. The UI value becomes out of sync with the backing model. There are a few solutions to this issue:

1. Call comp.setValue(the value) on the actual component. This will require accessing the component directly.
2. Call comp.resetValue() - this syncs the value to the binding but also requires that you access the component on the server.
3. Use PartialTriggers when generating the component. This requires that you specify the id of the components that will cause a refresh of the value, and that you keep track of these ids. Then you specify the partial triggers on the component at the time of component generation.

I found that solution 3 works best for me, as I found another issue with generating components server side and using AJAX on the front. Sometimes, inexplicably, components would disappear. The reason for this is that Trinidad generates ids for components, and when you have components generated on the fly, there seems to be competition among the components for the ids. This seems to be a result of mixing generated and dynamically generated ids. The solution for this is to specify all ids for your generated components. Coupled with the task of specifying partialTriggers and using ids for javascript calls, this is the best comprehensive and cohesive approach.

A little example of a dynamically generated UI :


In your UI page you simply add a container with a binding to a managed bean. In the following line, I specify a panelAccordion (without opening and closing brackets) :
tr:panelAccordion binding="#{myManagedBean.accordionX}"

then in your backing bean you generate the PanelAccordion:

CorePanelAccordion accordionX = new PanelAccordion();

Make sure that you provide public access to this component. You then build the component by adding boxes, tables, and inputs as you need. You also set the various attributes on all the components.

For instance, lets add a Detail Item with a box and an input with a button:

FacesContext fc = FacesContext.currentInstance();
CoreShowDetailItem tab1 = new CoreShowDetailItem();
// the following names are the handles you would call in the ui.
String tab1Binding = "myManagerKeyString.tab1Name";
tab1.setValueBinding("binding" , fc.getApplication().createValueBinding("#{" + tab1Binding + "}"));

tab1.setDisclosed(true);



CorePanelBox pb1 = new CorePanelBox();
pb1.setStyleClass("yourStyleClass");



CoreInputText comp = new CoreInputText();
comp.setId(genYourId());
comp.setLabel("Label for Component");
comp.setConverter(new YourCompConverter());

// This is the value binding to the backing pojo
String compBindString = "myManagerKeyString.pojo.compAttributeRelationName";
comp.setValueBinding("value", fc.getApplication().createValueBinding(#{"+ compBindString + "}"));


CoreCommandButton button = new CoreCommandButton();
button.setText("GO");
addButton.setId(genYourId());

// create a methodBinding
String actionBindingString = "myMangerKeyString.go";
MethodBinding mb = fc.getApplication().createMethodBinding("#{" + actionBindingString + "}"));
button.setAction(mb);


Now lets add the components:


pb1.getChildren().add(comp);
pb1.getChildren().add(button);
tab1.getChildren().add(pb1);
accordionX.getChildren().add(tab1);

Then when the UI is displayed, it references accordionX, and in the backing bean you generate this UI. Your managed bean needs to provide access to the actions and the pojo. This was a simple case and you can of course enhance you components by specifying any more attributes.

A couple pieces of advice:

1. Specify styleClasses and use css to control layout.
2. ValueBindings can be used to specify all the attributes that you normally specify client side. For instance, if the style of a component is bound to a server side call:

String styleBinding= "myMangerKeyString.someStyleBindingCallName";
comp.setValueBinding("styleClass", fc.getApplication().createValueBinding("#" + styleBinding + "}" ));

You can use this to bind any attribute to a server call.


In a recent application, I used xml to specify the fields and their attributes possible in a UI. I then allowed business analysts to choose which components displayed when, and even some of the attributes such as label and order. Then, when the call is made to display a container, I generate the UI based on their selections. It worked quite well.
























Hibernate and EnhancerByCGLIB issues

Posted in Bob Hedlund on December 30th, 2008 by admin

admin originally posted this on Eldorado Software.

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.


Notes from a Windows to Mac Conversion Bender

Posted in Bob Hedlund on December 8th, 2008 by admin

admin originally posted this on Eldorado Software.

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...









Tomcat and Eclipse on Ubuntu

Posted in Bob Hedlund on December 1st, 2008 by admin

admin originally posted this on Eldorado Software.

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

Posted in Bob Hedlund on December 1st, 2008 by admin

admin originally posted this on Eldorado Software.

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

Posted in Bob Hedlund on December 1st, 2008 by admin

admin originally posted this on Eldorado Software.

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. 


Install Java on Ubuntu

Posted in Bob Hedlund on November 30th, 2008 by admin

admin originally posted this on Eldorado Software.

I thought this would be straight forward....

I tried to install using the apt-get command, but my install died when the license agreement took over my terminal, and did not give me a way to accept the agreement. I exited and ran the dpkg --configure -a command to clean up the failed install. 

Instead I went to the System>Synaptic Package Manager and performed the install this way: 
  • Specify "java" in the search box. A List of packages to install appears.
  • I chose the java5 jre, jdk and plugin, and accepted the installation of packages that they were dependent on. 
  • Click Apply
  • Accept the license agreements as they appear.
  • The java home directory will be in /usr/lib/jvm
Note: If the java versions do not appear in the above list, ensure that the /etc/apt/sources.list file contains the following entries : 

  • deb http://us.archive.ubuntu.com/ubuntu/ intrepid multiverse
  • deb-src http://us.archive.ubuntu.com/ubuntu/ intrepid multiverse
  • deb http://us.archive.ubuntu.com/ubuntu/ intrepid-updates multiverse
  • deb-src http://us.archive.ubuntu.com/ubuntu/ intrepid-updates multiverse

You then need to ensure that this Java is the default java for the system: 
  • update-java-alternatives -s java-5-sun (specifies java 5 as the default)
  • update-java-alternatives -l (lists out the default)

Finally, add environmental variables. I add them in /etc/environment. You could also add them in your local .bashrc file in your home directory. The values specified in /etc/environment are in all shell. Be careful of the syntax in this file! If an error occurs you may not be able to boot up... : 

  • JAVA_HOME="/usr/lib/jvm/java-1.5.0-sun"
  • CLASSPATH="/usr/lib/jvm/java-1.5.0-sun/bin:/usr/lib/jvm/java-1.5.0-sun/lib"




Install Oracle XE on Ubuntu

Posted in Bob Hedlund on November 30th, 2008 by admin

admin originally posted this on Eldorado Software.

Oracle XE can be downloaded at the Oracle site. I chose the Debian install for Linux. I read the installation guides. This is how to do it:

If you do not have 1GB free memory, or want to reduce the database footprint:

  • $sudo dd if=/dev/zero of=/swpfs1 bs=1M count=1000
  • $sudo mkswap /swpfs1
  • $sudo swapon /swpfs1
I used the Ubuntu apt-get utility to install the database.
  • sudo gedit /etc/apt/sources.list
Insert "deb http://oss.oracle.com/debian unstable main non-free" into the file if it does not exist and save. Then import the Key:
  • $wget http://oss.oracle.com/el4/RPM-GPG-KEY-oracle -O- |sudo apt-key add -
Now install the db:
  • $sudo apt-get update
  • $sudo apt-get install oracle-xe
After the installation you need to run the following:
  • sudo /etc/init.d/oracle-xe configure
Follow the instructions and write down the ports and passwords that you specify. Then you will need to update the users on the system. Go to System>Administration>Users and Groups. Unlock the editor and under the manage groups tab, add your user to the dba group.

The database should be running. Go to http://localhost:8080 - assuming you didn't change this port in post config, and the database info page should appear. The database is now integrated in the menu as well - so if not running, start it it up by navigating to the Applications>Oracle Database>Start Database.

One more step is to specify the ORACLE_HOME and ORACLE_SID environmental variables:
  • gedit $HOME/.bashrc
Add the following lines:
  • ORACLE_HOME=/usr/lib/oracle/xe/app/oracle/product/10.2.0/server
  • ORACLE_SID=XE
  • export ORACLE_HOME ORACLE_SID
You could also/instead add these to your /etc/environment file. In that file you would add:
  • ORACLE_HOME="/usr/lib/oracle/xe/app/oracle/product/10.2.0/server"
  • ORACLE_SID="XE"
Either login in again, or type
  • source .bashrc
  • source /etc/environment
To run Oracle utilities add /usr/lib/oracle/xe/app/oracle/product/10.2.0/server/bin to the path in /etc/environment and source it.


You should be good to go. You can access the database home page via the menu, or go to http://localhost:8080/apex.





Install VMWare Fusion on Mac OS/X 10.5

Posted in Bob Hedlund on November 30th, 2008 by admin

admin originally posted this on Eldorado Software.

I needed to run an oracle database on a macbook. At the time, Oracle was not supported on Mac OS/X 10.5 . Talking to colleagues, and doing some research led me to VMWare fusion. It's not free. It works great.

Installing VMWare Fusion is a relatively simple process. I downloaded version 2.0.1-12 from the net and purchased a serial number from VMWare. The VMware site has excellent documentation and even supplies videos to walk you through the process. Here's what I did: 
  1. Download the .dmg file from vmware. (http://www.vmware.com/vmwarestore/buyfusion.html).
  2.  Double click on the .dmg file to mount it. The file will be mounted and displayed in a Finder window.
  3. Double click the Fusion Icon. The installation assistant will walk you through the process. 
  4.  To Start Fusion, navigate to Applications in Finder, and choose the VMWare Fusion Icon. 

The Fusion window will open, and you can create a new Virtual Machine at this point. Next I installed 2 different virtual machines - a copy of my old pc and a fresh Ubuntu install to house the Oracle Database. 

I followed the conversion guide for bringing my old pc over. The guide that comes with fusion (http://www.vmware.com/pdf/fusion_getting_started_11.pdf) has a link to a flash video that walks you through the process. The process took about 8 hours for the vmware converter to bundle my old pc onto an external disk drive and for me to boot the pc on the mac. It was pretty cool. The problem that I had was that my copy of XP only had 1 license, (it came bundled on the pc when I bought it) , and Windows required a new activation key. So I only had access to my old pc on the mac for 3 days. I would not do this unless you know you have multiple licenses. 

To install Ubuntu, I went to the Ubuntu site (http://www.Ubuntu.com/getubuntu/download) and downloaded the 8.10 version on 32 bit. I chose the 32 bit because the 64 bit has some issues with downloading and installing packages. Once downloaded, simply open the vmware fusion :


  1. Choose new 
  2. Click the continue without disk button. 
  3. Choose the "Use Operating System Disk image file" option.
  4. Choose the Ubuntu file you downloaded above from the finder that opens. 
  5. Click Continue and Finish. The new virtual machine will appear in the Fusion window. 
  6. Click the start button next to the Ubuntu vm, and fusion will open a window with the Ubuntu installation running within it. 



I ran through the installation by specifying users and some configuration. I simply accepted the defaults. 

Sage Advice
Install VMWare tools immediately after the Ubuntu installation occurs. Choose Install VMWare tools from the virtual machine menu item on the mac. To do this, move the untarred vmware tools distib to the /tmp directory.  Then run : 
  • sudo ./vmware-install.pl
Choose the defaults, and when complete, add the toolbox to the startup of your Ubuntu session: 

  1. Preferences>Sessions>StartUp Programs>+Add>
  2. Specify vmware-toolbox as the command to execute. 

Fusion allows you to take snapshots of your vm. DO THIS!!!! . Comment well the snapshot state, so that when you screw something up you can return to a stable state.  As I was installing the various applications to the Ubuntu system , I would stop and take a snapshot along the way after each milestone. This will save you considerable time.