1 2 Previous Next 18 Replies Latest reply on Nov 12, 2008 11:41 AM by juandbotiva

    Detach entities - Obtaining a clear pojo

    ajaleo

      Hi all!

      I have a question related with detaching entities. With the new specification is possible to send an entity bean to upper layers. However, the class of the entity obtained by the EntityManager is a class that extend of the pojo class. Then, is needed to have additional jars in the client. I've been looking for a detach method in the EntityManager but it's not there. Some topics talk about the clear method but I think it has the same problem.

      Could you give me any possibility to get a detached entity with no need of additional jars?

      Thanks a lot!

        • 1. Re: Detach entities - Obtaining a clear pojo

          I also need to obtain a completely clean Pojo.

          I currently serialise detached entities then send them over a network to a remote client which is an applet that has no concept of EJB3 (no ejb jars on classpath). Here the client treats them as Pojos.

          When running a full round trip unit test in which a detached obj is sent to the client, if a field/association is not initialised then I want a null value, not a LazyInitialisationException. But because in the test case the client and server are both run within the same JVM I get a LazyInitialisationException.
          If I was able to get a completely clean Pojo, this wouldn't happen.
          Is there some standard way of doing this? like a;
          Object getCompletelyDetachedClone(obj) method?

          thanks in advance

          James

          • 2. Re: Detach entities - Obtaining a clear pojo

            ...also, this method would ideally return an object that does not use any hibernate classes such as PersistentSet etc, so the client doesn't require the hibernate.jars.

            • 3. Re: Detach entities - Obtaining a clear pojo
              alrubinger

              Not sure if I'm necessarily understanding the issue but...

              Most times we encounter a LazyInitilizationException when trying to iterate over relationships outside the scope of the session/transaction; when sent to a remote client, for example.

              In these cases I like to call on a little utility method to ensure the association's objects are stored in a Collection of type I dictate:

              class PersistenceUtils
              
               /**
               * Returns a Collection of all objects in the specified persistentCollection
               * without binding to any persistence context or session.
               *
               * @param <T>
               * @param targetCollection
               * @param persistentCollection
               * @return
               */
               public static <T> Collection<T> getCollectionItemsRemovedFromPersistenceContext(
               Collection<T> targetCollection, Collection<T> persistentCollection) {
               // If runtime type of persistentCollection is not PersistentCollection,
               // take no action
               if (!(persistentCollection instanceof PersistentCollection))
               return persistentCollection;
              
               // Clear existing target
               targetCollection.clear();
              
               // Place all items in persistent collection into target
               for (T item : persistentCollection) {
               targetCollection.add(item);
               }
              
               // Return target
               return targetCollection;
               }


              ...and I can easily call it by:

              Collection<MyType> associations = new ArrayList<MyType>();
              
              associations = PersistenceUtils
               .getCollectionItemsRemovedFromPersistenceContext(
               associations,
               myAttachedObject.getAttachedAssociations());
              



              ...also, while responding here, found this post with a similar solution:

              http://www.mojavelinux.com/blog/archives/2006/06/hibernate_get_out_of_my_pojo/

              S,
              ALR

              • 4. Re: Detach entities - Obtaining a clear pojo

                I'm aware of why LazyInitilizationException's are thrown, and know I could do something like you demonstrated. My point though is this, as I understand things DTO's are no longer fashionable in EJB3 and entiy classes are POJO's which can be populated, then moved between the application tiers. This works fine unless you need to send the object to remote client, in which case you get the issue mentioned above. You can of course do some kind of manual cloning, but isn't doing such a thing very similar to creating a DTO albeit of the same class?
                Or you can create your own custom serialisation/encoding (perhaps using something like XStream as mentioned by Gavin in the URL), but then are the entities really POJO's if you have to do that?

                I understand that hibernate needs to add some magic for it to operate, but I'm requesting that there is some kind of method within Hibernate to return a version of the entity (probably a specially constructed instance/clone) with all traces of Hibernate removed.
                The "hibernate cleanser" as discussed at http://blog.murf.org/2005/04/06/lazy-loading-and-the-hibernate-cleanser/ proposes a similar thing but I couldn't find any source code.
                Does anyone else thing this would be a useful function? or am I missing something?

                • 5. Re: Detach entities - Obtaining a clear pojo
                  alrubinger

                  OK; cool.

                  Would a function that does the following meet your objectives?

                  * Obtain all internal members of the POJO via reflection/BeanUtils
                  * Iterate through 'em
                  * If the current is of type java.lang.Collection, run my above method on it (which replaces any Hibernate Collections with a standard Java Collection)
                  * Return the original POJO

                  ...?

                  It's kinda been on my to-do list to create the above as a util for my company, but because it seems hacky and "against the Hibernate" way, I've gone back and forth as to whether it's good design. It does, however, immediately seem to solve many problems with sending a entity to a remote client. Like a "cleanser." Thoughts/Opinions?

                  S,
                  ALR

                  • 6. Re: Detach entities - Obtaining a clear pojo

                    Yes that's exactly the kind of function and I agree with you it does seem slightly hacky implementing this ourself, which is why I was surprised to find there wasn't such a function within Hibernate iteself.

                    I suppose in a perfect world hibernate could return the "clean pojo" from within a hibernate wrapping, to avoid the computation overhead of having iterate through constructing a clean replica of the object tree, but.....

                    If you do come up with any code, please post it on this forum, alternatively if anyone from Hibernate/JBoss could add their thoughts on the design implications of this it would be appreciated.
                    I would think that if serialising EJB3 entites to XML for AJAX, this would be a common issue?

                    regards

                    James

                    • 7. Re: Detach entities - Obtaining a clear pojo
                      alrubinger

                      Great; going to play around with this today. Might have to break out some of those college books about object graph traversal and not getting stuck in infinite recursion when obtaining circular references within objects...

                      Really? This hasn't been done before? And if not - there wasn't a good reason why not to do it?

                      S,
                      ALR

                      • 8. Re: Detach entities - Obtaining a clear pojo
                        alrubinger

                        Allright; I've got something. Would love to hear opinions as to whether this makes for good design, and I'll probably cross-post this somewhere on the Hibernate forums to get thoughts from there...

                        Given a static utility method (code all below) that will return a non-Hibernate Collection of Entities, I had wanted to create a method that would strip out all Hibernate Collections from a POJO, like:

                        Object newPojo = PersistenceUtils.removePersistenceContext(myPojo);


                        ...however, could not figure any intelligent way to instruct the method as to how deeply it should be traversing its own graph to look for Hibernate classes.

                        So I restructured a bit (this is the part on which I'm wavering) to place the Hibernate-stripping method in an assumed Base Class for all entities. So the call now looks more like:

                        myPojo.removePersistenceContext();


                        I don't like that this solution requires a base entity class.

                        But it works for now - curious as to anyone's thoughts.

                        The Utility Class:

                        public class PersistenceUtils {
                        
                         /**
                         * Returns a Collection of all objects in the specified persistentCollection
                         * without binding to any persistence context or session.
                         *
                         * @param <T>
                         * @param targetCollection
                         * @param persistentCollection
                         * @return
                         */
                         public static <T> Collection<T> removeCollectionItemsFromPersistenceContext(
                         Collection<T> targetCollection, Collection<T> persistentCollection) {
                         // If runtime type of persistentCollection is not PersistentCollection,
                         // take no action
                         if (!(persistentCollection instanceof PersistentCollection))
                         return persistentCollection;
                        
                         // Clear existing target
                         targetCollection.clear();
                        
                         // Place all items in persistent collection into target
                         for (T item : persistentCollection) {
                         targetCollection.add(item);
                         }
                        
                         // Return target
                         return targetCollection;
                         }
                        
                        }
                        



                        Base Entity Class (from which all EJB3 Entities inherit; relevent code only shown):

                        /**
                         * Strips the entity of all data binding it to a specific persistence
                         * context, leaving intact only model-centric data
                         *
                         * @author ALR
                         */
                         public void removePersistenceContext() {
                         // Initialize
                         Collection<Integer> visitedObjectHashCodes = new ArrayList<Integer>();
                        
                         // Run
                         this.removePersistenceContext(this, visitedObjectHashCodes);
                         }
                        
                         /**
                         * If the specified object's identity hash code is not in the specified
                         * collection of visited hash codes, removes of all data binding the object
                         * (and its members) to a specific persistence context, leaving intact only
                         * model-centric data
                         *
                         * @param visitedObjectHashCodes
                         * @param obj
                         * @author ALR
                         */
                         private Object removePersistenceContext(Object obj,
                         Collection<Integer> visitedObjectHashCodes) {
                        
                         // Ensure the current object has not been visited
                         if (visitedObjectHashCodes.contains(System.identityHashCode(obj))) {
                         return obj;
                         }
                        
                         // Add the current object's hash to the Collection of visited hash codes
                         visitedObjectHashCodes.add(System.identityHashCode(obj));
                        
                         // If the current object is of type Collection
                         if (Collection.class.isInstance(obj)) {
                         // Remove persistence context
                         obj = PersistenceUtils.removeCollectionItemsFromPersistenceContext(
                         new ArrayList(), (Collection) obj);
                         }
                        
                         // Only traverse into types of PersistedEntity
                         if (!(PersistedEntity.class.isInstance(obj))) {
                         return obj;
                         }
                        
                         // Remove persistence context of all members
                         Map allMembers = this.getInternalMembers();
                         for (Object member : allMembers.entrySet()) {
                         Map.Entry m = (Map.Entry) member;
                         try {
                         PropertyUtils.setProperty((Object) obj, m.getKey().toString(),
                         this.removePersistenceContext((m.getValue()),
                         visitedObjectHashCodes));
                         } catch (IllegalAccessException e) {
                         throw new RuntimeException(e);
                         } catch (InvocationTargetException e) {
                         throw new RuntimeException(e);
                         } catch (NoSuchMethodException e) {
                         }
                         }
                        
                         // Return
                         return obj;
                         }
                        
                         // Internal Helper Methods
                         /**
                         * Returns a Map of all internal members of the specified object
                         *
                         * @param obj
                         * The object for which to obtain internal members
                         *
                         * @author ALR
                         * @see http://jakarta.apache.org/commons/beanutils/
                         */
                         protected Map getInternalMembers(PersistedEntity obj) {
                         try {
                         // Return a map of all visible properties
                         return PropertyUtils.describe(obj);
                         }
                         // Throw as unchecked exceptions up the stack trace
                         catch (IllegalAccessException iae) {
                         throw new RuntimeException(iae);
                         } catch (NoSuchMethodException nsme) {
                         throw new RuntimeException(nsme);
                         } catch (InvocationTargetException ite) {
                         throw new RuntimeException(ite);
                         }
                         }
                        
                         /**
                         * Returns a Map of all internal members of this PersistedEntitys
                         *
                         * @author ALR
                         * @see http://jakarta.apache.org/commons/beanutils/
                         */
                         protected Map getInternalMembers() {
                         return getInternalMembers(this);
                         }


                        Easy to use (one line of code), and it works. But I have the nagging feeling I can't be the first to have come across this, and there's a better way?

                        S,
                        ALR

                        • 9. Re: Detach entities - Obtaining a clear pojo
                          alrubinger

                          A note on the code above:

                          My Base Entity class is called itself PersistedEntity.

                          • 10. Re: Detach entities - Obtaining a clear pojo

                            Looks good, I've made some slight adjustments, will post code later on tonight

                            regards

                            James

                            • 11. Re: Detach entities - Obtaining a clear pojo
                              alrubinger

                              Looking forward to it.

                              Also crossposted on Hibernate to get input there: http://forum.hibernate.org/viewtopic.php?t=964452&sid=ade6a882a46b1af525a3c4068f298459

                              One thing to look out for:

                              new ArrayList();


                              ...not every Collection is a List. We need some more checking in there to determine if List, Set, Queue...

                              S,
                              ALR

                              • 12. Re: Detach entities - Obtaining a clear pojo

                                Thanks, your code works well, I made a few adjustment though to suit my particular needs.

                                I think it only makes sense to use this function outside the persistance session/transaction. This way you can decide how far you wish to traverse the object graph by populating the data you require within the session, (just as you'd do normally to avoid a LazyInitialisationException). Then when we call the clean function outside the session we traverse until we get a LazyInitializationException, at which point we return null for that field.

                                I put everything within one class as I don't think we need to force people to extend a base class. I check if an object is an entity by looking for the @Entity annotation. If defining entities in XML this wouldn't work, but I don't :)
                                .....alternative implementation could get required object to implement some empty interface and check for that, or alternatively I'm sure there is some way of checking using some Hibernate util function, but I don't know what that is.

                                Its not very well tested but it seems to work for what I need it for at the moment, I'll post bug fixes here as and when they come up.

                                Let me know your thoughts

                                James


                                import java.lang.annotation.Annotation;
                                import java.lang.reflect.InvocationTargetException;
                                import java.util.ArrayList;
                                import java.util.Collection;
                                import java.util.HashMap;
                                import java.util.HashSet;
                                import java.util.List;
                                import java.util.Map;
                                import java.util.Set;
                                import java.util.SortedMap;
                                import java.util.SortedSet;
                                import java.util.TreeMap;
                                import java.util.TreeSet;
                                
                                import javax.persistence.Entity;
                                
                                import org.apache.commons.beanutils.PropertyUtils;
                                import org.hibernate.LazyInitializationException;
                                import org.hibernate.collection.PersistentCollection;
                                
                                public class HibernateUtils {
                                
                                 public static Object clean(Object obj){
                                 return removePersistenceContext(obj, new ArrayList<Integer>());
                                 }
                                
                                 /**
                                 * Returns a Collection of all objects in the specified persistentCollection
                                 * without binding to any persistence context or session.
                                 *
                                 * @param <T>
                                 * @param targetCollection
                                 * @param persistentCollection
                                 * @return
                                 */
                                 public static <T> Collection<T> getCollectionItemsRemovedFromPersistenceContext(
                                 Collection<T> targetCollection, Collection<T> persistentCollection) throws LazyInitializationException {
                                 // If runtime type of persistentCollection is not PersistentCollection,
                                 // take no action
                                 if (!(persistentCollection instanceof PersistentCollection))
                                 return persistentCollection;
                                
                                 // Clear existing target
                                 targetCollection.clear();
                                
                                 // Place all items in persistent collection into target
                                 for (T item : persistentCollection) {
                                 targetCollection.add(item);
                                 }
                                
                                 // Return target
                                 return targetCollection;
                                 }
                                
                                 /**
                                 * Returns a Map of all objects in the specified persistentCollection Map
                                 * without binding to any persistence context or session.
                                 *
                                 * @param <T>
                                 * @param targetCollection
                                 * @param persistentCollection
                                 * @return
                                 */
                                 public static <T, U> Map<T, U> getCollectionItemsRemovedFromPersistenceContext(
                                 Map<T, U> targetMap, Map<T, U> persistentMap) throws LazyInitializationException {
                                 // If runtime type of persistentCollection is not PersistentCollection,
                                 // take no action
                                 if (!(persistentMap instanceof PersistentCollection))
                                 return persistentMap;
                                
                                 //Clear existing target
                                 targetMap.clear();
                                
                                 // Place all items in persistent collection into target
                                 for (T key : persistentMap.keySet()) {
                                 targetMap.put(key, persistentMap.get(key));
                                 }
                                
                                 // Return target
                                 return targetMap;
                                 }
                                
                                
                                
                                 /**
                                 * Checks if the object is an Entity bean by searching for the presence of
                                 * the @Entity tag in the objects class.
                                 * NOTE - If you are not using annotations to define your entity objects this function will not work
                                 * and require an alternative implementation.
                                 *
                                 */
                                 protected static boolean isEntityBean(Object obj){
                                 for(Annotation a : obj.getClass().getAnnotations()){
                                 if(a.annotationType()==Entity.class){
                                 return true;
                                 }
                                 }
                                 return false;
                                 }
                                
                                
                                 /**
                                 * If the specified object's identity hash code is not in the specified
                                 * collection of visited hash codes, removes of all data binding the object
                                 * (and its members) to a specific persistence context, leaving intact only
                                 * model-centric data
                                 *
                                 * @param visitedObjectHashCodes
                                 * @param obj
                                 * @author ALR
                                 */
                                 protected static Object removePersistenceContext(Object obj, Collection<Integer> visitedObjectHashCodes ){
                                 if(obj==null){
                                 return null;
                                 }
                                
                                 if(visitedObjectHashCodes.contains(System.identityHashCode(obj))){
                                 return obj;
                                 }
                                
                                 //Add the object's hash to the Collection of visited hash codes
                                 visitedObjectHashCodes.add(System.identityHashCode(obj));
                                
                                 try{
                                 if(obj instanceof Set){
                                 obj = getCollectionItemsRemovedFromPersistenceContext(new HashSet(), (Set)obj);
                                 }else if(obj instanceof SortedSet){
                                 obj = getCollectionItemsRemovedFromPersistenceContext(new TreeSet(), (SortedSet)obj);
                                 }else if(obj instanceof List){
                                 obj = getCollectionItemsRemovedFromPersistenceContext(new ArrayList(), (List)obj);
                                 }else if(obj instanceof Map){
                                 obj = getCollectionItemsRemovedFromPersistenceContext(new HashMap(), (Map)obj);
                                 }else if(obj instanceof SortedMap){
                                 obj = getCollectionItemsRemovedFromPersistenceContext(new TreeMap(), (SortedMap)obj);
                                 }else if(obj instanceof PersistentCollection){
                                 obj = getCollectionItemsRemovedFromPersistenceContext(new ArrayList(), (Collection)obj);
                                 }
                                 }catch(LazyInitializationException e){
                                 return null;
                                 }
                                
                                 if(!isEntityBean(obj)){
                                 return obj;
                                 }
                                
                                 Map allMembers = getInternalMembers(obj);
                                 for(Object member : allMembers.entrySet()){
                                 Map.Entry m = (Map.Entry)member;
                                 try {
                                 try{
                                 PropertyUtils.setProperty(obj, m.getKey().toString(), removePersistenceContext(m.getValue(), visitedObjectHashCodes));
                                 }catch (LazyInitializationException e){
                                 e.printStackTrace();
                                 PropertyUtils.setProperty(obj, m.getKey().toString(), null);
                                 }
                                 } catch (IllegalAccessException e) {
                                 throw new RuntimeException(e);
                                 } catch (InvocationTargetException e) {
                                 throw new RuntimeException(e);
                                 } catch (NoSuchMethodException e) {
                                 }
                                 }
                                
                                 return obj;
                                 }
                                
                                
                                 /**
                                 * Returns a Map of all internal members of the specified object
                                 *
                                 * @param obj
                                 * The object for which to obtain internal members
                                 *
                                 * @author ALR
                                 * @see http://jakarta.apache.org/commons/beanutils/
                                 */
                                 protected static Map getInternalMembers(Object obj){
                                 try {
                                 Map map = PropertyUtils.describe(obj);
                                 return map;
                                 } catch (IllegalAccessException e) {
                                 throw new RuntimeException(e);
                                 } catch (InvocationTargetException e) {
                                 throw new RuntimeException(e);
                                 } catch (NoSuchMethodException e) {
                                 throw new RuntimeException(e);
                                 }
                                 }
                                
                                }


                                • 13. Re: Detach entities - Obtaining a clear pojo

                                  Actually I think a better implementation is that included below;
                                  it should certain perform much better as it now gets the inner hibernate collection object rather than creating a new object to replicate it.
                                  Note - the package name so we can get the inner collection.

                                  It still needs further testing, as I'm aware of couple of issues which I'll look at tomorrow

                                  package org.hibernate.collection;
                                  
                                  import java.lang.annotation.Annotation;
                                  import java.lang.reflect.InvocationTargetException;
                                  import java.util.ArrayList;
                                  import java.util.Collection;
                                  import java.util.Map;
                                  
                                  import javax.persistence.Entity;
                                  
                                  import org.apache.commons.beanutils.PropertyUtils;
                                  import org.hibernate.LazyInitializationException;
                                  
                                  /**
                                   * Some utils methods to get a complete clean POJO from an entity bean with hibernate specific fields stripped out.
                                   * Note this class is not meant as a way of avoid LazyInitializationException's. Its purpose to strip out data before sending the object
                                   * to remote client so that does not have knowledge of hibernate classes.
                                   * @author ALR 5/9/2006
                                   * @author modified by James Adams 6/9/2006, 7/9/2006
                                   *
                                   */
                                  public class HibernateCleaner {
                                  
                                   public static Object clean(Object obj){
                                   return removePersistenceContext(obj, new ArrayList<Integer>(), 0);
                                   }
                                  
                                   /**
                                   * Checks if the object is an Entity bean by searching for the presence of
                                   * the @Entity tag in the objects class.
                                   * NOTE - If you are not using annotations to define your entity objects this function will not work
                                   * and require an alternative implementation.
                                   * @author James Adams
                                   */
                                   public static boolean isEntityBean(Object obj){
                                   for(Annotation a : obj.getClass().getAnnotations()){
                                   if(a.annotationType()==Entity.class){
                                   return true;
                                   }
                                   }
                                   return false;
                                   }
                                  
                                  
                                   /**
                                   * If the specified object's identity hash code is not in the specified
                                   * collection of visited hash codes, removes of all data binding the object
                                   * (and its members) to a specific persistence context, leaving intact only
                                   * model-centric data
                                   *
                                   * @param visitedObjectHashCodes
                                   * @param obj
                                   * @author ALR
                                   * @author James Adams, modified 6/9/2006,7/9/2006
                                   */
                                   private static Object removePersistenceContext(Object obj, Collection<Integer> visitedObjectHashCodes, int traverseLayer ){
                                   if(obj==null){
                                   return null;
                                   }
                                  
                                   if(visitedObjectHashCodes.contains(System.identityHashCode(obj))){
                                   return obj;
                                   }
                                  
                                   //Add the object's hash to the Collection of visited hash codes
                                   visitedObjectHashCodes.add(System.identityHashCode(obj));
                                  
                                   try{
                                   // If runtime type of persistentCollection is PersistentCollection get the inner collection
                                   if ((obj instanceof PersistentCollection))
                                   obj = getInnerCollection((PersistentCollection)obj);
                                   }catch(LazyInitializationException e){
                                   return null;
                                   }
                                  
                                   if(obj==null){
                                   return null;
                                   }
                                  
                                   //We always check all properties in the top layer object.
                                   if(traverseLayer>0 && !isEntityBean(obj) ){
                                   return obj;
                                   }
                                  
                                   Map allMembers = getInternalMembers(obj);
                                   for(Object member : allMembers.entrySet()){
                                   Map.Entry m = (Map.Entry)member;
                                   try {
                                   try{
                                   PropertyUtils.setProperty(obj, m.getKey().toString(), removePersistenceContext(m.getValue(), visitedObjectHashCodes, traverseLayer+1));
                                   }catch (LazyInitializationException e){
                                   PropertyUtils.setProperty(obj, m.getKey().toString(), null);
                                   }
                                   } catch (IllegalAccessException e) {
                                   throw new RuntimeException(e);
                                   } catch (InvocationTargetException e) {
                                   throw new RuntimeException(e);
                                   } catch (NoSuchMethodException e) {
                                   }
                                   }
                                  
                                   return obj;
                                   }
                                  
                                   /**
                                   * Because this class is in package org.hibernate.collection this method
                                   * allows us to access the inner protected fields of the hibernate collections
                                   * @param obj
                                   * @return The inner collection object of the PersistentCollection parameter
                                   * @author James Adams
                                   */
                                   protected static Object getInnerCollection(PersistentCollection obj){
                                   if(obj instanceof PersistentBag){
                                   return ((PersistentBag)obj).bag;
                                   }else if(obj instanceof PersistentList){
                                   return ((PersistentList)obj).list;
                                   }else if(obj instanceof PersistentSet){
                                   return ((PersistentSet)obj).set;
                                   }else if(obj instanceof PersistentMap){
                                   return ((PersistentMap)obj).map;
                                   }else if(obj instanceof PersistentSortedMap){
                                   return ((PersistentSortedMap)obj).map;
                                   }else if(obj instanceof PersistentSortedSet){
                                   return ((PersistentSortedSet)obj).set;
                                   }else{
                                   return null;
                                   }
                                   }
                                  
                                  
                                   /**
                                   * Returns a Map of all internal members of the specified object
                                   *
                                   * @param obj
                                   * The object for which to obtain internal members
                                   *
                                   * @author ALR
                                   * @see http://jakarta.apache.org/commons/beanutils/
                                   */
                                   private static Map getInternalMembers(Object obj){
                                   try {
                                   Map map = PropertyUtils.describe(obj);
                                   return map;
                                   } catch (IllegalAccessException e) {
                                   throw new RuntimeException(e);
                                   } catch (InvocationTargetException e) {
                                   throw new RuntimeException(e);
                                   } catch (NoSuchMethodException e) {
                                   throw new RuntimeException(e);
                                   }
                                   }
                                  
                                  }
                                  



                                  • 14. Re: Detach entities - Obtaining a clear pojo
                                    alrubinger

                                    Nice - looking forward to checking it out in more detail tomorrow. :)

                                    S,
                                    ALR

                                    1 2 Previous Next