org.datanucleus.store.db4o.DB4OStoreManager Maven / Gradle / Ivy
/**********************************************************************
Copyright (c) 2006 Andy Jefferson and others. All rights reserved.
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.
Contributors:
2007 Xuan Baldauf - Major improvements in the way DB4O is accessed. See CORE-3272
2007 Xuan Baldauf - Simplified and optimized the access of the DB4O database. Previously, for every access, a new
database connection was made, and the old database connection was flushed, resulting in inferior performance.
2007 Xuan Baldauf - Use DatastoreUniqueOID to save memory
2007 Andy Jefferson - removed numerous pointless "if (true)" lines that should not be in CVS
...
**********************************************************************/
package org.datanucleus.store.db4o;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import org.datanucleus.ClassLoaderResolver;
import org.datanucleus.NucleusContext;
import org.datanucleus.PersistenceConfiguration;
import org.datanucleus.exceptions.NucleusException;
import org.datanucleus.identity.OIDFactory;
import org.datanucleus.metadata.AbstractClassMetaData;
import org.datanucleus.metadata.AbstractMemberMetaData;
import org.datanucleus.metadata.IdentityStrategy;
import org.datanucleus.metadata.IdentityType;
import org.datanucleus.store.AbstractStoreManager;
import org.datanucleus.store.ExecutionContext;
import org.datanucleus.store.Extent;
import org.datanucleus.store.ObjectProvider;
import org.datanucleus.store.ObjectReferencingStoreManager;
import org.datanucleus.store.StoreData;
import org.datanucleus.store.exceptions.NoExtentException;
import org.datanucleus.util.Localiser;
import org.datanucleus.util.NucleusLogger;
import org.datanucleus.util.StringUtils;
import com.db4o.Db4o;
import com.db4o.ObjectContainer;
import com.db4o.config.Configuration;
import com.db4o.config.ObjectClass;
import com.db4o.config.ObjectField;
/**
* Store Manager for DB4O (http://www.db4o.com).
*
* DataNucleus will select this StoreManager with URLs of the form "db4o:...".
* Support for DB4O is for the following URLs
*
* - db4o:{filename} - uses a local DB4O database in the named file (no username/password needed)
* - db4o:server:{filename} - embedded DB4O server in the named file (no username/password needed)
* - db4o:{hostname}:{port} - uses a remote DB4O database with the specified username/password
*
* These URLs are specific to our support since DB4O doesn't use URLs to define the datastore.
*
* Object Activation and Field Loading
*
* DB4O provides methods to hand out memory versions of the datastore objects. Each object has to be
* "activated" to have its values accessible. Similarly the object can be "deactivated" when it is
* no longer needed (freeing up resources, and the link to the disk object). When we retrieve an object
* from DB4O it activates the object. This makes all fields accessible. If however one of the fields is
* a PC object (or collection, or map, or array etc) that object itself is not activated. Consequently
* all fields are accessible from the start (unlike with RDBMS), but the StateManager manages the list
* of fields that are considered "loaded", and this is initially just those in the DFG (even though others
* are available). When the user accesses one of these "not-loaded" fields DB4OManager.fetchObject is called
* and that field is marked as loaded (if it is a SCO mutable it is wrapped, and if it is a PC object it
* has a StateManager connected and is activated).
*
* Persistence
*
* Each object is persisted on its own, and we dont use DB4O's internal cascade mechanism. Instead
* the DB4OManager.insertObject, or DB4OManager.updateObject methods are called for each object that
* is to be persisted/updated. Each call to insertObject/updateObject provides reachability by use
* of PersistFieldManager.
*
* Deletion
*
* Currently objects are deleted using DB4O's internal cascade mechanism.
*
* Transactions and Connections
*
* Refer to ConnectionFactoryImpl. In simple terms each ObjectManager has a DB4O ObjectContainer associated
* with it, and this is retained until the end of life of the ObjectManager. With DB4O in server mode this
* allows use of multiple PMs on the same underlying datastore. With DB4O in file mode you can only
* have one PM operating on the same underlying datastore at once. This is a DB4O limitation rather than ours.
*
*/
public class DB4OStoreManager extends AbstractStoreManager implements ObjectReferencingStoreManager
{
/** Localiser for messages. */
protected static final Localiser LOCALISER_DB4O = Localiser.getInstance(
"org.datanucleus.store.db4o.Localisation", DB4OStoreManager.class.getClassLoader());
/**
* Collection of the currently active ObjectContainers.
* Used for providing class mapping information when they are found.
*/
private Set activeObjectContainers = new HashSet();
/**
* Constructor for a new DB4O StoreManager.
* Stores the basic information required for the datastore management.
* @param clr the ClassLoaderResolver
* @param ctx The corresponding context.
* @param props Properties for the datastore
*/
public DB4OStoreManager(ClassLoaderResolver clr, NucleusContext ctx, Map props)
{
super("db4o", clr, ctx, props);
PersistenceConfiguration conf = ctx.getPersistenceConfiguration();
if (!conf.getStringProperty("datanucleus.cache.level2.type").equalsIgnoreCase("none"))
{
// TODO Remove this when we can handle getting objects from the L2 cache
NucleusLogger.PERSISTENCE.warn("DB4O StoreManager is not fully supported when using the L2 cache");
}
// Use unique datastore-identity ids
conf.setProperty("datanucleus.datastoreIdentityType", "unique");
// Log the manager configuration
logConfiguration();
// Handler for persistence process
persistenceHandler = new DB4OPersistenceHandler(this);
// Make sure transactional connection factory has listener for closing object container
ctx.addObjectManagerListener(new ExecutionContext.LifecycleListener()
{
public void preClose(ExecutionContext ec)
{
// Close any ObjectContainer for this ObjectManager
ConnectionFactoryImpl cf =
(ConnectionFactoryImpl)connectionMgr.lookupConnectionFactory(txConnectionFactoryName);
cf.closeObjectContainerForObjectManager(ec);
}
});
// Initialise the auto start process
initialiseAutoStart(clr);
}
/**
* Release of resources
*/
public void close()
{
super.close();
activeObjectContainers.clear();
}
/* (non-Javadoc)
* @see org.datanucleus.store.AbstractStoreManager#getStrategyForNative(org.datanucleus.metadata.AbstractClassMetaData, int)
*/
@Override
protected String getStrategyForNative(AbstractClassMetaData cmd, int absFieldNumber)
{
return "increment";
}
/**
* Convenience method to log the configuration of this store manager.
*/
protected void logConfiguration()
{
super.logConfiguration();
if (NucleusLogger.DATASTORE.isDebugEnabled())
{
// DB4O specific configuration
NucleusLogger.DATASTORE.debug("DB4O Version : " + Db4o.version());
PersistenceConfiguration conf = nucleusContext.getPersistenceConfiguration();
String outputFilename = conf.getStringProperty("datanucleus.db4o.outputFile");
if (outputFilename != null)
{
NucleusLogger.DATASTORE.debug("DB4O Output : " + outputFilename);
}
// TODO Add db4o global properties here
NucleusLogger.DATASTORE.debug("===========================================================");
}
}
// ------------------------------- Class Management -----------------------------------
/**
* Method to register some data with the store.
* This will also register the data with the starter process.
* @param data The StoreData to add
*/
protected void registerStoreData(StoreData data)
{
// Register the data
super.registerStoreData(data);
// Make this class config known to all active ObjectContainers for this store
if (activeObjectContainers.size() > 0)
{
Iterator containerIter = activeObjectContainers.iterator();
while (containerIter.hasNext())
{
ObjectContainer cont = (ObjectContainer)containerIter.next();
supportClassInDB4O(cont.ext().configure(), (AbstractClassMetaData)data.getMetaData());
}
}
}
/**
* Method to register an ObjectContainer as active on this store.
* Will load up all known class mapping information into the datastore container.
* @param cont ObjectContainer
*/
public void registerObjectContainer(ObjectContainer cont)
{
if (cont == null)
{
return;
}
// Register all known classes with the ObjectContainer of this transaction
Configuration conf = cont.ext().configure();
Collection storeDataValues = storeDataMgr.getManagedStoreData();
Iterator iter = storeDataValues.iterator();
while (iter.hasNext())
{
StoreData data = (StoreData)iter.next();
supportClassInDB4O(conf, (AbstractClassMetaData)data.getMetaData());
}
activeObjectContainers.add(cont);
}
/**
* Method to deregister an ObjectContainer from this store.
* ObjectContainers are deregistered when about to be closed and hence not interested in more class mapping information.
* @param cont ObjectContainer
*/
public void deregisterObjectContainer(ObjectContainer cont)
{
if (cont == null)
{
return;
}
activeObjectContainers.remove(cont);
}
/**
* Convenience method to support the specified class in DB4O.
* Prepares the DB4O environment for the persistence of this class (indexed fields, persist-cascade etc).
* TODO This will need update in the future to provide for schema updates, so where a class definition has
* changed and we need to set the changes in DB4O.
* @param conf The DB4O Configuration to add the class support to
* @param cmd MetaData for the class
*/
private void supportClassInDB4O(Configuration conf, AbstractClassMetaData cmd)
{
ObjectClass objectCls = conf.objectClass(cmd.getFullClassName());
if (cmd.isVersioned())
{
// Class has version requirements so DB4O should use versioning
objectCls.generateVersionNumbers(true);
}
objectCls.cascadeOnUpdate(false); // Make sure we don't update cascade
// TODO Allow option of having UUID datastore identities
// objectCls.generateUUIDs(true);
AbstractMemberMetaData[] fmds = cmd.getManagedMembers();
for (int i=0;i