/* * #! * Ontopia Engine * #- * Copyright (C) 2001 - 2013 The Ontopia Project * #- * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * 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 net.ontopia.topicmaps.impl.rdbms; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import net.ontopia.persistence.proxy.CachesIF; import net.ontopia.persistence.proxy.IdentityCollectionWrapper; import net.ontopia.persistence.proxy.IdentityIF; import net.ontopia.persistence.proxy.PersistentIF; import net.ontopia.persistence.proxy.QueryCache; import net.ontopia.persistence.proxy.RDBMSStorage; import net.ontopia.persistence.proxy.TransactionIF; import net.ontopia.persistence.proxy.TransactionalLookupIndexIF; import net.ontopia.topicmaps.core.AssociationRoleIF; import net.ontopia.topicmaps.core.TopicIF; import net.ontopia.topicmaps.core.TopicMapIF; import net.ontopia.topicmaps.impl.utils.TopicMapTransactionIF; import net.ontopia.topicmaps.impl.utils.EventListenerIF; import net.ontopia.topicmaps.impl.utils.EventManagerIF; import net.ontopia.utils.PropertyUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * INTERNAL: */ public class RoleTypeCache { // Define a logging category. static Logger log = LoggerFactory.getLogger(RoleTypeCache.class.getName()); protected TopicMapIF tm; protected TopicMapTransactionIF txn; protected TransactionIF ptxn; protected TransactionalLookupIndexIF rolesByType; protected boolean qlshared; protected Map radd = new HashMap(); protected Map rrem = new HashMap(); public RoleTypeCache(TopicMapTransactionIF txn, EventManagerIF emanager, EventManagerIF otree) { this.txn = txn; this.ptxn = ((RDBMSTopicMapTransaction) txn).getTransaction(); // lookup caches this.tm = txn.getTopicMap(); IdentityIF tmid = ((PersistentIF) tm)._p_getIdentity(); RDBMSStorage storage = (RDBMSStorage) ptxn.getStorageAccess().getStorage(); if (storage.isSharedCache()) { this.rolesByType = new SharedQueryLookup(ptxn.getStorageAccess(), (QueryCache) storage.getHelperObject(CachesIF.QUERY_CACHE_RT1, tmid)); this.qlshared = true; // register event handlers (only needed with shared query cache) otree.addListener(new AssociationRoleAddedHandler(), AssociationRoleIF.EVENT_ADDED); otree.addListener(new AssociationRoleRemovedHandler(), AssociationRoleIF.EVENT_REMOVED); emanager.addListener(new EH01(), AssociationRoleIF.EVENT_SET_TYPE); emanager.addListener(new EH02(), AssociationRoleIF.EVENT_SET_PLAYER); } else { int lrusize = PropertyUtils .getInt( getProperty("net.ontopia.topicmaps.impl.rdbms.Cache.rolesbytype.lru"), 1000); this.rolesByType = new QueryLookup("TopicIF.getRolesByType", ptxn, lrusize); this.qlshared = false; } } // --------------------------------------------------------------------------- // Other // --------------------------------------------------------------------------- protected String getProperty(String name) { return ptxn.getStorageAccess().getProperty(name); } // --------------------------------------------------------------------------- // transaction callbacks // --------------------------------------------------------------------------- public void commit() { if (qlshared) { if (!radd.isEmpty()) { try { rolesByType.removeAll(new ArrayList(radd.keySet())); } finally { radd = new HashMap(); } } if (!rrem.isEmpty()) { try { rolesByType.removeAll(new ArrayList(rrem.keySet())); } finally { rrem = new HashMap(); } } } rolesByType.commit(); } public void abort() { if (qlshared) { radd.clear(); rrem.clear(); } rolesByType.abort(); } // --------------------------------------------------------------------------- // Lookup methods // --------------------------------------------------------------------------- // Delegated to by TopicIF.getRolesByType(TopicIF roletype) public Collection getRolesByType(TopicIF player, TopicIF roletype) { // IMPORTANT: this method will *never* be called if the topic's // roles property is dirty. Thus there is no need to sync the // query result with the txn changes, i.e. the radd and rrem maps. // construct query key ParameterArray key = new ParameterArray(new Object[] { i(tm), i(player), i(roletype) }); Collection result = (Collection) rolesByType.get(key); return new IdentityCollectionWrapper(ptxn, result); } // ----------------------------------------------------------------------------- // Event handlers // ----------------------------------------------------------------------------- protected Object i(Object value) { if (value instanceof PersistentIF) return ((PersistentIF) value)._p_getIdentity(); else return value; } protected void addEntry(ParameterArray key, AssociationRoleIF added) { // + added Collection avals = (Collection) radd.get(key); if (avals == null) { avals = new HashSet(); radd.put(key, avals); } avals.add(added); // - removed Collection rvals = (Collection) rrem.get(key); if (rvals != null) rvals.remove(added); } protected void removeEntry(ParameterArray key, AssociationRoleIF removed) { // + removed Collection rvals = (Collection) rrem.get(key); if (rvals == null) { rvals = new HashSet(); rrem.put(key, rvals); } rvals.add(removed); // - added Collection avals = (Collection) radd.get(key); if (avals != null) avals.remove(removed); } /** * EventHandler: AssociationRoleIF.added */ class AssociationRoleAddedHandler implements EventListenerIF { public void processEvent(Object object, String event, Object new_value, Object old_value) { AssociationRoleIF added = (AssociationRoleIF) new_value; // ignore event if player is null TopicIF player = added.getPlayer(); if (player == null) return; // register entry addEntry(new ParameterArray(new Object[] { i(tm), i(player), i(added.getType()) }), added); } } /** * EventHandler: AssociationRoleIF.removed */ class AssociationRoleRemovedHandler implements EventListenerIF { public void processEvent(Object object, String event, Object new_value, Object old_value) { AssociationRoleIF removed = (AssociationRoleIF) old_value; // ignore event if player is null TopicIF player = removed.getPlayer(); if (player == null) return; // unregister entry removeEntry(new ParameterArray(new Object[] { i(tm), i(player), i(removed.getType()) }), removed); } } /** * EventHandler: AssociationRoleIF.setType */ class EH01 implements EventListenerIF { public void processEvent(Object object, String event, Object new_value, Object old_value) { AssociationRoleIF arole = (AssociationRoleIF) object; // ignore event if player is null TopicIF player = arole.getPlayer(); if (player == null) return; // unregister old entry removeEntry(new ParameterArray(new Object[] { i(tm), i(player), i(old_value) }), arole); // register new entry addEntry(new ParameterArray(new Object[] { i(tm), i(player), i(new_value) }), arole); } } /** * EventHandler: AssociationRoleIF.setPlayer */ class EH02 implements EventListenerIF { public void processEvent(Object object, String event, Object new_value, Object old_value) { AssociationRoleIF arole = (AssociationRoleIF) object; // unregister old entry if (old_value != null) removeEntry(new ParameterArray(new Object[] { i(tm), i(old_value), i(arole.getType()) }), arole); // register new entry if (new_value != null) addEntry(new ParameterArray(new Object[] { i(tm), i(new_value), i(arole.getType()) }), arole); } } }