All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.openjpa.abstractstore.AbstractStoreManager Maven / Gradle / Ivy

There is a newer version: 4.0.1
Show newest version
/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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 org.apache.openjpa.abstractstore;

import java.util.BitSet;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedList;

import org.apache.openjpa.conf.OpenJPAConfiguration;
import org.apache.openjpa.conf.OpenJPAConfigurationImpl;
import org.apache.openjpa.kernel.FetchConfiguration;
import org.apache.openjpa.kernel.FetchConfigurationImpl;
import org.apache.openjpa.kernel.OpenJPAStateManager;
import org.apache.openjpa.kernel.PCState;
import org.apache.openjpa.kernel.Seq;
import org.apache.openjpa.kernel.StoreContext;
import org.apache.openjpa.kernel.StoreManager;
import org.apache.openjpa.kernel.StoreQuery;
import org.apache.openjpa.lib.rop.ResultObjectProvider;
import org.apache.openjpa.meta.ClassMetaData;
import org.apache.openjpa.meta.FieldMetaData;
import org.apache.openjpa.meta.JavaTypes;
import org.apache.openjpa.meta.ValueStrategies;
import org.apache.openjpa.util.ApplicationIds;
import org.apache.openjpa.util.Id;
import org.apache.openjpa.util.ImplHelper;

