package utils; import java.io.Serializable; import java.lang.reflect.Field; import java.util.Collection; import java.util.Iterator; import org.hibernate.collection.PersistentSet; import org.hibernate.engine.LoadQueryInfluencers; import org.hibernate.engine.SessionImplementor; import org.hibernate.impl.FilterImpl; import org.hibernate.persister.collection.CollectionPersister; import org.hibernate.type.CustomCollectionType; @SuppressWarnings({ "unchecked", "serial" }) public class PersistentOwnedSet extends PersistentSet implements utils.PersistentOwnedCollection { protected int cachedSize = -1; protected org.hibernate.impl.FilterImpl filter = null; protected org.hibernate.impl.FilterImpl filterHint = null; protected utils.AbstractOwnedSetType cachedType = null; protected org.hibernate.impl.FilterImpl restoreFilter = null; public PersistentOwnedSet(SessionImplementor session) { super(session); } public PersistentOwnedSet(SessionImplementor session, java.util.Set set) { super(session, set); } @Override public void beforeInitialize(CollectionPersister persister, int anticipatedSize) { super.beforeInitialize(persister, anticipatedSize); /*java.util.Set<java.util.Map.Entry<String, org.hibernate.impl.FilterImpl>> e = this.getSession().getLoadQueryInfluencers().getEnabledFilters().entrySet(); for(java.util.Map.Entry<String, org.hibernate.impl.FilterImpl> f : e) { String params = ""; for(Object p : f.getValue().getParameters().values()) { if(!"".equals(params)) params += ","; params += p; } try{org.webdsl.logging.Logger.info("enabled: " + f.getKey() + "(" + params + ")");}catch(Exception e2){org.webdsl.logging.Logger.error("EXCEPTION",e2);} } try{throw new Exception("beforeInitialize");}catch(Exception e){org.webdsl.logging.Logger.error("EXCEPTION",e);}*/ // The super method just initialized the set, so we need to pass on the owner if(set != null && set instanceof utils.OwnedSet) { ((utils.OwnedSet)set).setOwner(getOwner()); } } @Override public void beginRead() { setFilter(getAffectingFilter()); /*java.util.Set<java.util.Map.Entry<String, org.hibernate.impl.FilterImpl>> e = this.getSession().getLoadQueryInfluencers().getEnabledFilters().entrySet(); for(java.util.Map.Entry<String, org.hibernate.impl.FilterImpl> f : e) { String params = ""; for(Object p : f.getValue().getParameters().values()) { if(!"".equals(params)) params += ","; params += p; } try{org.webdsl.logging.Logger.info("enabled: " + f.getKey() + "(" + params + ")");}catch(Exception e2){org.webdsl.logging.Logger.error("EXCEPTION",e2);} } try{throw new Exception("beginRead");}catch(Exception e){org.webdsl.logging.Logger.error("EXCEPTION",e);}*/ ((utils.OwnedSet)set).setDoEvents(false); // This prevents events, like inverse updates, while initializing super.beginRead(); } @Override public boolean endRead() { //afterInitialize(); // Needed for DelayedOperations boolean result = super.endRead(); ((utils.OwnedSet)set).setDoEvents(true); // We should resume updating the inverse, because initialization is complete if(this.restoreFilter != null) { // Restore the filter that was enabled before enabling the filter hint SessionImplementor session = getSession(); org.hibernate.engine.LoadQueryInfluencers lqi = session.getLoadQueryInfluencers(); org.hibernate.impl.FilterImpl oldFilter = this.getAffectingFilter(); if(oldFilter != null) lqi.disableFilter(oldFilter.getName()); utils.QueryOptimization.restoreFilter(lqi, this.restoreFilter); this.restoreFilter = null; } return result; } @Override public boolean add(Object value) { if(value == null) { return false; } correctFilter(true); return super.add(value); } @Override public boolean remove(Object value) { if(value == null) { return false; } correctFilter(true); return super.remove(value); } @Override public void setOwner(Object owner) { if(set != null && set instanceof utils.OwnedSet) { ((utils.OwnedSet)set).setOwner(owner); } super.setOwner(owner); } @Override public void initializeFromCache(CollectionPersister persister, Serializable disassembled, Object owner) throws org.hibernate.HibernateException { Serializable[] array = ( Serializable[] ) disassembled; int size = array.length; beforeInitialize( persister, size ); ((utils.OwnedSet)set).setDoEvents(false); for (int i = 0; i < size; i++ ) { Object element = persister.getElementType().assemble( array[i], getSession(), owner ); if ( element != null ) { set.add( element ); } } ((utils.OwnedSet)set).setDoEvents(true); } @Override protected int getCachedSize() { if(cachedSize == -1) return super.getCachedSize(); return cachedSize; } @Override public boolean afterInitialize() { cachedSize = -1; return super.afterInitialize(); } @Override public void postAction() { super.postAction(); cachedSize = -1; } protected void throwLazyInitializationExceptionIfNotConnected() { SessionImplementor session = getSession(); if ( !(session!=null && session.isOpen() && session.getPersistenceContext().containsCollection(this)) ) { throwLazyInitializationException("no session or session was closed"); } if ( !session.isConnected() ) { throwLazyInitializationException("session is disconnected"); } } protected void throwLazyInitializationException(String message) { throw new org.hibernate.LazyInitializationException( "failed to lazily initialize a collection" + ( getRole()==null ? "" : " of role: " + getRole() ) + ", " + message ); } // Ideally this should be implemented by overriding the initialize(boolean), read(), write(), dirty() methods of AbstractPersistentCollection // However, these methods are declared final. So instead we override all methods that call them and include a call to correctFilter(boolean) or // unfiltered(boolean). These methods ensure that the correct filtering is used, for example, that no filter has been applied to dirty collections, // because then filtered elements would be removed from the collection permanently after a flush/commit. public void correctFilter(boolean writing) { if(writing) { unfiltered(writing); } else if(!wasInitialized() && getFilterHint() != null) { // implies getFilter() == null, because !wasInitialized() // Use the filter hint if it is less restrictive than the requested filter // In that case the filter hint warns about the future use of a different filter that requires re-fetching utils.AbstractOwnedSetType type = getOwnedSetType(); FilterImpl newFilter = getAffectingFilter(type); FilterImpl hintFilter = getFilterHint(); if(newFilter != null && !utils.QueryOptimization.equalFilters(hintFilter, newFilter) && type.isFilterCompatible(hintFilter, newFilter)) { this.restoreFilter = newFilter; SessionImplementor session = getSession(); org.hibernate.engine.LoadQueryInfluencers lqi = session.getLoadQueryInfluencers(); lqi.disableFilter(newFilter.getName()); utils.QueryOptimization.restoreFilter(lqi, hintFilter); } } else if(wasInitialized() && getFilter() != null) { // Only need to check the filter if the collection was initialized using a filter utils.AbstractOwnedSetType type = getOwnedSetType(); FilterImpl oldFilter = getFilter(); FilterImpl newFilter = getAffectingFilter(type); if(!type.isFilterCompatible(oldFilter, newFilter)) unfiltered(writing); } } public void unfiltered(boolean writing) { if(wasInitialized() && getFilter() == null) return; // The collection was already initialized without filters if(wasInitialized()) { // Cleaning the filtered collection try{ Field f = org.hibernate.collection.AbstractPersistentCollection.class.getDeclaredField("initialized"); f.setAccessible(true); f.setBoolean(this, Boolean.FALSE); set = null; }catch(Exception e) { org.webdsl.logging.Logger.error("EXCEPTION",e); } } SessionImplementor session = getSession(); // Disable the affecting filter FilterImpl oldFilter = getAffectingFilter(); if(oldFilter != null) { session.getLoadQueryInfluencers().disableFilter(oldFilter.getName()); } // Initialize the collection initialize(writing); session.initializeCollection(this, writing); // Enable the affecting filter again if(oldFilter != null) { utils.QueryOptimization.restoreFilter(session.getLoadQueryInfluencers(), oldFilter); } if(writing) dirty(); } public org.hibernate.impl.FilterImpl getFilter() { return this.filter; } public void setFilter(org.hibernate.impl.FilterImpl filter) { this.filter = filter; this.filterHint = null; // } public org.hibernate.impl.FilterImpl getFilterHint() { return this.filterHint; } public void setFilterHint(org.hibernate.impl.FilterImpl filterHint) { this.filterHint = filterHint; } public utils.AbstractOwnedSetType getOwnedSetType() { if(cachedType != null) { return cachedType; } else { SessionImplementor session = getSession(); CollectionPersister persister = session.getPersistenceContext().getCollectionEntry(this).getLoadedPersister(); if(persister.getCollectionType() instanceof CustomCollectionType && ((CustomCollectionType)persister.getCollectionType()).getUserType() instanceof utils.AbstractOwnedSetType) { cachedType = (utils.AbstractOwnedSetType)((CustomCollectionType)persister.getCollectionType()).getUserType(); return cachedType; } return null; } } protected FilterImpl getAffectingFilter() { return getAffectingFilter(getOwnedSetType()); } protected FilterImpl getAffectingFilter(utils.AbstractOwnedSetType type) { SessionImplementor session = getSession(); FilterImpl filter = null; LoadQueryInfluencers lqi = session.getLoadQueryInfluencers(); if(lqi != null) { java.util.Map filters = lqi.getEnabledFilters(); for(Object entry : filters.entrySet()) { if(!(entry instanceof java.util.Map.Entry)) continue; Object key = ((java.util.Map.Entry)entry).getKey(); Object value = ((java.util.Map.Entry)entry).getValue(); if(key != null && value != null && value instanceof org.hibernate.impl.FilterImpl && type.isAffectedBy(key.toString())) { if(filter == null) { filter = (org.hibernate.impl.FilterImpl) value; } else { throw new java.lang.UnsupportedOperationException("Filters '" + filter.getName() + "' and '" + key.toString() + "' both filter the same collection role" + (getRole() == null ? "." : " (" + getRole() + ").")); } } } } return filter; } @Override public Iterator iterator() { correctFilter(false); read(); return new IteratorProxy(set.iterator()); } @Override public Object[] toArray() { correctFilter(false); return super.toArray(); } @Override public Object[] toArray(Object[] array) { correctFilter(false); return super.toArray(array); } @Override public boolean addAll(Collection coll) { if ( coll.size() > 0 ) { correctFilter(true); return super.addAll(coll); } return false; } @Override public boolean retainAll(Collection coll) { correctFilter(true); return super.retainAll(coll); } @Override public boolean removeAll(Collection coll) { if ( coll.size() > 0 ) { correctFilter(true); return super.removeAll(coll); } return false; } @Override public void clear() { correctFilter(true); super.clear(); } // The following methods always force unfiltered initialization, because they have different semantics if performed on a filtered collection. // For example, existence checks and size operations. @Override public boolean containsAll(Collection coll) { unfiltered(false); return super.containsAll(coll); } @Override public boolean equals(Object other) { unfiltered(false); return super.equals(other); } @Override public int hashCode() { unfiltered(false); return super.hashCode(); } @Override public String toString() { unfiltered(false); return super.toString(); } @Override protected Boolean readIndexExistence(Object index) { unfiltered(false); read(); return super.readIndexExistence(index); } @Override protected Boolean readElementExistence(Object element) { unfiltered(false); read(); return super.readElementExistence(element); } // From AbstractPErsistentCollection, but without extra-lazy check @Override protected boolean readSize() { if (!wasInitialized()) { if ( cachedSize!=-1 && !hasQueuedOperations() ) { return true; } else { // Can be worse for: if(set.length > 0) { "set:" output(set) } /*throwLazyInitializationExceptionIfNotConnected(); SessionImplementor session = getSession(); org.hibernate.engine.CollectionEntry entry = session.getPersistenceContext().getCollectionEntry(this); CollectionPersister persister = entry.getLoadedPersister(); //if ( persister.isExtraLazy() ) { if ( hasQueuedOperations() ) { session.flush(); } cachedSize = persister.getSize( entry.getLoadedKey(), session ); return true;*/ //} } } unfiltered(false); read(); return false; } @Override protected Object readElementByIndex(Object index) { unfiltered(false); read(); return super.readElementByIndex(index); } final class IteratorProxy implements Iterator { private final Iterator iter; IteratorProxy(Iterator iter) { this.iter=iter; } public boolean hasNext() { return iter.hasNext(); } public Object next() { return iter.next(); } public void remove() { correctFilter(true); write(); iter.remove(); } } }