/** * <copyright> * * Copyright (c) 2005, 2006, 2007, 2008 Springsite BV (The Netherlands) and others * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Martin Taal * </copyright> * * $Id: HibernatePersistableEMap.java,v 1.8 2008/06/29 14:24:25 mtaal Exp $ */ package org.eclipse.emf.teneo.hibernate.mapping.elist; import java.lang.reflect.Field; import java.util.List; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.eclipse.emf.common.util.BasicEMap; import org.eclipse.emf.common.util.EList; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.EReference; import org.eclipse.emf.ecore.EStructuralFeature; import org.eclipse.emf.ecore.InternalEObject; import org.eclipse.emf.ecore.resource.Resource; import org.eclipse.emf.teneo.extension.ExtensionPoint; import org.eclipse.emf.teneo.hibernate.SessionWrapper; import org.eclipse.emf.teneo.hibernate.resource.HbResource; import org.eclipse.emf.teneo.mapping.elist.PersistableDelegateList; import org.eclipse.emf.teneo.mapping.elist.PersistableEList; import org.eclipse.emf.teneo.mapping.elist.PersistableEMap; import org.eclipse.emf.teneo.resource.StoreResource; import org.hibernate.Session; import org.hibernate.collection.AbstractPersistentCollection; import org.hibernate.collection.PersistentCollection; /** * Implements the hibernate persistable emap. Note an emap is not loaded lazily! * * @author <a href="mailto:mtaal@elver.org">Martin Taal</a> * @version $Revision: 1.8 $ */ public class HibernatePersistableEMap<K, V> extends PersistableEMap<K, V> implements ExtensionPoint { /** * Serial Version ID */ private static final long serialVersionUID = -4553160393592497834L; /** The logger */ private static Log log = LogFactory.getLog(HibernatePersistableEMap.class); /** Constructor */ public HibernatePersistableEMap(InternalEObject owner, EReference eref, List<Entry<K, V>> list) { super(eref.getEReferenceType(), owner, eref, list); } /** Do the actual load can be overridden */ @Override @SuppressWarnings("unchecked") protected void doLoad() { SessionWrapper sessionWrapper = null; boolean controlsTransaction = false; boolean err = true; Resource res = null; final List<?> delegate = ((HibernatePersistableEList) delegateEList).getDelegate(); try { res = getEObject().eResource(); if (res != null && res instanceof HbResource) { sessionWrapper = ((HbResource) res).getSessionWrapper(); if (res.isLoaded()) // resource is loaded reopen transaction { // if the delegate is already loaded then no transaction is // required final boolean isDelegateLoaded = delegate instanceof AbstractPersistentCollection && ((AbstractPersistentCollection) delegate).wasInitialized(); if (!isDelegateLoaded && !sessionWrapper.isTransactionActive()) { log.debug("Reconnecting session to read a lazy collection, elist: " + logString); controlsTransaction = true; sessionWrapper.beginTransaction(); sessionWrapper.setFlushModeManual(); } else { log.debug("Delegate loaded or resource session is still active, using it"); } } else { log.debug("EMap uses session from resource, " + logString); } } else { log.debug("EMap is not loaded in session context"); } if (controlsTransaction) { assert (res instanceof HbResource); ((StoreResource) res).setIsLoading(true); } try { Object[] objs = delegate.toArray(); // this forces the load // add the new objects to the resource so they are tracked if (res != null && res instanceof StoreResource) { // attach the new contained objects so that they are adapted // when required for (Object element : objs) { if (element instanceof EObject) { ((StoreResource) res).addToContentOrAttach((InternalEObject) element, (EReference) getEStructuralFeature()); } } } log.debug("Loaded " + objs.length + " from backend store for " + logString); } finally { if (controlsTransaction) { ((StoreResource) res).setIsLoading(false); } } err = false; } finally { if (controlsTransaction) { if (err) { sessionWrapper.rollbackTransaction(); sessionWrapper.restorePreviousFlushMode(); } else { // a bit rough but delete from the persitence context // otherwise // hibernate will think that this collection is not attached // to anything and // will delete me // getSession().getPersistenceContext().getCollectionEntries().remove(this); sessionWrapper.commitTransaction(); sessionWrapper.restorePreviousFlushMode(); } ((HbResource) res).returnSessionWrapper(sessionWrapper); } } log.debug("Finished loading elist " + logString); } /** * Overridden because of access to size attribute. This version will try to read the collection * size without lading it if it is lazy loaded */ @Override public int size() { if (size != 0) { return size; } // if we are not loaded yet, we return the size of the buffered lazy // load delegate if (delegateEList instanceof PersistableEList && ((PersistableEList<?>) delegateEList).getDelegate() instanceof AbstractPersistentCollection) { try { // here is a neat trick. we use reflection to get the // session of the persistanMap. Field field = AbstractPersistentCollection.class.getDeclaredField("session"); field.setAccessible(true); Session s = (Session) field.get(((PersistableEList<?>) delegateEList).getDelegate()); // now that we have the session, we can query the size of // the list without loading it s.flush(); size = ((Long) s.createFilter(((PersistableEList<?>) delegateEList).getDelegate(), "select count(*)") .list().get(0)).intValue(); return size; } catch (Throwable t) { // ignore on purpose, let the call to super handle it } } // didnt work, so we simply call the parent version return super.size(); } /** Needs to be implemented by concrete subclass */ @Override protected EList<BasicEMap.Entry<K, V>> createDelegateEList(InternalEObject owner, EStructuralFeature feature, List<BasicEMap.Entry<K, V>> delegateORMList) { return new HibernatePersistableEList<Entry<K, V>>(owner, feature, delegateORMList) { private static final long serialVersionUID = 1L; @Override protected void didAdd(int index, Entry<K, V> newObject) { doPut(newObject); } @Override protected void didSet(int index, Entry<K, V> newObject, Entry<K, V> oldObject) { didRemove(index, oldObject); didAdd(index, newObject); } @Override protected void didRemove(int index, Entry<K, V> oldObject) { HibernatePersistableEMap.this.doRemove(oldObject); } @Override protected void didClear(int size, Object[] oldObjects) { doClear(); } @Override protected void didMove(int index, Entry<K, V> movedObject, int oldIndex) { HibernatePersistableEMap.this.doMove(movedObject); } }; } /** If the delegate has been initialized */ public boolean isInitialized() { return ((PersistentCollection) getDelegate()).wasInitialized(); } /** Return the orm backing list */ @Override public Object getDelegate() { return ((PersistableDelegateList<?>) delegateEList).getDelegate(); } }