/**
 * Abstract store manager implementation to ease development of custom
 * OpenJPA back-ends. A concrete subclass must define implementations for the
 * following methods:
 * 
    *
  • {@link StoreManager#exists}
  • *
  • {@link #initialize}
  • *
  • {@link #load}
  • *
  • {@link * #flush(Collection,Collection,Collection,Collection,Collection)}
  • *
  • {@link #executeExtent}
  • *
Additionally, subclasses should not attempt to acquire resources * until {@link #open} has been called. Store manager instances might be * created to call metadata methods such as {@link #newConfiguration} or * {@link #getUnsupportedOptions} and never opened. These instances should * not consume any data store resources. * Notes: *
    *
  • The {@link StoreManager#initialize} method is responsible * for creating new instances of objects freshly loaded from the * database. The method will be invoked with a {@link OpenJPAStateManager} * that the newly-loaded object should be associated with. To create the * new object and set up this association correctly, the implementation * should use the {@link OpenJPAStateManager#initialize} method.
  • *
  • If your data store supports some sort of transaction or * unit of work, you should override the {@link #begin}, {@link #commit}, * and {@link #rollback} methods.
  • *
  • This class provides no infrastructure support for optimistic * transactions. To provide optimistic transaction support: *
      *
    • Override {@link #beginOptimistic}, {@link #rollbackOptimistic}, * and {@link #syncVersion}.
    • *
    • Override {@link #getUnsupportedOptions} to not include {@link * OpenJPAConfiguration#OPTION_OPTIMISTIC} in the list of unsupported * options.
    • *
    • Ensure that your flush implementation sets the next * version for each modified object via the {@link * OpenJPAStateManager#setNextVersion} method.
    • *
    • If your version object does not implement {@link Comparable}, * override {@link #compareVersion}, which relies on the * {@link Comparable#compareTo} method.
    • *
  • *
  • If your data store supports a mechanism for automatically * generating and managing identity values (or if you want to * provide that facility on top of your data store), implement * the {@link #getDataStoreIdSequence} method if you want to use a * long as your datastore identity type and are * happy with OpenJPA's {@link Id} class. To use another datastore identity * type, override {@link #getManagedType}, * {@link #getDataStoreIdType}, {@link #copyDataStoreId}, and * {@link #newDataStoreId} instead. In either case, override * {@link #getUnsupportedOptions} to not include * {@link OpenJPAConfiguration#OPTION_ID_DATASTORE} in the list of * unsupported options.
  • *
  • If your data store does not support queries (or if you do * not want to convert OpenJPA's query parse tree into a * datastore-specific query), you still have two options in terms * of query execution: *
      *
    • In-memory execution: If you * execute a query against an extent or a class, OpenJPA will * automatically load the full extent of objects into memory and * execute the query in memory.
    • *
    • openjpa.MethodQL: MethodQL allows * you to use the query APIs to execute a method that finds * data in your back-end and returns that data as a * {@link org.apache.openjpa.lib.rop.ResultList}. For more details on * MethodQL, see the OpenJPA Reference Guide.
    • *
  • *
* * @since 0.3.1 */ public abstract class AbstractStoreManager implements StoreManager { protected StoreContext ctx; public final void setContext(StoreContext ctx) { this.ctx = ctx; open(); } /** * Returns the {@link StoreContext} that this store manager is * associated with. */ public StoreContext getContext() { return ctx; } /** * No-op implementation. Ready this store manager for persistent operations. */ protected void open() { } /** * No-op implementation. Override this method to provide optimistic * locking semantics for your data store if you need notification of * the beginning of an optimistic transaction. */ public void beginOptimistic() { } /** * No-op implementation. Override this method to provide optimistic * locking semantics for your data store if you need notification of * a rollback of an optimistic transaction before {@link #begin} is invoked. */ public void rollbackOptimistic() { } /** * OpenJPA assumes that after this method is invoked, all data * accesses through this store manager will be part of a single * unit of work that can be rolled back. * This is a no-op implementation. If your data store does not * support any concept of locking or transactions, you need not * override this method. */ public void begin() { } /** * This is a no-op implementation. If your data store does not * have a concept of transactions or a unit of work, you need not * override this method. If it does, then override this method to * notify the data store that the current transaction should be committed. */ public void commit() { } /** * This is a no-op implementation. If your data store does not * have a concept of transactions or a unit of work, you need not * override this method. If it does, then override this method to * notify the data store that the current transaction should be rolled back. */ public void rollback() { } /** * Since this store manager does not provide optimistic locking * support, this method always returns true. */ public boolean syncVersion(OpenJPAStateManager sm, Object edata) { return true; } /** * This method is invoked when OpenJPA needs to load an object whose * identity is known but which has not yet been loaded from the data * store. sm is a partially-set-up state manager for this * object. The ID and least-derived type information for the instance * to load can be obtained by invoking * sm.getObjectId() and sm.getMetaData(). * * When implementing this method, load the data for this object from * the data store, determine the most-derived subclass of the newly-loaded * data, and then use the {@link OpenJPAStateManager#initialize} method to * populate sm with a new instance of the appropriate type. * Once {@link OpenJPAStateManager#initialize} has been invoked, proceed to * load field data into sm as in the {@link #load} method, by * using {@link OpenJPAStateManager#store} (or the appropriate * OpenJPAStateManager.storetype method) to put the * data into the object. */ public abstract boolean initialize(OpenJPAStateManager sm, PCState state, FetchConfiguration fetch, Object edata); /** * This method is invoked when OpenJPA needs to load additional data * into an object that has already been at least partially loaded by * a previous {@link #initialize} invocation. * Load data into sm by using {@link * OpenJPAStateManager#store} (or the appropriate * OpenJPAStateManager.storetype method) to put the * data into the object. */ public abstract boolean load(OpenJPAStateManager sm, BitSet fields, FetchConfiguration fetch, int lockLevel, Object edata); /** * This implementation just delegates to the proper singular * method ({@link StoreManager#initialize} or {@link StoreManager#load}) * depending on each state manager's state. If your data store provides * bulk loading APIs, overriding this method to be more clever may be * advantageous. */ public Collection loadAll(Collection sms, PCState state, int load, FetchConfiguration fetch, Object edata) { return ImplHelper.loadAll(sms, this, state, load, fetch, edata); } /** * Breaks down states based on the objects' current * states, and delegates to * {@link #flush(Collection,Collection,Collection,Collection,Collection)}. */ public Collection flush(Collection sms) { // break down state managers by state; initialize as empty lists; // use constants for efficiency Collection pNew = new LinkedList(); Collection pNewUpdated = new LinkedList(); Collection pNewFlushedDeleted = new LinkedList(); Collection pDirty = new LinkedList(); Collection pDeleted = new LinkedList(); for (OpenJPAStateManager sm : sms) { if (sm.getPCState() == PCState.PNEW && !sm.isFlushed()) pNew.add(sm); else if (sm.getPCState() == PCState.PNEW && sm.isFlushed()) pNewUpdated.add(sm); else if (sm.getPCState() == PCState.PNEWFLUSHEDDELETED) pNewFlushedDeleted.add(sm); else if (sm.getPCState() == PCState.PDIRTY) pDirty.add(sm); else if (sm.getPCState() == PCState.PDELETED) pDeleted.add(sm); } // no dirty instances to flush? if (pNew.isEmpty() && pNewUpdated.isEmpty() && pNewFlushedDeleted.isEmpty() && pDirty.isEmpty() && pDeleted.isEmpty()) return Collections.EMPTY_LIST; return flush(pNew, pNewUpdated, pNewFlushedDeleted, pDirty, pDeleted); } public void beforeStateChange(OpenJPAStateManager sm, PCState fromState, PCState toState) { } public boolean assignObjectId(OpenJPAStateManager sm, boolean preFlush) { ClassMetaData meta = sm.getMetaData(); if (meta.getIdentityType() == ClassMetaData.ID_APPLICATION) return ApplicationIds.assign(sm, this, preFlush); // datastore identity Object val = ImplHelper.generateIdentityValue(ctx, meta, JavaTypes.LONG); return assignDataStoreId(sm, val); } /** * Assign a new datastore identity to the given instance. This given * value may be null. */ protected boolean assignDataStoreId(OpenJPAStateManager sm, Object val) { ClassMetaData meta = sm.getMetaData(); if (val == null && meta.getIdentityStrategy() != ValueStrategies.NATIVE) return false; if (val == null) val = getDataStoreIdSequence(meta).next(ctx, meta); sm.setObjectId(newDataStoreId(val, meta)); return true; } public boolean assignField(OpenJPAStateManager sm, int field, boolean preFlush) { FieldMetaData fmd = sm.getMetaData().getField(field); Object val = ImplHelper.generateFieldValue(ctx, fmd); if (val == null) return false; sm.store(field, val); return true; } public Class getManagedType(Object oid) { if (oid instanceof Id) return ((Id) oid).getType(); return null; } public Class getDataStoreIdType(ClassMetaData meta) { return Id.class; } public Object copyDataStoreId(Object oid, ClassMetaData meta) { Id id = (Id) oid; return new Id(meta.getDescribedType(), id.getId(), id.hasSubclasses()); } public Object newDataStoreId(Object val, ClassMetaData meta) { // we use base types for all oids while (meta.getPCSuperclass() != null) meta = meta.getPCSuperclassMetaData(); return Id.newInstance(meta.getDescribedType(), val); } /** * Override to retain a dedicated connection. */ public void retainConnection() { } /** * Override to release previously-retained connection. */ public void releaseConnection() { } /** * Returns null. If your data store can provide a * distinct connection object, return it here. */ public Object getClientConnection() { return null; } /** * Create a {@link ResultObjectProvider} that can return all instances * of type, optionally including subclasses as defined * by subclasses. * The implementation of the result provider will typically execute * some sort of data store query to find all the applicable objects, loop * through the results, extracting object IDs from the data, and invoke * {@link StoreContext#find(Object,FetchConfiguration,BitSet,Object,int)} * on each OID. When invoking this method, the first argument is the OID. * The second is the given fetch configuration. The * third argument is a mask of fields to exclude from loading; it will * typically be null. The fourth argument is an object that will be passed * through to {@link #initialize} or {@link #load}, and typically will * contain the actual data to load. For example, for a JDBC-based store * manager, this might be the result set that is being iterated over. If * this argument is null, then the {@link #initialize} or * {@link #load} method will have to issue another command to the data * store in order to fetch the data to be loaded. */ public abstract ResultObjectProvider executeExtent(ClassMetaData meta, boolean subs, FetchConfiguration fetch); public StoreQuery newQuery(String language) { return null; } public FetchConfiguration newFetchConfiguration() { return new FetchConfigurationImpl(); } /** * Casts v1 and v2 to {@link Comparable}, and * invokes v1.compareTo (v2). If v1 is less * than v2, returns {@link #VERSION_EARLIER}. If the same, * returns {@link #VERSION_SAME}. Otherwise, returns {@link * #VERSION_LATER}. If either v1 or v2 are * null, returns {@link #VERSION_DIFFERENT}. */ public int compareVersion(OpenJPAStateManager state, Object v1, Object v2) { if (v1 == null || v2 == null) return VERSION_DIFFERENT; int compare = ((Comparable) v1).compareTo((Comparable) v2); if (compare < 0) return VERSION_EARLIER; if (compare == 0) return VERSION_SAME; return VERSION_LATER; } /** * Returns the system-configured sequence. To use some other sort * of datastore identifier (a GUID, string, or someting of that nature), * override {@link #getManagedType}, * {@link #getDataStoreIdType}, {@link #copyDataStoreId}, * {@link #newDataStoreId}. */ public Seq getDataStoreIdSequence(ClassMetaData forClass) { return ctx.getConfiguration().getSequenceInstance(); } /** * Returns null. */ public Seq getValueSequence(FieldMetaData forField) { return null; } /** * Returns false. If your data store supports * cancelling queries, this method should cancel any * currently-running queries and return true if any * were cancelled. */ public boolean cancelAll() { return false; } public void close() { } /** * Responsible for writing modifications happened back to the data * store. If you do not remove the * {@link OpenJPAConfiguration#OPTION_INC_FLUSH} option in * {@link #getUnsupportedOptions}, this will be called only once at the * end of a transaction. Otherwise, it may be called periodically * throughout the course of a transaction. * If this store manager supports optimistic transactions, datastore * version information should be updated during flush, and the state * manager's version indicator should be updated through the * {@link OpenJPAStateManager#setNextVersion} method. * This method will only be invoked if there are meaningful changes * to store. This differs from the behavior of {@link StoreManager#flush}, * which may be invoked with a collection of objects in states that * do not require any datastore action (for example, objects in the * transient-transactional state). * * @param pNew Objects that should be added to the store, * and that have not previously been flushed. * @param pNewUpdated New objects that have been modified since * they were initially flushed. These were * in persistentNew in an earlier flush invocation. * @param pNewFlushedDeleted New objects that have been deleted since * they were initially flushed. These were * in persistentNew in an earlier flush invocation. * @param pDirty Objects that were loaded from the data * store and have since been modified. * @param pDeleted Objects that were loaded from the data * store and have since been deleted. These * may have been in a previous flush invocation's persistentDirty list. * @return a collection of exceptions encountered during flushing. */ protected abstract Collection flush(Collection pNew, Collection pNewUpdated, Collection pNewFlushedDeleted, Collection pDirty, Collection pDeleted); /** * Return a new configuration instance for this runtime. Configuration * data is maintained at the factory level and is available to all OpenJPA * components; therefore it is a good place to maintain shared resources * such as connection pools, etc. */ protected OpenJPAConfiguration newConfiguration() { return new OpenJPAConfigurationImpl(); } /** * Returns a set of option names that this store manager does * not support. By default, returns the following: *
    *
  • {@link OpenJPAConfiguration#OPTION_OPTIMISTIC}
  • *
  • {@link OpenJPAConfiguration#OPTION_ID_DATASTORE}
  • *
  • {@link OpenJPAConfiguration#OPTION_INC_FLUSH}
  • *
  • {@link OpenJPAConfiguration#OPTION_VALUE_AUTOASSIGN}
  • *
  • {@link OpenJPAConfiguration#OPTION_VALUE_INCREMENT}
  • *
  • {@link OpenJPAConfiguration#OPTION_DATASTORE_CONNECTION}
  • *
*/ protected Collection getUnsupportedOptions() { Collection c = new HashSet(); c.add(OpenJPAConfiguration.OPTION_OPTIMISTIC); c.add(OpenJPAConfiguration.OPTION_ID_DATASTORE); c.add(OpenJPAConfiguration.OPTION_INC_FLUSH); c.add(OpenJPAConfiguration.OPTION_VALUE_AUTOASSIGN); c.add(OpenJPAConfiguration.OPTION_VALUE_INCREMENT); c.add(OpenJPAConfiguration.OPTION_DATASTORE_CONNECTION); return c; } /** * Returns a string name to identify the platform of this * store manager. Returns the class name of this store manager by default. */ protected String getPlatform () { return getClass ().getName (); } }