/*******************************************************************************
* Copyright (c) 2004, 2010 BREDEX GmbH.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* BREDEX GmbH - initial API and implementation and/or initial documentation
*******************************************************************************/
package org.eclipse.jubula.client.core.persistence;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.persistence.EntityManager;
import javax.persistence.EntityNotFoundException;
import javax.persistence.EntityTransaction;
import javax.persistence.PersistenceException;
import org.eclipse.jubula.client.core.businessprocess.ComponentNamesBP.CompNameCreationContext;
import org.eclipse.jubula.client.core.businessprocess.IComponentNameCache;
import org.eclipse.jubula.client.core.businessprocess.IWritableComponentNameCache;
import org.eclipse.jubula.client.core.businessprocess.ParamNameBP;
import org.eclipse.jubula.client.core.businessprocess.ParamNameBPDecorator;
import org.eclipse.jubula.client.core.businessprocess.ProjectCompNameCache;
import org.eclipse.jubula.client.core.datastructure.CompNameUsageMap;
import org.eclipse.jubula.client.core.model.IAUTMainPO;
import org.eclipse.jubula.client.core.model.ICapPO;
import org.eclipse.jubula.client.core.model.ICategoryPO;
import org.eclipse.jubula.client.core.model.ICompNamesPairPO;
import org.eclipse.jubula.client.core.model.IComponentNamePO;
import org.eclipse.jubula.client.core.model.IEventExecTestCasePO;
import org.eclipse.jubula.client.core.model.IExecObjContPO;
import org.eclipse.jubula.client.core.model.IExecTestCasePO;
import org.eclipse.jubula.client.core.model.INodePO;
import org.eclipse.jubula.client.core.model.IObjectMappingAssoziationPO;
import org.eclipse.jubula.client.core.model.IParamDescriptionPO;
import org.eclipse.jubula.client.core.model.IParamNamePO;
import org.eclipse.jubula.client.core.model.IPersistentObject;
import org.eclipse.jubula.client.core.model.IProjectPO;
import org.eclipse.jubula.client.core.model.ISpecObjContPO;
import org.eclipse.jubula.client.core.model.ISpecTestCasePO;
import org.eclipse.jubula.client.core.model.ITcParamDescriptionPO;
import org.eclipse.jubula.client.core.model.NodeMaker;
import org.eclipse.jubula.client.core.persistence.locking.LockManager;
import org.eclipse.jubula.toolkit.common.xml.businessprocess.ComponentBuilder;
import org.eclipse.jubula.tools.internal.exception.InvalidDataException;
import org.eclipse.jubula.tools.internal.exception.ProjectDeletedException;
import org.eclipse.jubula.tools.internal.messagehandling.MessageIDs;
import org.eclipse.jubula.tools.internal.messagehandling.MessageInfo;
/**
* This class offers support for executing multiple commands in a single
* transaction. Therefore :
* -all commands are queried for a list of objects
* to lock
* -all commands are executed
* -locks are removed
*
* @author BREDEX GmbH
* @created 26.03.2007
*/
public class MultipleNodePM extends PersistenceManager {
/**
* Abstract command, offering support for a Set of objects to lock. All
* commands are executed in the same transaction
*
* REMEMBER : some nodes do not return their Persistence (JPA / EclipseLink) parent,
* but the project when in top level, for example ICategoryPO
* or ISpecTestCasePO
*/
public abstract static class AbstractCmdHandle {
/** set of objects to lock; empty by default */
private final Set<IPersistentObject> m_objsToLock =
new HashSet<IPersistentObject>();
/**
* @return a Set of objects to be locked in database
*/
public final Set <IPersistentObject> getObjsToLock() {
return m_objsToLock;
}
/**
* executes the command
*
* @param sess The session in which this command is executing.
* @return a message containing information about the error that
* occurred during execution, or <code>null</code> if no error
* occurred.
*/
public abstract MessageInfo execute(EntityManager sess);
}
/**
* "Transfers" Component Names used in Test Cases / Test Steps from one
* project to another. The transfer is not a move, nor is it a copy.
* It is based on the names of the Component Names and has the following
* logic:
*
* Rules for Component Names (CNs) used as "old name" in an
* ICompNamesPairPO:
* 1. If the CN is from a reused Project (P), do nothing to change it.
* Note that this can essentially "remove" the CN if the new
* project does not reuse P. However, this seems reasonable
* given that TCs from P are also not available in the new project.
* 2. If the CN is from the current Project, create a new CN in the new
* Project. The new CN will have the same name as the original, but
* will have a different GUID.
* If a CN with same name already exists in the new Project,
* cancel the operation with an error.
*
* Rules for Component Names (CNs) used as "new name" in an
* ICompNamesPairPO or used in a Test Step:
* 1. If a CN with the same name already exists in the new Project, use it.
* In case of type incompatibility, cancel the operation with an error.
* 2. If no CN with the same name exists in the new Project, create a new
* CN in the new Project. The new CN will have the same name as the
* original, but will have a different GUID.
*
* @author BREDEX GmbH
* @created May 30, 2008
*/
public static final class TransferCompNameHandle extends AbstractCmdHandle {
/** mapping from "old name"s to their users */
private Map<IComponentNamePO, Set<INodePO>> m_firstCompNameToUsers;
/** mapping from "new name"s to their users */
private Map<IComponentNamePO, Set<INodePO>> m_secondCompNameToUsers;
/**
* the ID of the Project from which the Component Names are being
* "transferred"
*/
private Long m_currentProjectId = null;
/**
* the Project into which the Component names are being "transferred"
*/
private IProjectPO m_newParentProj = null;
/**
* Constructor
*
* @param usageMap Mapping from used Component Names to their users.
* @param currentProjectId The ID of the Project from which the
* Component Names are being "transferred".
* @param newParentProj The Project into which the Component names
* are being "transferred".
*/
public TransferCompNameHandle(
CompNameUsageMap usageMap,
Long currentProjectId,
IProjectPO newParentProj) {
m_firstCompNameToUsers =
new HashMap<IComponentNamePO, Set<INodePO>>();
for (IComponentNamePO compName : usageMap.getFirstCompNames()) {
Set<INodePO> users = new HashSet<INodePO>();
users.addAll(usageMap.getFirstNameUsers(compName));
m_firstCompNameToUsers.put(compName, users);
}
m_secondCompNameToUsers =
new HashMap<IComponentNamePO, Set<INodePO>>();
for (IComponentNamePO compName : usageMap.getSecondCompNames()) {
Set<INodePO> users = new HashSet<INodePO>();
users.addAll(usageMap.getSecondNameUsers(compName));
m_secondCompNameToUsers.put(compName, users);
}
m_currentProjectId = currentProjectId;
m_newParentProj = newParentProj;
}
/**
* {@inheritDoc}
*/
public MessageInfo execute(EntityManager sess) {
IWritableComponentNameCache extProjectCompCache =
new ProjectCompNameCache(m_newParentProj);
try {
Map<String, IComponentNamePO> createdComponentNames =
new HashMap<String, IComponentNamePO>();
createdComponentNames.putAll(
handleFirstNames(sess, extProjectCompCache));
createdComponentNames.putAll(
handleSecondNames(sess, extProjectCompCache));
for (String guid : createdComponentNames.keySet()) {
handleMapping(sess, extProjectCompCache,
guid, createdComponentNames.get(guid));
}
CompNamePM.flushCompNames(
sess, m_newParentProj.getId(), extProjectCompCache);
} catch (PMException e) {
return new MessageInfo(e.getErrorId(), null);
} catch (ComponentNameExistsException e) {
return new MessageInfo(
e.getErrorId(), e.getErrorMessageParams());
}
return null;
}
/**
* Maps the given Component Name according to the current mapping for
* the Component Name with the given GUID. If the AUT for an Object Map
* is locked, the Component Name will not be mapped for that Object Map.
*
* @param sess The session in which to perform the work. This session
* will not be managed at all by this method (i.e. no
* commit or rollback for transactions, no closing).
* @param extProjectCompCache The Comp Name Cache
* @param originalGuid The GUID of the Component Name for which to
* check for mappings.
* @param compName The Component Name that will be mapped.
* @throws PMException If a persistence error occurs while performing
* the mapping.
*/
private void handleMapping(EntityManager sess,
IWritableComponentNameCache extProjectCompCache,
String originalGuid, IComponentNamePO compName)
throws PMException {
IProjectPO currentProject = sess.find(
NodeMaker.getProjectPOClass(), m_currentProjectId);
for (IAUTMainPO aut : currentProject.getAutMainList()) {
if (LockManager.instance().lockPO(sess, aut, true)) {
IObjectMappingAssoziationPO assoc =
aut.getObjMap().getLogicalNameAssoc(originalGuid);
extProjectCompCache.changeReuse(
assoc, null, compName.getGuid());
}
}
}
/**
* @param sess The session in which to perform the operation.
* @param extProjectCompCache The Component Name cache.
* @return mapping from GUID of "moved/cloned" Component Name to newly
* created Component Name in reused Project.
*/
private Map<String, IComponentNamePO> handleSecondNames(
EntityManager sess,
IWritableComponentNameCache extProjectCompCache) {
Map<String, IComponentNamePO> createdNames =
new HashMap<String, IComponentNamePO>();
Map<IComponentNamePO, Set<INodePO>> secondCompNameToUsers =
loadInSession(sess, m_secondCompNameToUsers);
for (IComponentNamePO compName : secondCompNameToUsers.keySet()) {
IComponentNamePO existingExtCompName =
getCompNamePOForName(
compName.getName(),
extProjectCompCache,
m_newParentProj.getId());
if (existingExtCompName != null
&& existingExtCompName.getParentProjectId().equals(
m_newParentProj.getId())) {
// Update relevant users
updateSecondNameUsers(extProjectCompCache,
secondCompNameToUsers, compName,
existingExtCompName);
} else {
// Create a new Component Name in the new project.
String compType = ComponentBuilder.getInstance()
.getCompSystem().getMostAbstractComponent().getType();
IComponentNamePO newCompName =
extProjectCompCache.createComponentNamePO(
compName.getName(), compType,
CompNameCreationContext.OVERRIDDEN_NAME);
extProjectCompCache.addCompNamePO(newCompName);
newCompName.setParentProjectId(m_newParentProj.getId());
createdNames.put(compName.getGuid(), newCompName);
// Update relevant users
updateSecondNameUsers(extProjectCompCache,
secondCompNameToUsers, compName, newCompName);
}
}
return createdNames;
}
/**
* Updates the objects using the given Component Name as a second name.
*
* @param extProjectCompCache The Component Name cache to use to
* perform the update.
* @param secondCompNameToUsers The users to update.
* @param compName The Component Name currently used.
* @param existingExtCompName The new Component Name to use.
*/
private void updateSecondNameUsers(
IWritableComponentNameCache extProjectCompCache,
Map<IComponentNamePO, Set<INodePO>> secondCompNameToUsers,
IComponentNamePO compName,
IComponentNamePO existingExtCompName) {
for (INodePO node : secondCompNameToUsers
.get(compName)) {
if (node instanceof ICapPO) {
ICapPO capPo = (ICapPO)node;
extProjectCompCache.changeReuse(
capPo, capPo.getComponentName(),
existingExtCompName.getGuid());
} else if (node instanceof IExecTestCasePO) {
for (ICompNamesPairPO pair
: ((IExecTestCasePO)node)
.getCompNamesPairs()) {
if (pair.getSecondName().equals(
compName.getGuid())) {
extProjectCompCache.changeReuse(
pair, pair.getSecondName(),
existingExtCompName.getGuid());
}
}
}
}
}
/**
*
* @param sess The session in which to perform the operation.
* @param compCache The Component Name cache to use for the
* operation.
* @return mapping from GUID of "moved/cloned" Component Name to newly
* created Component Name in reused Project.
* @throws ComponentNameExistsException
* If the name to handle already exists in the
* reused Project.
*/
private Map<String, IComponentNamePO> handleFirstNames(
EntityManager sess, IWritableComponentNameCache compCache)
throws ComponentNameExistsException {
Map<String, IComponentNamePO> createdComponentNames =
new HashMap<String, IComponentNamePO>();
// Load users into given session
Map<IComponentNamePO, Set<INodePO>> firstCompNameToUsers =
loadInSession(sess, m_firstCompNameToUsers);
for (IComponentNamePO compName : firstCompNameToUsers.keySet()) {
if (compName.getParentProjectId().equals(m_currentProjectId)) {
// Component Name is from the current project.
// Check whether a Component Name with the same name
// already exists in the new project.
IComponentNamePO existingExtCompName =
getCompNamePOForName(
compName.getName(),
compCache,
m_newParentProj.getId());
if (existingExtCompName != null
&& existingExtCompName.getParentProjectId()
.equals(m_newParentProj.getId())) {
// Component Name with same name already exists in
// new project.
throw new ComponentNameExistsException(
"Cannot perform move operation.", //$NON-NLS-1$
MessageIDs.E_MOVE_TC_COMP_NAME_EXISTS,
new String [] {existingExtCompName.getName()});
}
// Create a new Component Name in the new project.
String compType = ComponentBuilder.getInstance()
.getCompSystem().getMostAbstractComponent().getType();
IComponentNamePO newCompName =
compCache.createComponentNamePO(
compName.getName(), compType,
CompNameCreationContext.STEP);
compCache.addCompNamePO(newCompName);
newCompName.setParentProjectId(m_newParentProj.getId());
createdComponentNames.put(compName.getGuid(), newCompName);
// Update relevant users
for (INodePO node : firstCompNameToUsers.get(compName)) {
if (node instanceof IExecTestCasePO) {
for (ICompNamesPairPO pair
: ((IExecTestCasePO)node)
.getCompNamesPairs()) {
if (pair.getFirstName().equals(
compName.getGuid())) {
pair.setFirstName(newCompName.getGuid());
}
}
}
}
}
}
return createdComponentNames;
}
/**
*
* @param sess The session in which to load the objects in
* <code>toLoad</code>.
* @param toLoad Contains all of the objects to be loaded into the
* session.
* @return the objects that have been loaded in the given session as a
* result of this method call.
*/
private Map<IComponentNamePO, Set<INodePO>> loadInSession(
EntityManager sess,
Map<IComponentNamePO, Set<INodePO>> toLoad) {
Map<IComponentNamePO, Set<INodePO>> sessionMap =
new HashMap<IComponentNamePO, Set<INodePO>>();
for (IComponentNamePO compName : toLoad.keySet()) {
Set<INodePO> userSet = new HashSet<INodePO>();
for (INodePO node : toLoad.get(compName)) {
userSet.add(sess.find(node.getClass(), node.getId()));
}
sessionMap.put(sess.find(compName.getClass(),
compName.getId()), userSet);
}
return sessionMap;
}
/**
*
* @param name The name of the Component Name to find.
* @param compNameCache The cache to use to find the Component Name.
* @param parentProjectId The ID of the Project in which to find the
* Component Name.
* @return The Component Name with name equal to <code>name</code>
* within the Project with the given ID, or <code>null</code>
* if no such Component Name can be found. Reused Projects are
* ignored for the purposes of this search.
*/
private IComponentNamePO getCompNamePOForName(
String name, IComponentNameCache compNameCache,
Long parentProjectId) {
String guid = compNameCache.getGuidForName(name, parentProjectId);
if (guid != null) {
return compNameCache.getResCompNamePOByGuid(guid);
}
return null;
}
}
/**
* Command to add a single Exec to a parent
*/
public static class AddExecTCHandle extends AbstractCmdHandle {
/** parent in which the Exec should be included */
private INodePO m_parent;
/** the index at which position it should be added */
private Integer m_index;
/** the new Exec */
private IExecTestCasePO m_newExec;
/**
*
* @param parent the parent in which the node should be added
* @param newExec the new ExecTestCase
* @param index the index or null if it should be added to the end
*/
public AddExecTCHandle(INodePO parent, IExecTestCasePO newExec,
Integer index) {
getObjsToLock().add(parent);
m_parent = parent;
m_index = index;
m_newExec = newExec;
}
/**
* {@inheritDoc}
*/
public MessageInfo execute(EntityManager sess) {
sess.persist(m_newExec);
INodePO parent = sess.find(m_parent.getClass(), m_parent.getId());
if (m_newExec instanceof IEventExecTestCasePO) {
if (parent instanceof ISpecTestCasePO) {
ISpecTestCasePO spec = (ISpecTestCasePO) parent;
try {
spec.addEventTestCase((IEventExecTestCasePO) m_newExec);
return null;
} catch (InvalidDataException e) {
return new MessageInfo(e.getErrorId(), null);
}
}
// This secures that no IEventExecTC is added in a wrong PO
return new MessageInfo(MessageIDs.E_EVENT_SUPPORT, null);
}
parent.addNode(m_index, m_newExec);
return null;
}
}
/**
* Command to move a single INodePO from one parent to another. Should work
* for moving any type of node
*/
public static class MoveNodeHandle extends AbstractCmdHandle {
/** node to move */
private INodePO m_node;
/** oldParent (could be INodePO, ISpecObjCont or ITestSuiteCont)*/
private IPersistentObject m_oldParent;
/** newParent (could be INodePO, ISpecObjCont or ITestSuiteCont)*/
private IPersistentObject m_newParent;
/**
* constructor
* @param node
* INodePO
* @param oldParent
* INodePO
* @param newParent
* INodePO
*/
public MoveNodeHandle(INodePO node, IPersistentObject oldParent,
IPersistentObject newParent) {
IProjectPO proj = GeneralStorage.getInstance().getProject();
m_oldParent = oldParent;
if (oldParent == IExecObjContPO.TSB_ROOT_NODE) {
m_oldParent = proj.getExecObjCont();
} else if (oldParent == ISpecObjContPO.TCB_ROOT_NODE) {
m_oldParent = proj.getSpecObjCont();
}
m_newParent = newParent;
if (m_newParent == IExecObjContPO.TSB_ROOT_NODE) {
m_newParent = proj.getExecObjCont();
} else if (m_newParent == ISpecObjContPO.TCB_ROOT_NODE) {
m_newParent = proj.getSpecObjCont();
}
m_node = node;
getObjsToLock().add(m_node);
getObjsToLock().add(m_oldParent);
getObjsToLock().add(m_newParent);
}
/** {@inheritDoc} */
public MessageInfo execute(EntityManager sess) {
EntityManager masterSession =
GeneralStorage.getInstance().getMasterSession();
IPersistentObject oldParent = m_oldParent;
IPersistentObject newParent = m_newParent;
INodePO node = m_node;
if (masterSession != sess) {
masterSession.detach(node);
oldParent = sess.find(oldParent.getClass(), oldParent.getId());
newParent = sess.find(newParent.getClass(), newParent.getId());
node = sess.find(node.getClass(), node.getId());
}
// remove CentralTestData if the project changes.
if (node instanceof ISpecTestCasePO
&& !(oldParent.getParentProjectId().equals(
newParent.getParentProjectId()))) {
Iterator iter = node.getAllNodeIter();
while (iter.hasNext()) {
INodePO child = (INodePO)iter.next();
if (child instanceof IExecTestCasePO) {
IExecTestCasePO execTC = (IExecTestCasePO)child;
execTC.setReferencedDataCube(null);
}
}
((ISpecTestCasePO) node).setReferencedDataCube(null);
}
// remove from old parent
if (oldParent instanceof ISpecObjContPO) {
((ISpecObjContPO)oldParent).removeSpecObject(
(ISpecPersistable)node);
} else if (oldParent instanceof IExecObjContPO) {
((IExecObjContPO)oldParent).removeExecObject(
(IExecPersistable)node);
} else {
((INodePO)oldParent).removeNode(node);
}
// add to new parent
if (newParent instanceof ISpecObjContPO) {
((ISpecObjContPO)newParent).addSpecObject(
(ISpecPersistable)node);
} else if (newParent instanceof IExecObjContPO) {
((IExecObjContPO)newParent).addExecObject(
(IExecPersistable)node);
} else {
((INodePO)newParent).addNode(node);
}
return null;
}
}
/**
* command to update ParamNames, which was moved from current project
* to a reused project
*
*/
public static class UpdateParamNamesHandle extends AbstractCmdHandle {
/** list of moved SpecTestCases */
private List<ISpecTestCasePO> m_specTestCases;
/** project, where the SpecTestCases moved to */
private IProjectPO m_reusedProject;
/** list of param names to update */
private List <IParamNamePO> m_paramNames =
new ArrayList<IParamNamePO>();
/**
* @param specTCs list of moved SpecTestCases
* @param reusedProject project, where the SpecTestCases moved to
*/
public UpdateParamNamesHandle(List<ISpecTestCasePO> specTCs,
IProjectPO reusedProject) {
m_specTestCases = specTCs;
m_reusedProject = reusedProject;
}
/** {@inheritDoc}
* @see org.eclipse.jubula.client.core.persistence.MultipleNodePM.AbstractCmdHandle#execute(org.Persistence (JPA / EclipseLink).Session)
*/
public MessageInfo execute(EntityManager sess) {
for (ISpecTestCasePO specTC : m_specTestCases) {
for (IParamDescriptionPO desc : specTC.getParameterList()) {
IParamNamePO paramNamePO = ParamNameBP.getInstance()
.getParamNamePO(desc.getUniqueId());
if (paramNamePO != null) {
paramNamePO.setParentProjectId(m_reusedProject.getId());
m_paramNames.add(paramNamePO);
sess.merge(paramNamePO);
getObjsToLock().add(paramNamePO);
}
}
}
return null;
}
/**
* @return Returns the paramNames.
*/
List<IParamNamePO> getParamNames() {
return m_paramNames;
}
}
/**
* Command to update the reference to a TestCase.
*/
public static class UpdateTestCaseRefHandle extends AbstractCmdHandle {
/** node to update */
private IExecTestCasePO m_execTc;
/** new referenced SpecTestCase */
private ISpecTestCasePO m_specTc;
/**
* Constructor
*
* @param execTc
* ExecTestCase whose reference is to be updated.
* @param specTc
* New referenced SpecTestCase.
*/
public UpdateTestCaseRefHandle(
IExecTestCasePO execTc, ISpecTestCasePO specTc) {
m_execTc = execTc;
m_specTc = specTc;
getObjsToLock().add(m_execTc);
}
/**
* {@inheritDoc}
*/
public MessageInfo execute(EntityManager sess) {
ISpecTestCasePO specTc = sess.find(m_specTc.getClass(),
m_specTc.getId());
IExecTestCasePO execTc = sess.find(m_execTc.getClass(),
m_execTc.getId());
execTc.setSpecTestCase(specTc);
return null;
}
}
/**
* Command to delete a single test suite
*/
public static class DeleteExecHandle extends AbstractCmdHandle {
/** the exec node to delete */
private IExecPersistable m_execNode;
/**
* constructor
* @param exec
* the exec node to delete
*/
public DeleteExecHandle(IExecPersistable exec) {
m_execNode = exec;
getObjsToLock().add(exec);
if (isNestedNode(exec)) {
getObjsToLock().add(exec.getParentNode());
} else {
IProjectPO proj = GeneralStorage.getInstance().getProject();
getObjsToLock().add(proj.getExecObjCont());
}
}
/**
* {@inheritDoc}
*/
public MessageInfo execute(EntityManager sess) {
IExecPersistable tc = sess.find(m_execNode.getClass(),
m_execNode.getId());
if (isNestedNode(m_execNode)) {
INodePO par = m_execNode.getParentNode();
par = sess.find(par.getClass(), par.getId());
par.removeNode(tc);
} else {
IExecObjContPO cont = GeneralStorage.getInstance().
getProject().getExecObjCont();
cont = sess.find(cont.getClass(), cont.getId());
cont.removeExecObject(tc);
}
sess.remove(tc);
return null;
}
}
/**
* Command to delete a single test case
*/
public static class DeleteTCHandle extends AbstractCmdHandle {
/** test case to delete */
private ISpecTestCasePO m_testCase;
/**
* <code>m_mapper</code> to manage param names for deletion in db
*/
private ParamNameBPDecorator m_dec;
/**
* constructor
* @param tc to delete
* @param mapper mapper to manage param names for deletion in db
* ISpecTestCasePO
*/
public DeleteTCHandle(ISpecTestCasePO tc, ParamNameBPDecorator mapper) {
m_testCase = tc;
m_dec = mapper;
getObjsToLock().add(tc);
if (isNestedNode(tc)) {
getObjsToLock().add(tc.getParentNode());
} else {
IProjectPO proj = GeneralStorage.getInstance().getProject();
getObjsToLock().add(proj.getSpecObjCont());
}
}
/**
* {@inheritDoc}
*/
public MessageInfo execute(EntityManager sess) {
ISpecTestCasePO tc = sess.find(m_testCase.getClass(),
m_testCase.getId());
if (isNestedNode(m_testCase)) {
INodePO par = m_testCase.getParentNode();
par = sess.find(par.getClass(), par.getId());
par.removeNode(tc);
} else {
ISpecObjContPO cont = GeneralStorage.getInstance().
getProject().getSpecObjCont();
cont = sess.find(cont.getClass(), cont.getId());
cont.removeSpecObject(tc);
}
registerParamNamesForDeletion(tc);
sess.remove(tc);
return null;
}
/**
* @param specTcPO specTc to delete
*/
private void registerParamNamesForDeletion(ISpecTestCasePO specTcPO) {
for (IParamDescriptionPO desc : specTcPO.getParameterList()) {
getDec().registerParamDescriptions((ITcParamDescriptionPO)desc);
getDec().removeParamNamePO(desc.getUniqueId());
}
}
/**
* @return the m_dec
*/
public ParamNameBPDecorator getDec() {
return m_dec;
}
}
/**
* Command to delete an EventExecTestCasePO
*/
public static class DeleteEvHandle extends AbstractCmdHandle {
/** test suite to delete */
private IEventExecTestCasePO m_eventTestCase;
/**
* constructor
* @param tc
* IEventExecTestCasePO
*/
public DeleteEvHandle(IEventExecTestCasePO tc) {
m_eventTestCase = tc;
getObjsToLock().add(tc);
getObjsToLock().add(tc.getParentNode());
}
/**
* {@inheritDoc}
*/
public MessageInfo execute(EntityManager sess) {
ISpecTestCasePO specTc = (ISpecTestCasePO)m_eventTestCase.
getParentNode();
specTc = sess.find(specTc.getClass(), specTc.getId());
IEventExecTestCasePO evTC = sess.find(m_eventTestCase.getClass(),
m_eventTestCase.getId());
specTc.removeNode(evTC);
sess.remove(evTC);
return null;
}
}
/**
* Command to delete a single test case
*/
public static class DeleteCatHandle extends AbstractCmdHandle {
/** test suite to delete */
private ICategoryPO m_category;
/**
* constructor
* @param cat
* ICategoryPO
*/
public DeleteCatHandle(ICategoryPO cat) {
m_category = cat;
getObjsToLock().add(cat);
if (isNestedNode(cat)) {
getObjsToLock().add(cat.getParentNode());
} else {
IProjectPO proj = GeneralStorage.getInstance().getProject();
INodePO parent = m_category.getParentNode();
if (parent == ISpecObjContPO.TCB_ROOT_NODE) {
getObjsToLock().add(proj.getSpecObjCont());
} else if (parent == IExecObjContPO.TSB_ROOT_NODE) {
getObjsToLock().add(proj.getExecObjCont());
}
}
}
/**
* {@inheritDoc}
*/
public MessageInfo execute(EntityManager sess) {
ICategoryPO cat = sess.find(m_category.getClass(),
m_category.getId());
INodePO par = m_category.getParentNode();
if (isNestedNode(m_category)) {
par = sess.find(par.getClass(), par.getId());
par.removeNode(cat);
} else {
IProjectPO proj = GeneralStorage.getInstance().getProject();
if (par == ISpecObjContPO.TCB_ROOT_NODE) {
ISpecObjContPO cont = proj.getSpecObjCont();
cont = sess.find(cont.getClass(), cont.getId());
cont.removeSpecObject(cat);
} else if (par == IExecObjContPO.TSB_ROOT_NODE) {
IExecObjContPO cont = proj.getExecObjCont();
cont = sess.find(cont.getClass(), cont.getId());
cont.removeExecObject(cat);
}
}
sess.remove(cat);
return null;
}
}
/**
* Command to delete a single Exec
*/
public static class DeleteExecTCHandle extends AbstractCmdHandle {
/** the exec node to delete */
private IExecTestCasePO m_execNode;
/**
* constructor
* @param exec
* the exec node to delete
*/
public DeleteExecTCHandle(IExecTestCasePO exec) {
m_execNode = exec;
getObjsToLock().add(exec);
if (isNestedNode(exec)) {
getObjsToLock().add(exec.getParentNode());
} else {
IProjectPO proj = GeneralStorage.getInstance().getProject();
getObjsToLock().add(proj.getExecObjCont());
}
}
/**
* {@inheritDoc}
*/
public MessageInfo execute(EntityManager sess) {
if (isNestedNode(m_execNode)) {
m_execNode.getParentNode().removeNode(m_execNode);
}
IExecTestCasePO exec = sess.find(m_execNode.getClass(),
m_execNode.getId());
if (exec != null) {
sess.remove(exec);
}
return null;
}
}
/**
* class variable for Singleton
*/
private static MultipleNodePM persManager = null;
/**
* getter for Singleton
*
* @return single instance
*/
public static MultipleNodePM getInstance() {
if (persManager == null) {
persManager = new MultipleNodePM();
}
return persManager;
}
/**
* executes a list of commands in a single transaction in the Master
* Session.
*
* @param cmds
* List<AbstractCmdHandle>
* @throws PMException
* error occured
* @throws ProjectDeletedException
* error occured
* @return a message containing information about the error that
* occurred during execution, or <code>null</code> if no error
* occurred.
*/
public MessageInfo executeCommands(List<AbstractCmdHandle> cmds)
throws PMException, ProjectDeletedException {
return executeCommands(cmds, GeneralStorage.getInstance()
.getMasterSession());
}
/**
* executes a list of commands in a single transaction. The given session
* is managed by the caller. This method will commit or rollback the
* transaction as appropriate, but will not close the session.
*
* @param cmds
* List<AbstractCmdHandle>
* @param sess The session in which to execute the commands.
* @throws PMException
* error occured
* @throws ProjectDeletedException
* error occured
* @return a message containing information about the error that
* occurred during execution, or <code>null</code> if no error
* occurred.
*/
public MessageInfo executeCommands(List<AbstractCmdHandle> cmds,
EntityManager sess)
throws PMException, ProjectDeletedException {
final Persistor persistor = Persistor.instance();
EntityTransaction tx = null;
// get List of objects to lock
Set <IPersistentObject> objectsToLock =
new HashSet<IPersistentObject>();
IPersistentObject actObj = null;
for (AbstractCmdHandle cmd : cmds) {
objectsToLock.addAll(cmd.getObjsToLock());
}
try {
// get new Transaction
tx = persistor.getTransaction(sess);
// lock objects
persistor.lockPOSet(sess, objectsToLock);
// the param name bp decorator which should be used for param changes
ParamNameBPDecorator dec = null;
// execute command code in transaction
for (AbstractCmdHandle cmd : cmds) {
MessageInfo errorMessage = cmd.execute(sess);
if (errorMessage != null) {
persistor.rollbackTransaction(sess, tx);
return errorMessage;
}
if (cmd instanceof DeleteTCHandle) {
dec = ((DeleteTCHandle) cmd).getDec();
}
}
if (dec != null) {
deleteParamNames(dec, sess);
}
// commit transaction and remove all locks
persistor.commitTransaction(sess, tx);
if (dec != null) {
// sync with master sessions mapper
Long projId = GeneralStorage.getInstance().getProject().getId();
dec.updateStandardMapperAndCleanup(projId);
}
EntityManager master =
GeneralStorage.getInstance().getMasterSession();
for (AbstractCmdHandle cmd : cmds) {
if (cmd instanceof UpdateParamNamesHandle) {
UpdateParamNamesHandle paramCmd =
(UpdateParamNamesHandle)cmd;
for (IParamNamePO paramName : paramCmd.getParamNames()) {
master.detach(paramName);
}
}
}
for (IPersistentObject next : objectsToLock) {
IPersistentObject obj = master.find(next.getClass(),
next.getId());
if (obj != null) {
try {
master.refresh(obj);
} catch (EntityNotFoundException e) {
// ok, the object was deleted...
master.detach(obj);
}
}
}
} catch (PersistenceException e1) {
PersistenceManager.handleDBExceptionForMasterSession(actObj, e1);
}
return null;
}
/**
* @param dec decorator to handle deletion of param names
* @param s session to use for deletion
*/
private void deleteParamNames(ParamNameBPDecorator dec, EntityManager s) {
try {
dec.persist(s, GeneralStorage.getInstance()
.getProject().getId());
} catch (PMException e) {
throw new PersistenceException(e);
}
}
/**
* @param node
* the node to check wheter its nested = non-top level
* node
* @return true if non top-level node
*/
private static boolean isNestedNode(INodePO node) {
boolean isNested = false;
INodePO parent = node.getParentNode();
if (parent != null
&& parent != ISpecObjContPO.TCB_ROOT_NODE
&& parent != IExecObjContPO.TSB_ROOT_NODE) {
isNested = true;
}
return isNested;
}
/**
* collects all nodes of a specified node, that are required for the current
* operation
*
* @param affectedNodes
* Set<INodePO>
* @param node
* INodePO
*/
public static void collectAffectedNodes(List<INodePO> affectedNodes,
INodePO node) {
affectedNodes.add(node);
if (node instanceof ICategoryPO) {
Iterator iter = node.getNodeListIterator();
while (iter.hasNext()) {
collectAffectedNodes(affectedNodes, (INodePO) iter.next());
}
} else if (node instanceof ISpecTestCasePO) {
ISpecTestCasePO specTcPO = (ISpecTestCasePO) node;
if (!specTcPO.getAllEventEventExecTC().isEmpty()) {
affectedNodes.addAll(specTcPO.getAllEventEventExecTC());
}
}
}
/**
* Checks if all ExecTestCases are used in SpecTestCases, that are going
* to be affected
*
* @param affectedNodes
* List<INodePO>
* @param execTestCases
* List<IExecTestCasePO>
* @return
* true, if no conflict exists. That means, there are no IExecTestCasePO
* or all located in other nodes, which are going to be affected
*/
public static boolean allExecsFromList(Collection<INodePO> affectedNodes,
List<IExecTestCasePO> execTestCases) {
if (execTestCases.isEmpty()) {
return true;
}
for (IExecTestCasePO execTc : execTestCases) {
INodePO parent;
parent = execTc.getSpecAncestor();
if (!affectedNodes.contains(parent)) {
return false;
}
}
return true;
}
}