/* * Copyright 2015 Red Hat, Inc. and/or its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.drools.core.common; import org.drools.core.RuleBaseConfiguration; import org.drools.core.factmodel.traits.CoreWrapper; import org.drools.core.util.HashTableIterator; import org.drools.core.util.JavaIteratorAdapter; import org.drools.core.util.ObjectHashMap; import org.kie.api.runtime.ClassObjectFilter; import org.kie.api.runtime.ObjectFilter; import java.io.Externalizable; import java.io.IOException; import java.io.ObjectInput; import java.io.ObjectOutput; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.concurrent.locks.Lock; public class ClassAwareObjectStore implements Externalizable, ObjectStore { private Lock lock; private Map<String, SingleClassStore> storesMap = new HashMap<String, SingleClassStore>(); private List<ConcreteClassStore> concreteStores = new ArrayList<ConcreteClassStore>(); private ObjectHashMap equalityMap; private boolean isEqualityBehaviour; private int size; public ClassAwareObjectStore() { } public ClassAwareObjectStore(RuleBaseConfiguration conf, Lock lock) { this(conf.getAssertBehaviour(), lock); } public ClassAwareObjectStore( RuleBaseConfiguration.AssertBehaviour assertBehaviour, Lock lock ) { this.lock = lock; this.isEqualityBehaviour = RuleBaseConfiguration.AssertBehaviour.EQUALITY.equals(assertBehaviour); if (isEqualityBehaviour) { this.equalityMap = new ObjectHashMap(); this.equalityMap.setComparator( new EqualityAssertMapComparator() ); } } @Override public void writeExternal(ObjectOutput out) throws IOException { out.writeObject(storesMap); out.writeObject(concreteStores); out.writeObject(equalityMap); out.writeInt(size); out.writeBoolean(isEqualityBehaviour); out.writeObject(lock); } @Override public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { storesMap = (Map<String, SingleClassStore>) in.readObject(); concreteStores = (List<ConcreteClassStore>) in.readObject(); equalityMap = (ObjectHashMap) in.readObject(); size = in.readInt(); isEqualityBehaviour = in.readBoolean(); lock = (Lock)in.readObject(); } @Override public int size() { return size; } @Override public boolean isEmpty() { return size == 0; } @Override public void clear() { storesMap.clear(); concreteStores.clear(); if (isEqualityBehaviour) { equalityMap.clear(); } size = 0; } @Override public Object getObjectForHandle(InternalFactHandle handle) { try { this.lock.lock(); InternalFactHandle reconnectedHandle = reconnect(handle); return reconnectedHandle != null ? reconnectedHandle.getObject() : null; } finally { this.lock.unlock(); } } @Override public InternalFactHandle reconnect(InternalFactHandle handle) { if (handle == null) { return null; } String handleClass = handle.getObjectClassName(); if (handleClass != null) { SingleClassStore store = getClassStore(handleClass); if (store == null || !store.isConcrete()) { return null; } return handle.isNegated() ? (InternalFactHandle) ((ConcreteClassStore) store).getNegMap().get(handle) : (InternalFactHandle) ((ConcreteClassStore) store).getIdentityMap().get(handle); } if (isEqualityBehaviour) { return (InternalFactHandle) equalityMap.get(handle); } for (ConcreteClassStore stores : concreteStores) { Object reconnectedHandle = stores.getAssertMap().get(handle); if (reconnectedHandle != null) { return (InternalFactHandle) reconnectedHandle; } } return null; } @Override public InternalFactHandle getHandleForObject(Object object) { if ( object == null ) { return null; } return isEqualityBehaviour ? (InternalFactHandle) equalityMap.get(object) : (InternalFactHandle) getOrCreateConcreteClassStore(object).getAssertMap().get(object); } @Override public InternalFactHandle getHandleForObjectIdentity(Object object) { return (InternalFactHandle) getOrCreateConcreteClassStore(object).getIdentityMap().get(object); } @Override public void updateHandle(InternalFactHandle handle, Object object) { removeHandle(handle); handle.setObject(object); addHandle(handle, object); } @Override public void addHandle(InternalFactHandle handle, Object object) { if ( getOrCreateConcreteClassStore(object).addHandle(handle, object) ) { size++; } } @Override public void removeHandle(InternalFactHandle handle) { if ( getOrCreateConcreteClassStore(handle.getObject()).removeHandle(handle) != null ) { size--; } } @Override public Iterator<Object> iterateObjects() { return new CompositeObjectIterator(concreteStores, true); } public Iterator<Object> iterateObjects(Class<?> clazz) { return getOrCreateClassStore(clazz).objectsIterator(true); } @Override public Iterator<Object> iterateObjects(ObjectFilter filter) { if (filter instanceof ClassObjectFilter) { return getOrCreateClassStore(((ClassObjectFilter) filter).getFilteredClass()).objectsIterator(true); } return new CompositeObjectIterator(concreteStores, true, filter); } @Override public Iterator<InternalFactHandle> iterateFactHandles() { return new CompositeFactHandleIterator(concreteStores, true); } public Iterator<InternalFactHandle> iterateFactHandles(Class<?> clazz) { return getOrCreateClassStore(clazz).factHandlesIterator(true); } @Override public Iterator<InternalFactHandle> iterateFactHandles(ObjectFilter filter) { if (filter instanceof ClassObjectFilter) { return getOrCreateClassStore(((ClassObjectFilter) filter).getFilteredClass()).factHandlesIterator(true); } return new CompositeFactHandleIterator(concreteStores, true, filter); } @Override public Iterator<Object> iterateNegObjects(ObjectFilter filter) { if (filter instanceof ClassObjectFilter) { return getOrCreateClassStore(((ClassObjectFilter) filter).getFilteredClass()).objectsIterator(false); } return new CompositeObjectIterator(concreteStores, false, filter); } @Override public Iterator<InternalFactHandle> iterateNegFactHandles(ObjectFilter filter) { if (filter instanceof ClassObjectFilter) { return getOrCreateClassStore(((ClassObjectFilter) filter).getFilteredClass()).factHandlesIterator(false); } return new CompositeFactHandleIterator(concreteStores, false, filter); } // ///////////////////// // /// Internal Store // ///////////////////// private SingleClassStore getClassStore(String className) { return storesMap.get(className); } public static Class<?> getActualClass(Object object) { return object instanceof CoreWrapper ? ((CoreWrapper)object).getCore().getClass() : object.getClass(); } public SingleClassStore getOrCreateClassStore(Class<?> clazz) { SingleClassStore store = storesMap.get(clazz.getName()); if (store == null) { store = createClassStoreAndAddConcreteSubStores(clazz); storesMap.put(clazz.getName(), store); } return store; } public boolean clearClassStore(Class<?> clazz) { return storesMap.remove( clazz.getName() ) != null; } private ConcreteClassStore getOrCreateConcreteClassStore(Object object) { return getOrCreateConcreteClassStore(getActualClass(object)); } private ConcreteClassStore getOrCreateConcreteClassStore(Class<?> clazz) { SingleClassStore existingStore = getOrCreateClassStore(clazz); if (existingStore.isConcrete()) { return (ConcreteClassStore) existingStore; } else { // The existing store was abstract so has to be converted in a concrete one return makeStoreConcrete(existingStore); } } private ConcreteClassStore makeStoreConcrete(SingleClassStore storeToMakeConcrete) { ConcreteClassStore store = storeToMakeConcrete.makeConcrete(); Class<?> storedClass = storeToMakeConcrete.getStoredClass(); for (SingleClassStore classStore : storesMap.values()) { if (classStore.getStoredClass().isAssignableFrom(storedClass)) { classStore.addConcreteStore(store); } } concreteStores.add(store); return store; } private SingleClassStore createClassStoreAndAddConcreteSubStores(Class<?> clazz) { SingleClassStore newStore = isEqualityBehaviour ? new ConcreteEqualityClassStore(clazz, equalityMap) : new ConcreteIdentityClassStore(clazz); for (SingleClassStore classStore : storesMap.values()) { if (classStore.isConcrete() && clazz.isAssignableFrom(classStore.getStoredClass())) { newStore.addConcreteStore(((ConcreteClassStore) classStore)); } } return newStore; } public interface SingleClassStore extends Externalizable { Class<?> getStoredClass(); void addConcreteStore(ConcreteClassStore store); Iterator<Object> objectsIterator(boolean assrt); Iterator<InternalFactHandle> factHandlesIterator(boolean assrt); boolean isConcrete(); ConcreteClassStore makeConcrete(); } private abstract static class AbstractClassStore implements SingleClassStore { private Class<?> storedClass; private List<ConcreteClassStore> concreteStores = new ArrayList<ConcreteClassStore>(); public AbstractClassStore() { } private AbstractClassStore(Class<?> storedClass) { this.storedClass = storedClass; } public void addConcreteStore(ConcreteClassStore store) { concreteStores.add(store); } public Iterator<Object> objectsIterator(boolean assrt) { return new CompositeObjectIterator(concreteStores, assrt); } public Iterator<InternalFactHandle> factHandlesIterator(boolean assrt) { return new CompositeFactHandleIterator(concreteStores, assrt); } @Override public Class<?> getStoredClass() { return storedClass; } @Override public void writeExternal(ObjectOutput out) throws IOException { out.writeObject(storedClass); out.writeObject(concreteStores); } @Override public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { storedClass = (Class<?>)in.readObject(); concreteStores = (List<ConcreteClassStore>)in.readObject(); } @Override public String toString() { return "Object store for class: " + storedClass; } } private interface ConcreteClassStore extends SingleClassStore { boolean addHandle(InternalFactHandle handle, Object object); InternalFactHandle removeHandle(InternalFactHandle handle); ObjectHashMap getAssertMap(); ObjectHashMap getIdentityMap(); ObjectHashMap getNegMap(); } private static class ConcreteIdentityClassStore extends AbstractClassStore implements ConcreteClassStore { private ObjectHashMap identityMap; private ObjectHashMap negMap; public ConcreteIdentityClassStore() { } public ConcreteIdentityClassStore(Class<?> storedClass) { super(storedClass); } @Override public boolean addHandle(InternalFactHandle handle, Object object) { if ( handle.isNegated() ) { negMap.put(handle, handle, false); return false; } return identityMap.put(handle, handle, false) == null; } @Override public InternalFactHandle removeHandle(InternalFactHandle handle) { if ( handle.isNegated() ) { negMap.remove(handle); return null; } return (InternalFactHandle) identityMap.remove(handle); } @Override public ObjectHashMap getAssertMap() { return identityMap; } @Override public ObjectHashMap getNegMap() { return negMap; } @Override public ObjectHashMap getIdentityMap() { return identityMap; } @Override public void writeExternal(ObjectOutput out) throws IOException { super.writeExternal(out); out.writeObject(identityMap); out.writeObject(negMap); } @Override public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { super.readExternal(in); identityMap = (ObjectHashMap)in.readObject(); negMap = (ObjectHashMap)in.readObject(); } @Override public boolean isConcrete() { return identityMap != null; } @Override public ConcreteClassStore makeConcrete() { negMap = new ObjectHashMap(); identityMap = new ObjectHashMap(); identityMap.setComparator( new IdentityAssertMapComparator() ); return this; } } private static class ConcreteEqualityClassStore extends ConcreteIdentityClassStore { private ObjectHashMap equalityMap; public ConcreteEqualityClassStore() { } public ConcreteEqualityClassStore(Class<?> storedClass, ObjectHashMap equalityMap) { super(storedClass); this.equalityMap = equalityMap; } @Override public boolean addHandle(InternalFactHandle handle, Object object) { boolean isNew = super.addHandle(handle, object); equalityMap.put(handle, handle, false); return isNew; } @Override public InternalFactHandle removeHandle(InternalFactHandle handle) { InternalFactHandle removedHandle = super.removeHandle(handle); equalityMap.remove(handle); return removedHandle; } @Override public ObjectHashMap getAssertMap() { return equalityMap; } @Override public void writeExternal(ObjectOutput out) throws IOException { super.writeExternal(out); out.writeObject(equalityMap); } @Override public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { super.readExternal(in); equalityMap = (ObjectHashMap)in.readObject(); } } private static abstract class AbstractCompositeIterator<T> implements Iterator<T> { protected final Iterator<ConcreteClassStore> stores; protected final boolean assrt; protected final ObjectFilter filter; protected Iterator<T> currentIterator; protected T currentNext; private AbstractCompositeIterator(Iterable<ConcreteClassStore> stores, boolean assrt) { this(stores, assrt, null); } private AbstractCompositeIterator(Iterable<ConcreteClassStore> stores, boolean assrt, ObjectFilter filter) { this.stores = stores.iterator(); this.assrt = assrt; this.filter = filter; fetchNext(); } private void fetchNext() { while (currentNext == null) { nextIterator(); if (currentIterator == null) { break; } currentNext = currentIterator.next(); if (filter != null && !accept()) { currentNext = null; } } } private void nextIterator() { while (currentIterator == null || !currentIterator.hasNext()) { if (stores.hasNext()) { fetchNextIterator(); } else { currentIterator = null; break; } } } protected abstract void fetchNextIterator(); protected abstract boolean accept(); @Override public boolean hasNext() { return currentNext != null; } @Override public T next() { T next = currentNext; currentNext = null; fetchNext(); return next; } @Override public void remove() { throw new UnsupportedOperationException(); } } private static class CompositeObjectIterator extends AbstractCompositeIterator<Object> { private CompositeObjectIterator(Iterable<ConcreteClassStore> stores, boolean assrt) { super(stores, assrt); } private CompositeObjectIterator(Iterable<ConcreteClassStore> stores, boolean assrt, ObjectFilter filter) { super(stores, assrt, filter); } @Override protected void fetchNextIterator() { HashTableIterator iterator = assrt ? new HashTableIterator( stores.next().getIdentityMap() ) : new HashTableIterator( stores.next().getNegMap() ); iterator.reset(); currentIterator = new JavaIteratorAdapter<Object>( iterator, JavaIteratorAdapter.OBJECT ); } @Override protected boolean accept() { return filter.accept(currentNext); } } private static class CompositeFactHandleIterator extends AbstractCompositeIterator<InternalFactHandle> { private CompositeFactHandleIterator(Iterable<ConcreteClassStore> stores, boolean assrt) { super(stores, assrt); } private CompositeFactHandleIterator(Iterable<ConcreteClassStore> stores, boolean assrt, ObjectFilter filter) { super(stores, assrt, filter); } @Override protected void fetchNextIterator() { HashTableIterator iterator = assrt ? new HashTableIterator( stores.next().getIdentityMap() ) : new HashTableIterator( stores.next().getNegMap() ); iterator.reset(); currentIterator = new JavaIteratorAdapter<InternalFactHandle>( iterator, JavaIteratorAdapter.FACT_HANDLE ); } @Override protected boolean accept() { return filter.accept(currentNext.getObject()); } } }