/*
* This file is part of the HyperGraphDB source distribution. This is copyrighted
* software. For permitted uses, licensing options and redistribution, please see
* the LicensingInformation file at the root level of the distribution.
*
* Copyright (c) 2005-2010 Kobrix Software, Inc. All rights reserved.
*/
package org.hypergraphdb;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import org.hypergraphdb.HGQuery.hg;
import org.hypergraphdb.atom.HGAtomRef;
import org.hypergraphdb.atom.HGStats;
import org.hypergraphdb.cache.HGCache;
import org.hypergraphdb.cache.MRUCache;
import org.hypergraphdb.cache.WeakRefAtomCache;
import org.hypergraphdb.event.HGAtomAccessedEvent;
import org.hypergraphdb.event.HGAtomAddedEvent;
import org.hypergraphdb.event.HGAtomLoadedEvent;
import org.hypergraphdb.event.HGAtomProposeEvent;
import org.hypergraphdb.event.HGAtomRemoveRequestEvent;
import org.hypergraphdb.event.HGAtomRemovedEvent;
import org.hypergraphdb.event.HGAtomReplaceRequestEvent;
import org.hypergraphdb.event.HGAtomReplacedEvent;
import org.hypergraphdb.event.HGClosingEvent;
import org.hypergraphdb.event.HGEvent;
import org.hypergraphdb.event.HGEventManager;
import org.hypergraphdb.event.HGListener;
import org.hypergraphdb.event.HGListenerAtom;
import org.hypergraphdb.event.HGOpenedEvent;
import org.hypergraphdb.handle.HGLiveHandle;
import org.hypergraphdb.handle.HGManagedLiveHandle;
import org.hypergraphdb.maintenance.MaintenanceException;
import org.hypergraphdb.maintenance.MaintenanceOperation;
import org.hypergraphdb.query.AtomTypeCondition;
import org.hypergraphdb.query.HGQueryCondition;
import org.hypergraphdb.storage.BAtoHandle;
import org.hypergraphdb.transaction.HGTransactionConfig;
import org.hypergraphdb.transaction.HGTransactionManager;
import org.hypergraphdb.type.AtomRefType;
import org.hypergraphdb.type.HGAtomType;
import org.hypergraphdb.type.TypeUtils;
import org.hypergraphdb.util.HGLogger;
import org.hypergraphdb.util.Pair;
/**
* <p>
* This is the main class representing a HyperGraph database. A HyperGraph database resides
* in a dedicated folder and can be accessed by more than one process. Also, a single process
* may open several databases at once.
* </p>
*
* <p>
* Because HyperGraphDB is primarily an embedded database, it does not rely on a client-server
* model and therefore there is no notion of a connection. The database is accessed by
* instantiating an instance of this class and then using its methods. An instance of this
* class is essentially a connection to the database and it must be always properly closed
* before application shutdown. To close the database, call the <code>close</code> method.
* When the application exits abruptly, without having a chance to close, it may fail to
* open subsequently without running a recovery process. The recovery process depends on the
* underlying storage implementation. The default storage implementation performs a light recovery
* process every time you open a database. If a full recovery is needed, please consult the
* documentation of the storage implementation.
* </p>
*
* <p>
* This class encapsulates and offers access to all important HyperGraphDB top-level objects such as
* its associated {@link HGTransactionManager}, {@link HGTypeSystem},
* {@link HGEventManager} and the low-level {@link HGStore}.
* </p>
*
* <p>
* Each datum in a HyperGraph database is called an <code>atom</code>. Atoms are either
* arbitrary plain objects or instances of {@link HGLink}. Using this class, you may:
*
* <ul>
* <li>Add new atoms with the <code>add</code> family of methods.</li>
* <li>Remove existing atoms with the <code>remove</code> method.</li>
* <li>Change the value of an atom while preserving its HyperGraph handle (i.e. its
* <em>database id</em>, if you will) with the <code>replace</code> family of methods.</li>
* <li>Add new atoms with existing handles with the <code>define</code> family of methods.
* This is useful, for example, when moving atoms from one HyperGraphDB to another.</li>
* </ul>
* </p>
*
* <p>
* An important aspect of HyperGraph atoms are their associated system flags. More information
* about what those are and how to use them can be found in the {@link HGSystemFlags} class.
* </p>
*
* <p>
* For aggregate structures such as Java beans, it is often useful to create indices
* to speed up searching by certain properties. You can manipulate indices with the
* {@link HGIndexManager} associated with a HyperGraph instance. Call <code>getIndexManager</code>
* to get the index manager.
* </p>
*
* @author Borislav Iordanov
*/
@SuppressWarnings("unchecked")
public /*final*/ class HyperGraph implements HyperNode
{
public static final HGHandle [] EMPTY_HANDLE_SET = new HGHandle[0];
public static final LazyRef<HGHandle[]> EMPTY_HANDLE_SET_REF = new ReadyRef<HGHandle[]>(new HGHandle[0]);
public static final HGPersistentHandle [] EMPTY_PERSISTENT_HANDLE_SET = new HGPersistentHandle[0];
/**
* An object ID for locking the incidence set cache within a DB transaction.
*/
//private static final byte [] INCIDENCE_CACHE_ID = HGHandleFactory.makeHandle("128d0be0-b062-11dd-b416-0002a5d5c51b").toByteArray();
/**
* The resource name of the default types configuration file.
*/
// public static final String TYPES_CONFIG_FILE = "/org/hypergraphdb/types";
/**
* The name of the main by-type atom index.
*/
public static final String TYPES_INDEX_NAME = "HGATOMTYPE";
/**
* The name of the main by-value atom index.
*/
public static final String VALUES_INDEX_NAME = "HGATOMVALUE";
/**
* The name of the atom attributes DB.
*/
public static final String SA_DB_NAME = "HGSYSATTRIBS";
/**
* The location (full directory path on disk) for this HyperGraph database.
*/
private String location = null;
/**
* Is a database currently open?
*/
private volatile boolean is_open = false;
/**
* The hypergraph store. Manages low-level persistence operations and
* indexing.
*/
private HGStore store = null;
/**
* An index manager for user created indices.
*/
private HGIndexManager idx_manager = null;
/**
* The hypergraph typing manager. Integrates tightly with a HyperGraph
* instance.
*/
private HGTypeSystem typeSystem = null;
/**
* The HyperGraph atom cache.
*/
HGAtomCache cache = null;
/**
* The event manager handles event listener registration and even dispatching.
*/
HGEventManager eventManager = null;
/**
* A logger for HyperGraph.
*/
final HGLogger logger = new HGLogger();
//-------------------------------------------------------------------------
// DEFAULT INDICES
//-------------------------------------------------------------------------
/**
* Each atom is indexed by its type handle.
*/
HGIndex<HGPersistentHandle, HGPersistentHandle> indexByType = null;
/**
* Each atom is indexed by its value handle as well.
*/
HGIndex<HGPersistentHandle, HGPersistentHandle> indexByValue = null;
/**
* An index holding system attributes for atoms with different than default
* system flags.
*/
HGIndex<HGPersistentHandle, HGAtomAttrib> systemAttributesDB = null;
HGHandle statsHandle = null;
HGStats stats = new HGStats();
HGConfiguration config = new HGConfiguration();
public HyperGraph()
{
}
/**
* <p>Construct a run-time instance of a hypergraph database and open the database
* at the specified location.</p>
*
* @param location The full path of the directory where the database resides.
*/
public HyperGraph(String location)
{
open(location);
}
/**
* <p>Open the database at the specified location.</p>
*
* @param location The full path of the directory where the database resides.
*/
public synchronized void open(String location)
{
if (this.location != null && this.location.length() > 0)
HGEnvironment.remove(this.location);
this.location = location;
open();
HGEnvironment.set(this.location, this);
}
/**
* <p>Return the physical location of this HyperGraph database. The location is
* set either at construction or by a call to the <code>open(String location)</code>
* method.
* </p>
*/
public String getLocation()
{
return location;
}
/**
* <p>Return the set of configuration parameters for this HyperGraphDB instance.</p>
*/
public HGConfiguration getConfig()
{
return config;
}
/**
* <p>Specify configuration parameters for this HyperGraphDB instance. If the
* instance is already opened, most parameters will not take effect.
* </p>
*
* @param config A <code>HGConfiguration</code> holding the parameters. Must not be
* <code>null</code> - it is ignored if it is.
*/
public void setConfig(HGConfiguration config)
{
if (config == null)
return;
this.config = config;
}
/**
* <p>Return the {@link HGHandleFactory} implementation associated with this
* HyperGraph instance. The handle factory is responsible for managing the
* representation of persistent handles - generating new ones, converting to and
* from <code>byte[]</code> as well as the several predefined handles with special
* semantics.</p>
*/
public HGHandleFactory getHandleFactory()
{
return config.getHandleFactory();
}
/**
* <p>Return the {@link HGLogger} associated with this graph.</p>
*/
public HGLogger getLogger()
{
return logger;
}
/**
* <p>Execute all currently scheduled maintenance operations. Note that calling this
* method can potentially take a long time. Also, it is imperative that no other
* thread accesses this HyperGraphDB instance while the maintenance operations are
* being executed.
* </p>
* <p>
* This method is invoked by default when a HyperGraphDB instance is open, unless
* the <code>HGConfiguration</code> specifies that maintenance operations must be
* canceled or skipped. The method is made public for convenience to applications that
* "know" what they are doing - the same effect can be achieved by closing and re-opening
* the HyperGraphDB instance, but with the side effect of loosing all cached information.
* </p>
*/
public void runMaintenance()
{
List<MaintenanceOperation> L = HGQuery.hg.getAll(this, HGQuery.hg.typePlus(MaintenanceOperation.class));
for (MaintenanceOperation op : L)
try
{
op.execute(this);
remove(getHandle(op));
}
catch (MaintenanceException ex)
{
ex.printStackTrace(System.err);
if (ex.isFatal())
break;
}
}
/**
* <p>Open the database if it's not already open.</p>
*/
private synchronized void open()
{
if (is_open)
close();
is_open = true;
try
{
store = new HGStore(location, config);
store.getTransactionManager().setHyperGraph(this);
eventManager = config.getEventManager();
eventManager.setHyperGraph(this);
cache = new WeakRefAtomCache(this);
cache.setHyperGraph(this);
HGCache<HGPersistentHandle, IncidenceSet> incidenceCache =
new MRUCache<HGPersistentHandle, IncidenceSet>(0.9f, 0.3f);
((MRUCache<HGPersistentHandle, IncidenceSet>)incidenceCache).setLockImplementation(
new ReentrantReadWriteLock()
/* new HGLock(this, INCIDENCE_CACHE_ID) */);
// new SimpleCache<HGPersistentHandle, IncidenceSet>();
incidenceCache.setResolver(new ISRefResolver(this));
cache.setIncidenceCache(incidenceCache);
typeSystem = new HGTypeSystem(this);
//
// Make sure system indices are created.
//
indexByType = store.getIndex(TYPES_INDEX_NAME,
BAtoHandle.getInstance(this.getHandleFactory()),
BAtoHandle.getInstance(this.getHandleFactory()),
null,
true);
indexByValue = store.getIndex(VALUES_INDEX_NAME,
BAtoHandle.getInstance(this.getHandleFactory()),
BAtoHandle.getInstance(this.getHandleFactory()),
null,
true);
if (config.isUseSystemAtomAttributes())
systemAttributesDB = store.getIndex(SA_DB_NAME,
BAtoHandle.getInstance(this.getHandleFactory()),
HGAtomAttrib.baConverter,
null,
true);
idx_manager = new HGIndexManager(this);
//
// Now, bootstrap the type system.
//
getTransactionManager().beginTransaction(HGTransactionConfig.DEFAULT);
typeSystem.bootstrap(config.getTypeConfiguration());
getTransactionManager().endTransaction(true);
idx_manager.loadIndexers();
// Initialize atom access statistics, purging and the like.
initAtomManagement();
// Load all listeners stored in this HyperGraph as HGListenerAtoms
loadListeners();
// This is kind of completing the type system bootstrap process, otherwise
// there's a circular dependency between the initialization of the index manager
// and the JavaObjectMapper (which has an index on the HGSerializable atoms). Another
// option to avoid this typical bootstrapping circularity is the implement the JavaObjectMapper
// to directly work with the HGStore instead of relying on the index manager. In any case,
// it all remains an implementation detail.
// typeSystem.getJavaTypeFactory().initNonDefaultMappers();
if (config == null || !config.getSkipOpenedEvent())
eventManager.dispatch(this, new HGOpenedEvent());
if (config != null)
{
if (config.getCancelMaintenance())
{
List<HGHandle> L = HGQuery.hg.findAll(this, HGQuery.hg.typePlus(MaintenanceOperation.class));
for (HGHandle x : L)
remove(x);
}
else if (!config.getSkipMaintenance())
runMaintenance();
}
}
catch (Throwable t)
{
if (store != null) try { store.close(); } catch (Throwable t1) { }
try { cache.close(); } catch (Throwable t1) { }
is_open = false;
throw new HGException(t);
}
}
// A lock that makes sure only one thread can close the HGDB instance.
private Object closeLock = new Object();
/**
* <p>Gracefully close all resources associated with the run-time instance
* of <code>HyperGraph</code>. </p>
*/
public void close()
{
synchronized (closeLock)
{
if (!is_open)
return;
ArrayList<Throwable> problems = new ArrayList<Throwable>();
try { eventManager.dispatch(this, new HGClosingEvent()); } catch (Throwable t) { problems.add(t); }
try { replace(statsHandle, stats); } catch (Throwable t) { problems.add(t); }
try { cache.close(); } catch (Throwable t) { problems.add(t); }
try { idx_manager.close(); } catch (Throwable t) { problems.add(t); }
try { eventManager.clear(); } catch (Throwable t) { problems.add(t); }
try { store.close(); } catch (Throwable t) { problems.add(t); }
is_open = false;
for (Throwable t : problems)
{
System.err.println("Problem during HyperGraph close, stack trace of exception follows:");
t.printStackTrace(System.err);
}
} // synchronize closing
}
/**
* <p>Return <code>true</code> is the database is currently open and <code>false</code>
* otherwise.</p>
*/
public boolean isOpen()
{
return is_open;
}
/**
* <p>Return the <code>HGStore</code> used by this hypergraph.</p>
*/
public HGStore getStore()
{
return store;
}
/**
* <p>Return the <code>HGTransactionManager</code> associated with this
* HyperGraph.
* </p>
* <p>
* The transaction manager allows you to encapsulate several operations
* as a single, atomic transaction.
* </p>
*/
public HGTransactionManager getTransactionManager()
{
return store.getTransactionManager();
}
/**
* <p>Return the <code>HGTypeSystem</code> instance managing run-time types
* for this hypergraph.</p>
*/
public HGTypeSystem getTypeSystem()
{
return typeSystem;
}
/**
* <p>Return the atom cache associated with this HyperGraph instance.</p>
*/
public HGAtomCache getCache()
{
return cache;
}
/**
* <p>Return this <code>HyperGraph</code>'s event manager instance.</p>
*/
public HGEventManager getEventManager()
{
return eventManager;
}
/**
* <p>Return the persistent handle of a given atom. Generally, when working
* with atom handles, one needn't worry whether they are in-memory or not. However,
* some low-level APIs dealing with permanent storage explicitly require
* a <code>HGPersistentHandle</code> and, in addition, applications may not to record
* the permanent handle of an atom somewhere else.
* </p>
*
* @param handle The <code>HGHandle</code> of the atom.
* @return The <code>HGPersistentHandle</code> corresponding to the passed in
* <code>HGHandle</code>.
*/
public HGPersistentHandle getPersistentHandle(HGHandle handle)
{
if (handle instanceof HGPersistentHandle)
return (HGPersistentHandle)handle;
else
return ((HGLiveHandle)handle).getPersistent();
}
/**
* <p>Return <code>true</code> if a given is currently loaded in main memory
* and <code>false</code> otherwise.</p>
*
* @param handle The handle of the atom.
*/
public boolean isLoaded(HGHandle handle)
{
if (handle instanceof HGPersistentHandle)
return cache.get(handle.getPersistent()) != null;
else
return ((HGLiveHandle)handle).getRef() != null;
}
/**
* <p>Return <code>true</code> if a given is currently frozen in the cache
* and <code>false</code> otherwise. Frozen atoms are guaranteed to NOT be evicted
* from the cache.</p>
*
* @param handle The handle of the atom.
*/
public boolean isFrozen(HGHandle handle)
{
HGLiveHandle lHandle = (handle instanceof HGPersistentHandle) ? cache.get((HGPersistentHandle)handle) : (HGLiveHandle)handle;
return (lHandle == null) ? false : cache.isFrozen(lHandle);
}
/**
* <p>
* Freeze an atom into the HyperGraph cache.Frozen atoms are guaranteed to NOT be evicted
* from the cache. If the atom is not already currently loaded, it will be loaded. If it is
* already frozen, nothing will be done - so, it's safe to call this method multiple times.
* Because the atom instance is returned, this method may be called instead of <code>get</code>
* to retrieve an atom instance based on its handle while making sure that it's never removed
* from the cache.
* </p>
*
* <p>
* This method should be called with care since it is a possible source of memory leaks. The
* <code>un-freeze</code> method should be called at appropriate times when you no longer need
* an object to absolutely remain in main memory. Typically, freezing an atom is desirable in
* the following situations:
*
* <ul>
* <li>You need to retrieve a <code>HGHandle</code> from a Java instance reference by a call
* to the <code>getHandle</code>. This is only guaranteed to work when the atom is in the cache.</li>
* <li>The atom is a very large object, expansive to re-construct from permanent storage and even
* though used relatively rarely, it's better is it remain in memory. The cache will normally
* evict atoms based on used, not on they memory footprint which would be too much overhead
* to estimate in Java.</li>
* </ul>
* </p>
*
* @param handle The handle of the atom.
* @return The instance of the atom that was frozen.
*/
public Object freeze(final HGHandle handle)
{
return this.getTransactionManager().ensureTransaction(new Callable<Object>() {
public Object call() {
if (handle == null)
throw new NullPointerException("Trying to freeze null atom handle.");
get(handle);
HGLiveHandle lHandle = (handle instanceof HGPersistentHandle) ? cache.get((HGPersistentHandle)handle) : (HGLiveHandle)handle;
while (lHandle == null)
{
get(handle);
lHandle = cache.get((HGPersistentHandle)handle);
}
if (!cache.isFrozen(lHandle))
cache.freeze(lHandle);
return lHandle.getRef();
}
});
}
/**
* <p>
* Unfreeze a previously frozen atom so that it becomes subject of eviction from the cache.
* If the atom is not loaded or not currently frozen in the cache, nothing is done - so, it's
* safe to call this method multiple times.
* </p>
*
* @param handle
*/
public void unfreeze(HGHandle handle)
{
HGLiveHandle lHandle = (handle instanceof HGPersistentHandle) ? cache.get((HGPersistentHandle)handle) : (HGLiveHandle)handle;
if (lHandle != null && cache.isFrozen(lHandle))
cache.unfreeze(lHandle);
}
/**
* <p>Return <code>true</code> if the incidence set of a given atom is currently
* loaded in main memory and <code>false</code> otherwise.</p>
*
* @param h The handle of the atom in whose incidence we are interested.
*/
public boolean isIncidenceSetLoaded(HGHandle h)
{
return cache.getIncidenceCache().isLoaded(getPersistentHandle(h));
}
/**
* <p>Add a new atom to the database using the default, Java Beans based typing
* mechanism and default system flags.
* </p>
*
* @param atom The <code>Object</code> instance to be stored as a hypergraph atom.
* @return A <code>HGHandle</code> to the newly created atom. The handle may be used
* as a reference to the atom within hypergraph and to construct link to that atom.
*/
public HGHandle add(Object atom)
{
return add(atom, 0);
}
/**
* <p>Add a new atom to the database using the default, Java Beans based typing
* mechanism and the set of specified system flags. The atom to be added will be
* treated as a Java bean with appropriate getter and setter methods used as
* property accessors. The type of the atom will be inferred using the Java
* introspection mechanism.
* </p>
*
* @param atom The <code>Object</code> instance to be stored as a hypergraph atom.
* @param flags A combination of system-level bit flags. Available flags that can
* be <em>or-ed</em> together are listed in the <code>HGSystemFlags</code> interface.
* @return A <code>HGHandle</code> to the newly created atom. The handle may be used
* as a reference to the atom within hypergraph and to construct link to that atom.
*/
public HGHandle add(Object atom, int flags)
{
HGHandle result;
if (atom instanceof HGLink)
{
HGLink link = (HGLink)atom;
Object value = link;
if (link instanceof HGValueLink)
value = ((HGValueLink)link).getValue();
HGHandle type = typeSystem.getTypeHandle(value);
if (type == null)
throw new HGException("Unable to create HyperGraph type for class " + value.getClass().getName());
result = addLink(value, type, link, (byte)flags);
}
else
{
HGHandle type = typeSystem.getTypeHandle(atom);
if (type == null)
throw new HGException("Unable to create HyperGraph type for class " + atom.getClass().getName());
result = addNode(atom, type, (byte)flags);
}
eventManager.dispatch(this, new HGAtomAddedEvent(result, "HyperGraph.add"));
return result;
}
/**
* <p>Add a new atom with a specified type and default system flags to the database.</p>
*
* @param atom The new atom to add.
* @param type The handle of the <em>HyperGraphDB</em> type of the atom. This type must have
* been previously registered with the type system.
*/
public HGHandle add(Object atom, HGHandle type)
{
return add(atom, type, 0);
}
/**
* <p>Add a new atom with a specified type and system flags to the database.</p>
*
* @param atom The new atom to add.
* @param type The handle of the <em>HyperGraphDB</em> type of the atom. This type must have
* been previously registered with the type system.
* @param flags A combination of system-level bit flags. Available flags that can
* be <em>or-ed</em> together are listed in the <code>HGSystemFlags</code> interface.
* @return The HyperGraph handle of the newly added atom or <code>null</code> if
* the addition was refused by a listener to the {@link HGAtomProposeEvent}.
*/
public HGHandle add(Object atom, HGHandle type, int flags)
{
if (eventManager.dispatch(this,
new HGAtomProposeEvent(atom, type, flags)) == HGListener.Result.cancel)
return null;
HGHandle result;
if (atom instanceof HGLink)
{
HGLink link = (HGLink)atom;
Object value = link;
if (link instanceof HGValueLink)
value = ((HGValueLink)link).getValue();
result = addLink(value, type, link, (byte)flags);
}
else
result = addNode(atom, type, (byte)flags);
if (atom instanceof HGGraphHolder)
((HGGraphHolder)atom).setHyperGraph(this);
if (atom instanceof HGHandleHolder)
((HGHandleHolder)atom).setAtomHandle(result);
eventManager.dispatch(this, new HGAtomAddedEvent(result, "HyperGraph.add"));
return result;
}
/**
* <p>
* Refresh an atom handle with a currently valid and efficient run-time value. HyperGraph
* manages essentially two types of handles: run-time handles that are reminiscent to memory
* pointers and provide very fast access to loaded atoms and persistent handles that refer
* to long term storage. An atom can be accessed with both types of handles at all times
* regardless of whether it is currently in memory or "passified" into permanent storage.
* During long operations on a large graph, it is likely that atoms get moved in and out
* of main memory and their <em>live</em> status constantly fluctuates. This method allows
* you to bring a <code>HGHandle</code> in line with the current <em>live</em> status of
* an atom. It is desirable to bring a <code>HGHandle</code> when the atom will be accessed
* frequently. The following scenarios describe situation where it may be worth refreshing
* a handle:
* </p>
*
* <ul>
* <li>A link has been retrieved and the atoms in its target set will be frequently
* accessed. Generally, the atom handles comprising the target set will be persistent handles,
* unless those atoms are loaded at the time the link is retrieved. Therefore, one may
* want to refresh that target handles after their corresponding atoms have been loaded
* in memory.</li>
* <li>An atom was loaded, but it wasn't used for a long time and was therefore removed
* from the cache. In this case, its live handle would loose its "liveliness" and it would
* be a good idea to refresh it if the atom starts being used again.
* </ul>
*
* <p>
* Given a persistent handle as an argument, this method will return the corresponding live handle
* if and only if the atom currently loaded. Given an invalid live handle, the method will
* either return a new, valid one if the atom was re-loaded, or it will simply return
* its argument if the atom is currently in the cache.
* </p>
*
* @param handle The handle of the atom.
* @return An updated handle according to the behavior described above.
*/
public HGHandle refreshHandle(HGHandle handle)
{
if (handle instanceof HGPersistentHandle)
{
HGHandle result = cache.get((HGPersistentHandle)handle);
return result != null ? result : handle;
}
else
{
HGLiveHandle live = (HGLiveHandle)handle;
if (live.getRef() == null)
{
HGLiveHandle updated = cache.get(live.getPersistent());
if (updated != null)
return updated;
else
return live.getPersistent();
}
else
return handle;
}
}
/**
* <p>Retrieve a hypergraph atom by its handle.</p>
* <p>HyperGraph will immediately return
* an atom available in memory through a live handle, and it will fetch
* the atom from the store for a persistent handle.</p>
*
* @param handle The handle of the atom.
* @return The hypergraph atom. HyperGraph will construct a run-time instance
* of the appropriate type, but it is up to the application logic to perform
* the right cast. The actual type of the atom may be obtained via a call
* to <code>HyperGraph.getTypeSystem().getAtomType(Object)</code>.
*/
public <T> T get(final HGHandle handle)
{
//return getTransactionManager().ensureTransaction(new Callable<T>()
// { public T call() {
stats.atomAccessed();
HGLiveHandle liveHandle = null;
HGPersistentHandle persistentHandle = null;
if (handle instanceof HGLiveHandle)
liveHandle = (HGLiveHandle)handle;
else
liveHandle = cache.get((HGPersistentHandle)handle);
if (liveHandle != null)
{
T theAtom = (T)liveHandle.getRef();
if (theAtom == null)
{
//
// The atom has been evicted from the cache, so the live reference is no
// longer valid. We have to rely on the persistent handle reference.
HGLiveHandle existing = cache.get(liveHandle.getPersistent());
if (existing != null)
{
theAtom = (T)existing.getRef();
if (theAtom != null)
{
eventManager.dispatch(HyperGraph.this, new HGAtomAccessedEvent(existing, theAtom));
return (T)theAtom;
}
}
persistentHandle = liveHandle.getPersistent();
}
else
{
eventManager.dispatch(HyperGraph.this, new HGAtomAccessedEvent(liveHandle, theAtom));
return (T)theAtom;
}
}
else
persistentHandle = (HGPersistentHandle)handle;
Pair<HGLiveHandle, Object> loaded = loadAtom(persistentHandle, liveHandle);
if (loaded == null)
return null; // TODO: perhaps we should throw an exception here, but a new type, e.g. HGInvalidHandleException?
// HGLiveHandle temp = cache.get(loaded.getSecond());
// if (temp == null ) // || loaded.getSecond() != temp.getRef())
// {
// System.out.println("oops, just loaded not same as in cache " + persistentHandle);
// //return get(persistentHandle);
// }
//
liveHandle = loaded.getFirst();
// if (liveHandle.getRef() != loaded.getSecond())
// return get(liveHandle);
//
// If the incidence set of the newly fetched atom is already loaded,
// traverse it to update the target handles of all links pointing to it.
//
IncidenceSet incidenceSet = cache.getIncidenceCache().getIfLoaded(persistentHandle);
if (incidenceSet != null)
updateLinksInIncidenceSet(incidenceSet, liveHandle);
//
// If the newly fetched atom is a link, update all loaded incidence
// sets, of which it is part, with its live handle.
//
// NOTE: commented out for now since IncidenceSet stores only persistent handles for
// speedier lookup (because this way there's no need to check for live handles and do type casts)
/* if (liveHandle.getRef() instanceof HGLink)
{
HGLink link = (HGLink)liveHandle.getRef();
for (int i = 0; i < link.getArity(); i++)
{
IncidenceSet targetIncidenceSet = cache.getIncidenceSet(getPersistentHandle(link.getTargetAt(i)));
if (targetIncidenceSet != null)
for (int j = 0; j < targetIncidenceSet.length; j++)
{
if (targetIncidenceSet[j].equals(persistentHandle))
targetIncidenceSet[j] = liveHandle;
}
}
} */
eventManager.dispatch(HyperGraph.this, new HGAtomAccessedEvent(liveHandle, loaded.getSecond()));
return (T)loaded.getSecond();
//}});
}
/**
* <p>Return the handle of the specified atom.</p>
*
* @param atom The atom whose handle is desired.
* @return The <code>HGHandle</code> of the passed in atom, or <code>null</code>
* if the atom is not in HyperGraph cache at the moment.
*/
public HGHandle getHandle(final Object atom)
{
return cache.get(atom);
}
/**
* <p>Retrieve the handle of the type of the atom referred to by <code>handle</code>.</p>
*
* <p><strong>FIXME:</strong> Instances of the same run-time Java type are not guaranteed
* to have the same HyperGraph type. For instance, a Java <code>String</code> may be mapped
* either to a HyperGraph indexed and reference counted strings, or to long text blobs. Therefore,
* the correct way of getting the actual HG type of an atom is by reading of off storage. We
* don't cache type handles of atoms as of now and this might turn out to be a performance issue.
* </p.
*
* @param handle The <code>HGHandle</code> of the atom whose type is desired.
* @return The <code>HGHandle</code> of the atom type.
* @throws HGException if the passed in handle is invalid or unknown to HyperGraph.
*/
public HGHandle getType(HGHandle handle)
{
HGPersistentHandle pHandle;
Object atom = null;
if (handle instanceof HGLiveHandle)
{
atom = ((HGLiveHandle)handle).getRef();
pHandle = ((HGLiveHandle)handle).getPersistent();
}
else
{
pHandle = (HGPersistentHandle)handle;
HGLiveHandle lHandle = cache.get(pHandle);
if (lHandle != null)
atom = lHandle.getRef();
}
if (atom != null && atom instanceof HGTypeHolder)
return getHandle(((HGTypeHolder)atom).getAtomType());
HGPersistentHandle [] link = store.getLink(pHandle);
if (link == null || link.length < 2)
return null;
else
return refreshHandle(link[0]);
}
/**
* <p>Remove an atom from the HyperGraph database. This is equivalent to calling
* <code>remove(handle, false)</code> - see that version of <code>remove</code> for
* detailed explanation. Essentially, this means that all links pointing to the
* atom will be removed as well. This default behavior is based on the assumption
* that most frequently links as ordered tuples that establish a particular
* relationship b/w their targets and therefore make sense only as a whole.</p>
*
* @param handle The handle of the atom to be removed. <strong>NOTE:</strong> if no atom
* exists with this handle (e.g. the atom was already removed), the method does nothing and
* return false. If an attempt is made to remove an atom with a <code>null</code>
* handle, then a regular <code>NullPointerException</code> is thrown.
* @return <code>true</code> if the atom was successfully removed and <code>false</code>
* otherwise.
* @throws HGRemoveRefusedException if integrity constrains are violated or a user registered
* listener to the {@link HGAtomRemoveRequestEvent} returns a {@link HGListener.Result.cancel}
* result.
*/
public boolean remove(final HGHandle handle)
{
return remove(handle, config.isKeepIncidentLinksOnRemoval());
}
/**
* <p>Remove the atom referred to by <code>handle</code> from the hypergraph store.</p>
*
* <p>
* <strong>Note:</strong> This operation will delete the atom and potentially
* recursively all links pointing to it together with links pointing to them etc., if
* the <code>keepIncidentLinks</code> parameter is <code>false</code>.
* Also, if the atom
* is a type, all its instances are removed recursively (however, it is not possible to
* remove a predefined type). Thus, theoretically the removal of an atom could empty
* the whole hypergraph database.
* </p>
*
* <p>
* When dealing with a complex linked structure, it is never obvious when incident links
* should be removed along with a given atom. As a rule of thumb, if the links are
* interpreted as ordered, in general they should be removed. Otherwise, if they are
* unordered, the atom should simply be removed from their target set. Note, however,
* that this very much depends on the application semantics. In mixed cases, when
* some links pointing to the atom must be removed, but others must be preserved, one
* should delete the former "manually" and set the <code>keepIncidentLinks</code> to
* <code>true</code>.
* </p>
*
* <p>
* <strong>Note:</strong> also that the <code>keepIncidentLinks</code> applies recursively.
* Thus, if you set it to <code>false</code> links pointing to the links pointing to...etc.
* the given atom will all be deleted.
* </p>
*
* @param handle The handle of the atom to be removed. <strong>NOTE:</strong> if no atom
* exists with this handle (e.g. the atom was already removed), the method does nothing and
* return false. If an attempt is made to remove an atom with a <code>null</code>
* handle, then a regular <code>NullPointerException</code> is thrown.
* @param keepIncidentLinks A flag indicating whether to remove the atom from the links
* pointing to it (if <code>true</code>) or whether to remove the links altogether (if
* <code>false</code>). The flag applies recursively to all removals triggered from
* this call.
* @return <code>true</code> if the atom was successfully removed and <code>false</code>
* otherwise.
* @throws HGRemoveRefusedException if integrity constrains are violated or a user registered
* listener to the {@link HGAtomRemoveRequestEvent} returns a {@link HGListener.Result.cancel}
* result.
*/
public boolean remove(final HGHandle handle, final boolean keepIncidentLinks)
{
if (eventManager.dispatch(this, new HGAtomRemoveRequestEvent(handle)) == HGListener.Result.cancel)
throw new HGRemoveRefusedException(handle, "Removal cancelled by atom listener");
if (config.getPreventDanglingAtomReferences())
{
AtomRefType refType = typeSystem.getAtomType(HGAtomRef.class);
// symbolic links don't prevent removal of atoms
if (refType.getHardIdx().findFirst(handle.getPersistent()) != null ||
refType.getFloatingIdx().findFirst(handle.getPersistent()) != null)
throw new HGRemoveRefusedException(handle, "Atom is in use in a HGAtomRef");
}
return getTransactionManager().ensureTransaction(new Callable<Boolean>()
{ public Boolean call() { return removeTransaction(handle, keepIncidentLinks); }});
}
private boolean removeTransaction(final HGHandle handle, final boolean keepIncidentLinks)
{
HGPersistentHandle pHandle = getPersistentHandle(handle);
Set<HGPersistentHandle> inRemoval = TxAttribute.getSet(getTransactionManager(),
TxAttribute.IN_REMOVAL,
HashSet.class);
if (inRemoval.contains(handle))
return true;
else
inRemoval.add(pHandle);
try
{
HGPersistentHandle [] layout = store.getLink(pHandle);
if (layout == null)
return false;
else if (layout[0].equals(typeSystem.getTop()))
throw new HGRemoveRefusedException(handle,
"Cannot remove the HyperGraph primitive type: " + pHandle);
Object atom = get(handle); // need the atom in order to clear all indexes...
//
// If the atom is a type, remove it from the type system
// (which also removes all its instances).
//
if (atom instanceof HGAtomType)
{
HGSearchResult<HGPersistentHandle> instances = null;
try
{
instances = indexByType.find(pHandle);
while (instances.hasNext())
removeTransaction((HGPersistentHandle)instances.next(), keepIncidentLinks);
}
finally
{
if (instances != null) instances.close();
}
idx_manager.unregisterAll(pHandle);
typeSystem.remove(pHandle, (HGAtomType)atom);
}
HGPersistentHandle typeHandle = layout[0];
HGPersistentHandle valueHandle = layout[1];
HGAtomType type = typeSystem.getType(typeHandle);
//
// Clean all indexing entries related to this atom.
//
idx_manager.maybeUnindex(typeHandle, type, pHandle, atom);
indexByType.removeEntry(typeHandle, pHandle);
indexByValue.removeEntry(valueHandle, pHandle);
//
// Remove the atom record from the store and cache.
//
TypeUtils.releaseValue(HyperGraph.this, type, valueHandle);
//type.release(valueHandle);
store.removeLink(pHandle);
//
// If it's a link, remove it from the incidence sets of all its
// targets.
//
if (layout.length > 2)
for (int i = 2; i < layout.length; i++)
removeFromIncidenceSet(layout[i], pHandle);
//
// Handle links pointing to this atom:
//
if (keepIncidentLinks)
{
IncidenceSet incidenceSet = cache.getIncidenceCache().get(pHandle);
HGSearchResult<HGHandle> rsInc = incidenceSet.getSearchResult();
try { while (rsInc.hasNext()) targetRemoved(rsInc.next(), pHandle); }
finally { rsInc.close(); }
}
else // Need to load in memory because circular dependencies might create deadlocks
{
IncidenceSet incidenceSet = cache.getIncidenceCache().getIfLoaded(pHandle);
if (incidenceSet != null)
{
HGSearchResult<HGHandle> rsInc = incidenceSet.getSearchResult();
try { while (rsInc.hasNext()) removeTransaction(rsInc.next(), false); }
finally { rsInc.close(); }
}
else
{
HGSearchResult<HGHandle> rsInc = (HGSearchResult)store.getIncidenceResultSet(pHandle);
try
{
while (rsInc.hasNext())
removeTransaction(rsInc.next(), false);
}
finally
{
rsInc.close();
}
}
// for (HGPersistentHandle h : store.getIncidenceSet(pHandle))
// removeTransaction(h, false);
}
store.removeIncidenceSet(pHandle);
cache.getIncidenceCache().remove(pHandle);
cache.remove((HGHandle)cache.get(atom));
eventManager.dispatch(HyperGraph.this, new HGAtomRemovedEvent(pHandle));
return true;
}
finally
{
inRemoval.remove(pHandle);
}
}
/**
* <p>
* Update the value of an atom in HyperGraph. This is equivalent to
* a call to <code>replace(getHandle(atom), atom)</code>. An exception is
* thrown if the handle of the passed in atom could not be found.
* </p>
*
* @param atom
*/
public boolean update(Object atom)
{
HGHandle h = getHandle(atom);
if (h == null)
throw new HGException("Could not find HyperGraph handle for atom " + atom);
else
return replace(h, atom, getType(h));
}
/**
* <p>
* Replace the value of an atom with a new value. The atom will preserve
* its handle and all links pointing to it will remain valid. This method allows
* you to effectively change the type of an atom while preserving the hypergraph
* structure intact. The new value does not have to be of a different than
* the old value type, of course.
* </p>
*
* <p>
* As when adding new atom of arbitrary Java types, the concrete type of the
* <code>atom</code> parameter will be inferred if necessary.
* </p>
*
* <p>
* The structure of the graph will be modified only if the atom is
* replaced with a new value that has a different linking import
* (e.g. a node is replaced by a link or vice-versa, or a link with
* a different target set is being replaced).
* </p>
*
* <p>
* <strong>NOTE:</strong> If a <code>HGAtomType</code> atom with a non-empty
* instance set is being replaced, the new value must also be an <code>HGAtomType</code>
* that is <em>compatible</em> with the old <code>HGAtomType</code>. Compatibility
* here means that the new type must be able to store all values of the old type.
* The <code>replace</code> method will effectively attempt to recursively morph
* all those values based on the the new <code>HGAtomType</code>.
* </p>
*
* @param handle The handle of the atom to be replaced.
* @param atom An arbitrary Java <code>Object</code> representing the new atom. The
* type of the atom will be inferred using the Java introspection mechanism.
*/
public boolean replace(HGHandle handle, Object atom)
{
if (handle.equals(getHandle(atom)))
return replace(handle, atom, getType(handle)); // this is an 'update'
HGHandle atomType;
if (atom instanceof HGValueLink)
{
Class<?> c = ((HGValueLink)atom).getValue().getClass();
atomType = typeSystem.getTypeHandle(c);
if (atomType == null)
throw new HGException("Unable to create HyperGraph type for class " + c.getName());
}
else
{
atomType = typeSystem.getTypeHandle(atom);
if (atomType == null)
atomType = typeSystem.getTypeHandle(atom.getClass());
if (atomType == null)
throw new HGException("Unable to create HyperGraph type for class " + atom.getClass().getName());
}
return replace(handle, atom, atomType);
}
/**
* <p>
* Replace the value of an atom with a new value. The atom will preserve
* its handle and all links pointing to it will remain valid. This method allows
* you to effectively change the type of an atom while preserving the hypergraph
* structure intact. The new value does not have to be of a different than
* the old value type, of course.
* </p>
*
* <p>
* The structure of the graph will be modified only if the atom is
* replaced with a new value that has a different linking import
* (e.g. a node is replaced by a link or vice-versa, or a link with
* a different target set is being replaced).
* </p>
*
* <p>
* <strong>NOTE:</strong> If a <code>HGAtomType</code> atom with a non-empty
* instance set is being replaced, the new value must also be an <code>HGAtomType</code>
* that is <em>compatible</em> with the old <code>HGAtomType</code>. Compatibility
* here means that the new type must be able to store all values of the old type.
* The <code>replace</code> method will effectively attempt to recursively morph
* all those values based on the the new <code>HGAtomType</code>.
* </p>
*
* <p>
* If an attempt is made to replace one of the predefined type atoms, the behavior
* is currently undefined.
* </p>
*
* @param handle The handle of the atom to be replaced.
* @param atom An arbitrary Java <code>Object</code> representing the new atom.
* @param type The type of the new atom value.
*/
public boolean replace(HGHandle handle, Object atom, HGHandle type)
{
if (eventManager.dispatch(this,
new HGAtomReplaceRequestEvent(handle, type, atom)) == HGListener.Result.cancel)
return false;
HGPersistentHandle pHandle = null;
HGLiveHandle lHandle = null;
if (handle instanceof HGPersistentHandle)
{
pHandle = (HGPersistentHandle)handle;
lHandle = cache.get(pHandle);
}
else
{
lHandle = (HGLiveHandle)handle;
pHandle = lHandle.getPersistent();
}
replaceInternal(lHandle, pHandle, atom, type);
eventManager.dispatch(this, new HGAtomReplacedEvent(lHandle));
return true;
}
/**
* <p>
* Put an existing atom into this HyperGraph instance. This is a rather low-level method
* that requires you to explicitly find the type and value handles for the atom and use
* an already existing, yet unknown to this HyperGraph instance, persistent handle.
* </p>
*
* <p>
* One possible use of this is when an application relies on a HyperGraph for storage and it needs
* to populate it with some predefined set of atoms with a set of existing, pre-fabricated handles.
* Using handles in an application instead of some naming scheme and the corresponding <em>name</em>
* properties is the preferred way of working with HyperGraph.
* </p>
*
* @param atomHandle A valid <code>HGPersistentHandle</code> of the atom being defined. If an atom
* already exists with this handle, it will be replaced. This parameter cannot be <code>null</code>.
* @param typeHandle The handle of this atom's type. The corresponding <code>HGAtomType</code> should
* be capable of retrieving the actual atom instance based on the passed in <code>valueHandle</code>.
* This parameter cannot be <code>null</code>.
* @param valueHandle The handle of the atom's value. This parameter cannot be <code>null</code>.
* @param outgoingSet If the atom is a link, this parameter specifies the set of atoms pointed to by the link.
* If this parameter is <code>null</code> or of size 0, then the atom is not a link.
* @param instance The runtime instance of the atom. The runtime instance is needed in order to
* update any indices related to the atom. If this parameter is <code>null</code>, an attempt to
* construct the instance from the <code>valueHandle</code> will be made.
*/
public void define(final HGHandle atomHandle,
final HGHandle typeHandle,
final HGHandle valueHandle,
final HGLink outgoingSet,
final Object instance)
{
getTransactionManager().ensureTransaction(new Callable<Object>()
{ public Object call() {
if (get(atomHandle) != null)
throw new IllegalArgumentException("Can't define an already existing atom " + atomHandle + ", please use replace instead.");
HGPersistentHandle [] layout = new HGPersistentHandle[outgoingSet == null ? 2 : 2 + outgoingSet.getArity()];
layout[0] = getPersistentHandle(typeHandle);
layout[1] = getPersistentHandle(valueHandle);
if (outgoingSet != null)
for (int i = 0; i < outgoingSet.getArity(); i++)
layout[i + 2] = getPersistentHandle(outgoingSet.getTargetAt(i));
store.store(atomHandle.getPersistent(), layout);
indexByType.addEntry(layout[0], atomHandle.getPersistent());
indexByValue.addEntry(layout[1], atomHandle.getPersistent());
// Need to construct the value and add it to atom indices.
HGAtomType type = get(typeHandle);
ReadyRef<HGHandle[]> linkRef = null;
if (outgoingSet != null)
{
HGHandle [] targets = new HGHandle[outgoingSet.getArity()];
System.arraycopy(layout, 2, targets, 0, targets.length);
updateTargetsIncidenceSets(atomHandle.getPersistent(), outgoingSet);
linkRef = new ReadyRef<HGHandle[]>(targets);
}
idx_manager.maybeIndex(layout[0],
type,
atomHandle.getPersistent(),
instance == null ? type.make(layout[1], linkRef, null) : instance);
return null;
}});
}
/**
* <p>
* Put an atom with a specific <code>HGPersistentHandle</code> into this HyperGraph instance.
* </p>
*
* <p>
* One possible of this is when an application relies on a HyperGraph for storage and it needs
* to populate it with some predefined set of atoms with a set of existing, prefabricated handles.
* Using handles in an application instead of some naming scheme and the corresponding <em>name</em>
* properties is the preferred way of working with HyperGraph.
* </p>
*
* @param atomHandle A valid <code>HGPersistentHandle</code> of the atom being defined. If an atom
* already exists with this handle, it will be replaced. This parameter cannot be <code>null</code>.
* @param instance The handle of the atom's value. May be <code>null</code> in which case the default
* HyperGraph <code>NullType</code> is used.
*/
public void define(final HGHandle atomHandle, final Object instance, final int flags)
{
HGHandle typeHandle = null;
if (instance == null)
typeHandle = typeSystem.getNullType();
else
typeHandle = typeSystem.getTypeHandle(instance.getClass());
if (typeHandle == null)
throw new HGException("Could not find HyperGraph type for object of type " + instance.getClass());
define(atomHandle, typeHandle, instance, flags);
}
/**
* <p>
* A version of <code>define</code> allowing one to pass a specific type to use when
* storing the atom.
* </p>
*
* @param atomHandle The atom handle.
* @param typeHandle The handle of the type to use.
* @param instance The atom instance.
* @param flags System flags.
*/
public void define(final HGHandle atomHandle,
final HGHandle typeHandle,
final Object instance,
final int flags)
{
getTransactionManager().ensureTransaction(new Callable<Object>()
{ public Object call() {
HGAtomType type = typeSystem.getType(typeHandle);
HGLink link = null;
Object payload = instance;
if (instance instanceof HGLink)
{
link = (HGLink)instance;
if (instance instanceof HGValueLink)
payload = ((HGValueLink)instance).getValue();
}
HGPersistentHandle valueHandle = TypeUtils.storeValue(HyperGraph.this, payload, type);
define(atomHandle, typeHandle, valueHandle, link, instance);
HyperGraph.this.atomAdded(atomHandle.getPersistent(), instance, flags);
if (instance instanceof HGTypeHolder)
((HGTypeHolder<HGAtomType>)instance).setAtomType(type);
return null;
}});
}
/**
* <p>Delegate to <code>define(HGPersistentHandle, Object, HGHandle [], byte)</code> with the
* flags parameter = 0.
*
* @param atomHandle The handle of the atom to define.
* @param instance The atom's runtime instance.
*/
public void define(final HGHandle atomHandle, final Object instance)
{
define(atomHandle, instance, (byte)0);
}
/**
* <p>Return the <code>IncidenceSet</code>, that is the set of all <code>HGLink</code>s pointing
* to, the atom referred by the passed in handle.</p>
*
* @param handle The handle of the atom whose incidence set is desired.
* @return The atom's <code>IncidenceSet</code>.
* The returned set may have 0 elements, but it will never be <code>null</code>.
*/
public IncidenceSet getIncidenceSet(HGHandle handle)
{
return cache.getIncidenceCache().get(getPersistentHandle(handle));
}
public int getSystemFlags(HGHandle handle)
{
if (!config.isUseSystemAtomAttributes())
return 0;
else if (handle instanceof HGLiveHandle)
if (handle instanceof HGManagedLiveHandle)
return ((HGManagedLiveHandle)handle).getFlags();
else
return 0;
else
{
HGAtomAttrib attribs = this.getAtomAttributes((HGPersistentHandle)handle);
if (attribs != null)
return attribs.flags;
else
return 0;
}
}
public void setSystemFlags(final HGHandle handle, final int flags)
{
if (!config.isUseSystemAtomAttributes())
return;
getTransactionManager().ensureTransaction(new Callable<Object>()
{ public Object call() {
//
// NOTE: there are several cases here. We may be switching from
// default to non-default or vice-versa. We may be switching from
// managed to non-managed or vice-versa. In the first situation, we
// need to take care of adding/removing atom attributes. In all cases,
// we have to adjust the live handle from/to managed/normal.
//
HGPersistentHandle pHandle = getPersistentHandle(handle);
HGAtomAttrib attribs = HyperGraph.this.getAtomAttributes(pHandle);
boolean managed = (flags & HGSystemFlags.MANAGED) != 0;
boolean wasManaged = false;
if (attribs != null)
{
wasManaged = (attribs.flags & HGSystemFlags.MANAGED) != 0;
if (flags == HGSystemFlags.DEFAULT)
{
HyperGraph.this.removeAtomAttributes(pHandle);
attribs = null;
}
else
{
if (!wasManaged && managed)
{
attribs.lastAccessTime = System.currentTimeMillis();
attribs.retrievalCount = 1;
}
attribs.flags = (byte)flags;
HyperGraph.this.setAtomAttributes(pHandle, attribs);
}
}
else if (flags != HGSystemFlags.DEFAULT)
{
attribs = new HGAtomAttrib();
attribs.flags = (byte)flags;
if (managed)
{
attribs.lastAccessTime = System.currentTimeMillis();
attribs.retrievalCount = 1;
}
HyperGraph.this.setAtomAttributes(pHandle, attribs);
}
// else if we are trying to set the flags to default which they already are!
else
{
return null;
}
HGLiveHandle lHandle = cache.get(pHandle);
if (lHandle != null)
{
Object instance = lHandle.getRef();
if (wasManaged && managed)
{
attribs.lastAccessTime = ((HGManagedLiveHandle)lHandle).getLastAccessTime();
attribs.retrievalCount = ((HGManagedLiveHandle)lHandle).getRetrievalCount();
}
cache.remove(lHandle);
if (instance != null)
cache.atomRead(pHandle, instance, attribs);
}
return null;
}});
}
public <T> T findOne(HGQueryCondition condition)
{
return (T)hg.findOne(this, condition);
}
public <T> T getOne(HGQueryCondition condition)
{
return (T)hg.getOne(this, condition);
}
/**
* <p>Run a HyperGraphDB lookup query based on the specified condition.</p>
*
* @param condition The <code>HGQueryCondition</code> constraining the returned
* result set. It cannot be <code>null</code>.
*/
public <T> HGSearchResult<T> find(HGQueryCondition condition)
{
HGQuery<T> query = HGQuery.make(this, condition);
return query.execute();
}
public <T> List<T> getAll(HGQueryCondition condition)
{
return hg.getAll(this, condition);
}
public List<HGHandle> findAll(HGQueryCondition condition)
{
return hg.findAll(this, condition);
}
public long count(HGQueryCondition condition)
{
return hg.count(this, condition);
}
/**
* <p>
* Return the <code>HGIndexManager</code> that is associated with this
* HyperGraph instance. The index manager may be used to create indices
* for specific atoms types. Such indices may result in quicker queries
* at the expense of slower atom insertions.
* </p>
*/
public HGIndexManager getIndexManager()
{
return idx_manager;
}
// ------------------------------------------------------------------------
// PRIVATE METHOD SECTION
// ------------------------------------------------------------------------
private HGLiveHandle addNode(final Object payload, final HGHandle typeHandle, final byte flags)
{
return getTransactionManager().ensureTransaction(new Callable<HGLiveHandle>()
{ public HGLiveHandle call() {
HGAtomType type = typeSystem.getType(typeHandle);
HGPersistentHandle pTypeHandle = getPersistentHandle(typeHandle);
HGPersistentHandle valueHandle = TypeUtils.storeValue(HyperGraph.this, payload, type);
HGPersistentHandle [] layout = new HGPersistentHandle[2];
layout[0] = pTypeHandle;
layout[1] = valueHandle;
final HGLiveHandle lHandle = atomAdded(store.store(layout), payload, flags);
if (payload instanceof HGTypeHolder)
((HGTypeHolder<HGAtomType>)payload).setAtomType(type);
indexByType.addEntry(pTypeHandle, lHandle.getPersistent());
indexByValue.addEntry(valueHandle, lHandle.getPersistent());
idx_manager.maybeIndex(pTypeHandle, type, lHandle.getPersistent(), payload);
return lHandle;
} });
}
/**
* Add a link to the store: because we can have a wrapped HGValueLink, we
* pass the data as two parameters - payload and outgoingSet. The actual HG
* atom is always the outgoingSet parameter though.
*/
private HGLiveHandle addLink(final Object payload,
final HGHandle typeHandle,
final HGLink outgoingSet,
final byte flags)
{
return getTransactionManager().ensureTransaction(new Callable<HGLiveHandle>()
{ public HGLiveHandle call() {
HGAtomType type = typeSystem.getType(typeHandle);
HGPersistentHandle pTypeHandle = getPersistentHandle(typeHandle);
HGPersistentHandle valueHandle = TypeUtils.storeValue(HyperGraph.this, payload, type);
//
// Prepare link layout.
//
HGPersistentHandle [] layout = new HGPersistentHandle[2 + outgoingSet.getArity()];
layout[0] = pTypeHandle;
layout[1] = valueHandle;
for (int i = 0; i < outgoingSet.getArity(); i++)
layout[i + 2] = getPersistentHandle(outgoingSet.getTargetAt(i));
//
// Store in database.
//
HGPersistentHandle pHandle = store.store(layout);
HGLiveHandle lHandle = atomAdded(pHandle, outgoingSet, flags);
if (payload instanceof HGTypeHolder)
((HGTypeHolder<HGAtomType>)payload).setAtomType(type);
indexByType.addEntry(pTypeHandle, pHandle);
indexByValue.addEntry(valueHandle, pHandle);
idx_manager.maybeIndex(pTypeHandle, type, pHandle, payload);
//
// Update the incidence sets of all its targets.
//
updateTargetsIncidenceSets(pHandle, outgoingSet);
return lHandle;
}});
}
private HGLiveHandle atomAdded(HGPersistentHandle pHandle, Object instance, int flags)
{
if (instance instanceof HGGraphHolder)
((HGGraphHolder)instance).setHyperGraph(HyperGraph.this);
HGLiveHandle lHandle;
if (config.isUseSystemAtomAttributes())
{
HGAtomAttrib attribs = new HGAtomAttrib();
attribs.flags = (byte)flags;
attribs.retrievalCount = 1;
attribs.lastAccessTime = System.currentTimeMillis();
setAtomAttributes(pHandle, attribs);
lHandle = cache.atomAdded(pHandle, instance, attribs);
}
else
{
HGAtomAttrib attribs = new HGAtomAttrib();
if (config.isUseSystemAtomAttributes() && flags != 0)
{
attribs.flags = (byte)flags;
setAtomAttributes(pHandle, attribs);
}
lHandle = cache.atomAdded(pHandle, instance, attribs);
}
if (instance instanceof HGHandleHolder)
((HGHandleHolder)instance).setAtomHandle(lHandle);
return lHandle;
}
/**
* Loads an atom from storage. Returns a pair of (live handle, run-time instance) because the instance may very
* well get GC-ed before we even have the change to return it in the 'get' method above!
*
* @param persistentHandle
* @param liveHandle
* @return
*/
private Pair<HGLiveHandle, Object> loadAtom(final HGPersistentHandle persistentHandle, final HGLiveHandle liveHandle)
{
return getTransactionManager().ensureTransaction(new Callable<Pair<HGLiveHandle, Object>>()
{ public Pair<HGLiveHandle, Object> call() {
Object instance;
HGPersistentHandle [] link = store.getLink(persistentHandle);
if (link == null)
{
return null;
}
if (link.length < 2)
throw new HGException("The persistent handle " + persistentHandle +
" doesn't refer to a HyperGraph atom.");
HGPersistentHandle typeHandle = link[0];
HGPersistentHandle valueHandle = link[1];
if (typeHandle.equals(typeSystem.getTop()))
{
HGLiveHandle result = typeSystem.loadPredefinedType(persistentHandle);
return new Pair<HGLiveHandle, Object>(result, result.getRef());
}
IncidenceSetRef isref = new IncidenceSetRef(persistentHandle, HyperGraph.this);
HGAtomType type = typeSystem.getType(typeHandle);
boolean topCall = TypeUtils.initThreadLocals();
try
{
if (type == null)
throw new HGException("Unable to find type with handle " + typeHandle + " in database.");
if (link.length == 2)
instance = type.make(valueHandle, EMPTY_HANDLE_SET_REF, isref);
else
{
//
// If the atom is a link, update all targets with available
// live handles.
//
HGHandle [] targets = new HGHandle[link.length - 2];
for (int i = 2; i < link.length; i++)
{
HGPersistentHandle pHandle = link[i];
HGLiveHandle lHandle = (HGLiveHandle)cache.get(pHandle);
if (lHandle != null)
targets[i-2] = lHandle;
else
targets[i-2] = pHandle;
}
instance = type.make(valueHandle,
new ReadyRef<HGHandle[]>(targets),
isref);
//
// If the concrete result instance is not a link, then it has
// been embedded into a value link.
//
if (! (instance instanceof HGLink))
instance = new HGValueLink(instance, targets);
}
}
finally
{
TypeUtils.releaseThreadLocals(topCall);
}
if (instance instanceof HGAtomType)
instance = typeSystem.toRuntimeInstance(persistentHandle, (HGAtomType)instance);
HGLiveHandle result = null;
if (liveHandle == null)
{
HGAtomAttrib attribs = config.isUseSystemAtomAttributes() ? getAtomAttributes(persistentHandle) : new HGAtomAttrib();
result = cache.atomRead(persistentHandle, instance, attribs);
// The method could return an existing live handle, already in the cache
// we detect this by finding that the reference that handle holds is != from
// the instance we just loaded from disk
Object existing = result.getRef();
if (existing != instance)
{
if (existing != null)
{
instance = existing;
}
else
{
result = cache.atomRefresh(result, instance, false);
}
}
}
else
{
result = cache.atomRefresh(liveHandle, instance, false);
}
if (instance instanceof HGGraphHolder)
((HGGraphHolder)instance).setHyperGraph(HyperGraph.this);
if (instance instanceof HGHandleHolder)
((HGHandleHolder)instance).setAtomHandle(result);
if (instance instanceof HGTypeHolder)
((HGTypeHolder<HGAtomType>)instance).setAtomType(type);
eventManager.dispatch(HyperGraph.this, new HGAtomLoadedEvent(result, instance));
return new Pair<HGLiveHandle, Object>(result, instance);
}}, HGTransactionConfig.READONLY);
}
// private void unloadAtom(final HGLiveHandle lHandle, final Object instance)
// {
// try
// {
// getTransactionManager().ensureTransaction(new Callable<Object>()
// { public Object call() {
//
// // The following (both the code for MUTABLE and MANAGED flags) cause
// // deadlocks with a 'get' operation in the PhnatomHandle while
// // an atom is waiting to be de-queued: graph.get(handle) blocks
// // on waiting for the PhantomHandle to return, which in turn waits
// // for the atom (currently being GC-ed) to be dequeued, but this
// // doesn't happen because unloadAtom can't proceed due to simultaneous
// // DB write with the get operation.
///* if ((lHandle.getFlags() & HGSystemFlags.MUTABLE) != 0)
// {
// //TODO: Maybe this should be done somewhere else or differently...
// //in atomAdded() attribs are added only for MANAGED flag
// AtomAttrib attrib = getAtomAttributes(lHandle.getPersistentHandle());
// if(attrib == null){
// attrib = new AtomAttrib();
// attrib.flags = lHandle.getFlags();
// setAtomAttributes(lHandle.getPersistentHandle(), attrib);
// }
// //
// // We don't explicitly track what has changed in atom. So
// // we need to save its "whole" value. Because, the replace
// // operation is too general and it may interact with the cache
// // in complex ways, while this method may be called during cache
// // cleanup, we can't use 'replace'. We need a separate version
// // that is careful not to use the cache.
// //
// // rawSave(lHandle.getPersistentHandle(), instance);
// replace(lHandle, instance);
// }
// if ((lHandle.getFlags() & HGSystemFlags.MANAGED) != 0)
// {
// HGManagedLiveHandle mHandle = (HGManagedLiveHandle)lHandle;
// AtomAttrib attrib = getAtomAttributes(mHandle.getPersistentHandle());
// attrib.flags = mHandle.getFlags();
// attrib.retrievalCount += mHandle.getRetrievalCount();
// attrib.lastAccessTime = Math.max(mHandle.getLastAccessTime(), attrib.lastAccessTime);
// setAtomAttributes(lHandle.getPersistentHandle(), attrib);
// } */
// return null;
// }});
// }
// catch (HGException ex)
// {
// throw new HGException("Problem while unloading atom " +
// instance + " of type " + instance.getClass().getName() + " " + ex.getMessage(),
// ex);
// }
// catch (Throwable t)
// {
// throw new HGException("Problem while unloading atom " +
// instance + " of type " + instance.getClass().getName(),
// t);
// }
// }
void updateLinksInIncidenceSet(IncidenceSet incidenceSet, HGLiveHandle liveHandle)
{
HGSearchResult<HGHandle> rs = incidenceSet.getSearchResult();
try
{
while (rs.hasNext())
{
HGLiveHandle lh = cache.get((HGPersistentHandle)rs.next());
if (lh != null)
{
HGLink incidenceLink = (HGLink)lh.getRef();
if (incidenceLink != null) // ref may be null because of cache eviction
updateLinkLiveHandle(incidenceLink, liveHandle);
}
}
}
finally
{
rs.close();
}
}
/**
* Update a link to point to a "live" target instead of holding a
* persistent handle. This is slightly inefficient as it needs
* to loop through all targets of the link. It is here perhaps that
* a distinction b/w ordered and unordered links might become useful
* for efficiency purposes: an unordered link would be able to use
* a hash lookup on its handles to find the one that needs to be updated,
* instead of a linear traversal. On the other, link arities tend to be
* rather small, usually 2-3, so it shouldn't be a problem.
*/
void updateLinkLiveHandle(HGLink link, HGLiveHandle lHandle)
{
int arity = link.getArity();
for (int i = 0; i < arity; i++)
{
HGHandle current = link.getTargetAt(i);
if (current == lHandle)
return;
else if (current.equals(lHandle.getPersistent()))
{
link.notifyTargetHandleUpdate(i, lHandle);
return;
}
}
}
void updateTargetIncidenceSet(HGPersistentHandle targetHandle, HGPersistentHandle linkHandle)
{
store.addIncidenceLink(targetHandle, linkHandle);
IncidenceSet targetIncidenceSet = cache.getIncidenceCache().getIfLoaded(targetHandle);
if (targetIncidenceSet != null)
targetIncidenceSet.add(linkHandle);
}
void updateTargetsIncidenceSets(HGPersistentHandle atomHandle, HGLink link)
{
for (int i = 0; i < link.getArity(); i++)
updateTargetIncidenceSet(getPersistentHandle(link.getTargetAt(i)), atomHandle);
}
private void targetRemoved(HGHandle linkHandle, HGHandle target)
{
HGLink l = (HGLink)get(linkHandle);
int pos = -1;
for (int i = 0; i < l.getArity(); i++)
if (target.equals(l.getTargetAt(i)))
{
pos = i;
break;
}
if (pos > -1)
{
l.notifyTargetRemoved(pos);
replaceTransaction(cache.get(l), getPersistentHandle(linkHandle), l, getType(linkHandle));
}
}
/**
* Remove a link from the incidence set of a given atom. If the link is
* not part of the incidence set of the <code>targetAtom</code>, nothing
* is done.
*
* @param targetAtom The handle of the atom whose incidence set need modification.
* @param incidentLink The <code>HGPersistentHandle</code> of the link to be
* removed - cannot be <code>null</code>.
*/
private void removeFromIncidenceSet(HGPersistentHandle targetAtom,
HGPersistentHandle incidentLink)
{
// Set<HGPersistentHandle> inRemoval = TxAttribute.getSet(getTransactionManager(),
// TxAttribute.IN_REMOVAL,
// HashSet.class);
// Can't remember why atoms currently being removed should keep there incidence sets
// intact, next time a bug shows up related to this, please comment on why the following
// check is need.
// -Borislav
//
// However, if the following is uncommented, there's a problem when deleting
// types: the HGSubsumes links get removed during the type removal transaction
// but they remain in the incidence set (due to the following line) which subsequently
// causes an NPE.
//
// if (inRemoval.contains(targetAtom))
// return;
store.removeIncidenceLink(targetAtom, incidentLink);
IncidenceSet targetIncidenceSet = cache.getIncidenceCache().getIfLoaded(targetAtom);
if (targetIncidenceSet != null)
targetIncidenceSet.remove(incidentLink);
}
/**
* Save the run-time value of an atom back to the database store, without
* disrupting the cache. It is assumed
*
* @param handle The persistent handle of the atom.
* @param instance The run-time value of the atom.
*/
/* private void rawSave(HGPersistentHandle handle, Object instance)
{
HGPersistentHandle [] layout = store.getLink(handle);
if (layout == null)
throw new HGException("Can't unload atom with handle " +
handle +
" it's even in the DB anymore.");
HGAtomType type = (HGAtomType)rawGet(layout[0]);
HGAtomType oldTypeInstance = null;
if (instance instanceof HGAtomType)
oldTypeInstance = (HGAtomType)rawMake(layout, type, handle);
type.release(layout[1]);
indexByValue.removeEntry(layout[1], handle);
layout[1] = type.store(instance);
indexByValue.addEntry(layout[1], handle);
if (oldTypeInstance != null)
{
HGSearchResult<HGPersistentHandle> rs = indexByType.find(handle);
try
{
while (rs.hasNext())
{
HGPersistentHandle h = rs.next();
HGPersistentHandle [] L = store.getLink(h);
if (h == null)
throw new HGException("Can't load layout for atom " + h);
rawSave(h, rawMake(L, oldTypeInstance, h));
}
}
finally
{
rs.close();
}
}
} */
/**
* Make a run-time instance given a layout and a type. Ignore caching,
* incidence set management etc.
*
* @param layout
* @param type
* @return
*/
private Object rawMake(HGPersistentHandle [] layout, HGAtomType type, HGPersistentHandle atomHandle)
{
HGPersistentHandle [] targetSet = EMPTY_PERSISTENT_HANDLE_SET;
if (layout.length > 2)
{
targetSet = new HGPersistentHandle[layout.length - 2];
for (int i = 2; i < layout.length; i++)
targetSet[i - 2] = layout[i];
}
// TypeUtils.initiateAtomConstruction(HyperGraph.this, layout[1]);
Object result = type.make(layout[1],
new ReadyRef<HGHandle[]>(targetSet),
new IncidenceSetRef(atomHandle, this));
// TypeUtils.atomConstructionComplete(HyperGraph.this, layout[1]);
if (targetSet.length > 0 && ! (result instanceof HGLink))
result = new HGValueLink(result, targetSet);
if (result instanceof HGAtomType)
result = typeSystem.toRuntimeInstance(atomHandle, (HGAtomType)result);
return result;
}
/**
* Replace an atom with a new value within a newly created transaction.
*
* @param lHandle The live handle of the atom, if available, can be null...
* @param pHandle The persistent handle of the atom, can't be null
* @param atom The new value of the atom
* @param typeHandle The type of the new value
*/
private void replaceInternal(final HGLiveHandle lHandle,
final HGPersistentHandle pHandle,
final Object atom,
final HGHandle typeHandle)
{
getTransactionManager().ensureTransaction(new Callable<Object>()
{
public Object call() { replaceTransaction(lHandle, pHandle, atom, typeHandle); return null; }
});
}
/**
* Replace an atom with a new value. Recursively replace the values of type atoms.
*
* @param lHandle The live handle of the atom, if available, can be null...
* @param pHandle The persistent handle of the atom, can't be null
* @param atom The new value of the atom
* @param typeHandle The type of the new value
*/
private void replaceTransaction(final HGLiveHandle lHandle,
final HGPersistentHandle pHandle,
final Object atom,
final HGHandle typeHandle)
{
Object newValue = atom;
if (atom instanceof HGValueLink)
newValue = ((HGValueLink)atom).getValue();
HGPersistentHandle [] layout = store.getLink(pHandle);
HGPersistentHandle oldValueHandle = layout[1];
HGPersistentHandle oldTypeHandle = layout[0];
HGAtomType oldType = (HGAtomType)get(oldTypeHandle);
HGAtomType type = (HGAtomType)get(typeHandle);
Object oldValue = null;
if (lHandle != null)
oldValue = lHandle.getRef();
if (oldValue == null || oldValue == atom)
oldValue = rawMake(layout, oldType, pHandle); //rawMake will just construct the instance, without adding to cache
idx_manager.maybeUnindex(getPersistentHandle(typeHandle), type, pHandle, oldValue);
if (oldValue instanceof HGValueLink)
oldValue = ((HGValueLink)oldValue).getValue();
//
// If the atom is a type, we need to morph all its values to the new
// type. This is done simply by recursively replacing instances
// based on the old type atom with instances of the new type.
//
if (oldValue instanceof HGAtomType)
{
HGSearchResult<HGPersistentHandle> rs = null;
try
{
rs = indexByType.find(pHandle);
if (rs.hasNext() && ! (newValue instanceof HGAtomType))
throw new HGException("Attempt to replace a type atom " + pHandle +
" with a non-empty instance set by an atom that is not a HyperGraph type.");
HGAtomType oldTypeValue = (HGAtomType)oldValue;
HGAtomType newTypeValue = (HGAtomType)newValue;
while (rs.hasNext())
morph((HGPersistentHandle)rs.next(), oldTypeValue, newTypeValue);
}
finally
{
rs.close();
}
}
if (!oldTypeHandle.equals(typeHandle))
{
indexByType.removeEntry(getPersistentHandle(oldTypeHandle), pHandle);
indexByType.addEntry(getPersistentHandle(typeHandle), pHandle);
}
TypeUtils.releaseValue(HyperGraph.this, oldType, layout[1]);
//oldType.release(layout[1]);
if (newValue instanceof HGAtomType)
layout[1] = TypeUtils.storeValue(
this,
typeSystem.getSchema().fromRuntimeType(pHandle, (HGAtomType)newValue),
type);
else
layout[1] = TypeUtils.storeValue(this, newValue, type);
layout[0] = getPersistentHandle(typeHandle);
indexByValue.removeEntry(oldValueHandle, pHandle);
indexByValue.addEntry(layout[1], pHandle);
HGPersistentHandle [] newLayout;
if (atom instanceof HGLink)
{
HGLink newLink = (HGLink)atom;
newLayout = new HGPersistentHandle[newLink.getArity() + 2];
// If we are replacing a link by a link. We need to compute the
// delta of the target sets and remove the link from incidence
// sets where it no longer belongs.
HashSet<HGPersistentHandle> newTargets = new HashSet<HGPersistentHandle>();
for (int i = 0; i < newLink.getArity(); i++)
{
HGPersistentHandle target = getPersistentHandle(newLink.getTargetAt(i));
newLayout[2 + i] = target;
newTargets.add(target);
}
for (int i = 2; i < layout.length; i++)
if (!newTargets.remove(layout[i])) // remove targets that were there before, so we don't touch them below
removeFromIncidenceSet(layout[i], pHandle);
for (HGPersistentHandle newTarget : newTargets)
updateTargetIncidenceSet(newTarget, pHandle);
}
else
{
newLayout = new HGPersistentHandle[2];
for (int i = 2; i < layout.length; i++)
removeFromIncidenceSet(layout[i], pHandle);
}
newLayout[0] = layout[0];
newLayout[1] = layout[1];
store.store(pHandle, newLayout);
idx_manager.maybeIndex(getPersistentHandle(typeHandle), type, pHandle, atom);
if (atom instanceof HGGraphHolder)
((HGGraphHolder)atom).setHyperGraph(this);
if (atom instanceof HGHandleHolder)
((HGHandleHolder)atom).setAtomHandle(lHandle);
if (lHandle != null)
cache.atomRefresh(lHandle, atom, true);
}
/**
* Change the type value of an atom value. It is assumed that oldType and newType keep
* the same handle. This is used when we are replacing the value of a type atom with another
* value (which must be a compatible type value). Morphing can potentially lead to a
* different run-time instance of the atom. So, if the atom is "alive", we replace the
* run-time instance.
*
* @param instanceHandle The atom
* @param oldType the old type instance
* @param newType the new type instance
*/
private void morph(HGPersistentHandle instanceHandle, HGAtomType oldType, HGAtomType newType)
{
HGPersistentHandle [] layout = store.getLink(instanceHandle);
Object oldInstance = rawMake(layout, oldType, instanceHandle);
TypeUtils.releaseValue(this, oldType, layout[1]);
//2012.08.07 hilpold already called in previous method: oldType.release(layout[1]);
indexByValue.removeEntry(layout[1], instanceHandle);
layout[1] = TypeUtils.storeValue(this, oldInstance, newType);
indexByValue.addEntry(layout[1], instanceHandle);
store.store(instanceHandle, layout);
Object newInstance = rawMake(layout, newType, instanceHandle);
HGLiveHandle instanceLiveHandle = cache.get(instanceHandle);
if (instanceLiveHandle != null && instanceLiveHandle.getRef() != null)
cache.atomRefresh(instanceLiveHandle, newInstance, true);
if (oldInstance instanceof HGAtomType)
{
HGSearchResult<HGPersistentHandle> rs = null;
try
{
rs = indexByType.find(instanceHandle);
if (rs.hasNext() && ! (newInstance instanceof HGAtomType))
throw new HGException("Cannot replace value of atom " + instanceHandle +
" which was a type with something that is not a type");
oldType = (HGAtomType)oldInstance;
newType = (HGAtomType)newInstance;
while (rs.hasNext())
{
morph((HGPersistentHandle)rs.next(), oldType, newType);
}
}
finally
{
if (rs != null) rs.close();
}
}
// if newInstance is a type, but not oldInstance, we're ok...
}
private void initAtomManagement()
{
//
// Init HGStats atom.
//
getTransactionManager().transact(new Callable<Object>() { public Object call() {
statsHandle = hg.findOne(HyperGraph.this, hg.type(HGStats.class));
if (statsHandle == null)
statsHandle = add(stats);
else
stats = get(statsHandle);
return null; }});
// HGSearchResult<HGPersistentHandle> rs = null;
//
// try
// {
// rs = find(new AtomTypeCondition(typeSystem.getTypeHandle(HGStats.class)));
// if (rs.hasNext())
// {
// statsHandle = rs.next();
// stats = (HGStats)get(statsHandle);
// }
// else
// {
// statsHandle = add(stats);
// }
// }
// catch (HGException ex)
// {
// throw ex;
// }
// catch (Throwable t)
// {
// throw new HGException(t);
// }
// finally
// {
// if (rs != null) rs.close();
// }
//
// eventManager.addListener(
// HGAtomEvictEvent.class,
// new HGListener()
// {
// public HGListener.Result handle(HyperGraph hg, HGEvent event)
// {
// HGAtomEvictEvent ev = (HGAtomEvictEvent)event;
// unloadAtom((HGLiveHandle)ev.getAtomHandle(), ev.getInstance());
// return Result.ok;
// }
// }
// );
}
private void loadListeners()
{
HGSearchResult<HGHandle> rs = null;
try
{
rs = find(new AtomTypeCondition(typeSystem.getTypeHandle(HGListenerAtom.class)));
while (rs.hasNext())
{
HGListenerAtom listenerAtom = (HGListenerAtom)get((HGHandle)rs.next());
Class<?> eventClass;
Class<?> listenerClass;
try
{
eventClass = Class.forName(listenerAtom.getEventClassName());
listenerClass = Class.forName(listenerAtom.getListenerClassName());
eventManager.addListener((Class<HGEvent>)eventClass, (HGListener)listenerClass.newInstance());
}
catch (Throwable t)
{
logger.exception(t);
}
}
}
catch (Throwable t)
{
throw new HGException(t);
}
finally
{
if (rs != null) rs.close();
}
}
private HGAtomAttrib getAtomAttributes(HGPersistentHandle handle)
{
return systemAttributesDB.findFirst(handle);
}
private void setAtomAttributes(HGPersistentHandle handle, HGAtomAttrib attribs)
{
systemAttributesDB.removeAllEntries(handle);
systemAttributesDB.addEntry(handle, attribs);
}
private void removeAtomAttributes(HGPersistentHandle handle)
{
systemAttributesDB.removeAllEntries(handle);
}
protected void finalize() throws Throwable
{
try { close(); } catch (Throwable t) { }
}
}