org.datanucleus.flush.OperationQueue Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of datanucleus-core Show documentation
Show all versions of datanucleus-core Show documentation
DataNucleus Core provides the primary components of a heterogenous Java persistence solution.
It supports persistence API's being layered on top of the core functionality.
/**********************************************************************
Copyright (c) 2010 Peter Dettman 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:
2013 Andy Jefferson - converted to general purpose queue for all operations
...
**********************************************************************/
package org.datanucleus.flush;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import org.datanucleus.ExecutionContext;
import org.datanucleus.state.DNStateManager;
import org.datanucleus.store.types.SCOUtils;
import org.datanucleus.store.types.scostore.CollectionStore;
import org.datanucleus.store.types.scostore.ListStore;
import org.datanucleus.store.types.scostore.MapStore;
import org.datanucleus.store.types.scostore.Store;
import org.datanucleus.util.Localiser;
import org.datanucleus.util.NucleusLogger;
import org.datanucleus.util.StringUtils;
/**
* Queue of operations to be performed when operating in MANUAL FlushMode.
* This queue will typically contain operations on second class objects, such as Collections or Maps.
* They are queued in the order they are invoked by the user.
* The queue will contain all operations to be performed for an ExecutionContext. There are two methods for
* processing them, one processing all (for use in the future when we support that mode in full), and one
* that processes all for a particular SCO (backing store).
*/
public class OperationQueue
{
protected List queuedOperations = new ArrayList<>();
/**
* Method to add the specified operation to the operation queue.
* @param oper Operation
*/
public void enqueue(Operation oper)
{
DNStateManager sm = oper.getStateManager();
if (oper instanceof SCOOperation)
{
if (sm.isWaitingToBeFlushedToDatastore())
{
// Don't enlist since not yet flushed
NucleusLogger.PERSISTENCE.info(">> OperationQueue : not adding operation since owner not yet flushed - " + oper);
return;
}
}
queuedOperations.add(oper);
}
/**
* Convenience method to log the current operation queue.
*/
public void log()
{
NucleusLogger.PERSISTENCE.debug(">> OperationQueue :" + (queuedOperations.isEmpty() ? " Empty" : ("" + queuedOperations.size() + " operations")));
for (Operation oper : queuedOperations)
{
NucleusLogger.PERSISTENCE.debug(">> " + oper);
}
}
public void clear()
{
queuedOperations.clear();
}
/**
* Method to provide access to inspect the queued operations. The returned list is unmodifiable.
* @return The queued operations
*/
public List getOperations()
{
return Collections.unmodifiableList(queuedOperations);
}
public void removeOperations(List removedOps)
{
queuedOperations.removeAll(removedOps);
}
/**
* Method to perform all operations queued.
* Those operations are then removed from the queue.
*/
public void performAll()
{
for (Operation op : queuedOperations)
{
op.perform();
}
queuedOperations.clear();
}
/**
* Method to perform all operations queued for the specified StateManager and backing store.
* Those operations are then removed from the queue.
* @param store The backing store
* @param sm StateManager
*/
public void performAll(Store store, DNStateManager sm)
{
if (NucleusLogger.PERSISTENCE.isDebugEnabled())
{
NucleusLogger.PERSISTENCE.debug(Localiser.msg("023005", sm.getObjectAsPrintable(), store.getOwnerMemberMetaData().getFullFieldName()));
}
// Extract those operations for the specified backing store
List flushOperations = new ArrayList();
ListIterator operIter = queuedOperations.listIterator();
while (operIter.hasNext())
{
Operation oper = operIter.next();
if (oper.getStateManager() == sm && oper instanceof SCOOperation && ((SCOOperation)oper).getStore() == store)
{
// Process operations for this Store and this StateManager
flushOperations.add(oper);
operIter.remove();
}
}
// TODO Cater for Lists where cascade delete is enabled but we want to only allow cascade delete if the element isn't later added at a different place in the list.
ListIterator flushOperIter = flushOperations.listIterator();
while (flushOperIter.hasNext())
{
Operation oper = flushOperIter.next();
if (store instanceof CollectionStore)
{
if (!(store instanceof ListStore))
{
if (isAddFollowedByRemoveOnSameSCO(store, sm, oper, flushOperIter))
{
// add+remove of the same element - ignore this and the next one
flushOperIter.next();
}
else if (isRemoveFollowedByAddOnSameSCO(store, sm, oper, flushOperIter))
{
// remove+add of the same element - ignore this and the next one
flushOperIter.next();
}
else
{
oper.perform();
}
}
else
{
oper.perform();
}
}
else if (store instanceof MapStore)
{
if (isPutFollowedByRemoveOnSameSCO(store, sm, oper, flushOperIter))
{
// put+remove of the same key - ignore this and the next one
flushOperIter.next();
}
else
{
oper.perform();
}
}
else
{
oper.perform();
}
}
}
public void clearPersistDeleteUpdateOperations()
{
if (queuedOperations != null)
{
Iterator opsIter = queuedOperations.iterator();
while (opsIter.hasNext())
{
Operation op = opsIter.next();
if (op instanceof PersistOperation || op instanceof DeleteOperation || op instanceof UpdateMemberOperation)
{
opsIter.remove();
}
}
}
}
/**
* Method to process all SCO operations where the SCOs don't use a backing store.
* This will check for add+remove/remove+add and perform cascade delete as appropriate.
* It will then remove the SCO operations from the queue. It doesn't actually process the queued operations since when you have
* no backing store there is nothing to do.
* @param ec ExecutionContext
*/
public void processOperationsForNoBackingStoreSCOs(ExecutionContext ec)
{
if (queuedOperations != null && !ec.getStoreManager().usesBackedSCOWrappers())
{
// Make use of OperationQueue for any cascade deletes that may be needed as a result of removal from collections/maps
List opersToIgnore = new ArrayList<>();
List