/******************************************************************************* * 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.List; import java.util.Map; import java.util.Set; import javax.persistence.EntityManager; import javax.persistence.EntityNotFoundException; import javax.persistence.EntityTransaction; import javax.persistence.NoResultException; import javax.persistence.PersistenceException; import javax.persistence.Query; import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.CriteriaQuery; import javax.persistence.criteria.Predicate; import javax.persistence.criteria.Root; import org.apache.commons.lang.Validate; import org.eclipse.core.runtime.Assert; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jubula.client.core.i18n.Messages; import org.eclipse.jubula.client.core.model.IAbstractContainerPO; import org.eclipse.jubula.client.core.model.ICategoryPO; 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.IPersistentObject; import org.eclipse.jubula.client.core.model.IProjectPO; import org.eclipse.jubula.client.core.model.IRefTestSuitePO; import org.eclipse.jubula.client.core.model.IReusedProjectPO; import org.eclipse.jubula.client.core.model.ISpecObjContPO; import org.eclipse.jubula.client.core.model.ISpecTestCasePO; import org.eclipse.jubula.client.core.model.ITestSuitePO; import org.eclipse.jubula.client.core.model.NodeMaker; import org.eclipse.jubula.client.core.model.ProjectVersion; import org.eclipse.jubula.client.core.utils.AbstractNonPostOperatingTreeNodeOperation; import org.eclipse.jubula.client.core.utils.ITreeTraverserContext; import org.eclipse.jubula.client.core.utils.TreeTraverser; import org.eclipse.jubula.tools.internal.constants.StringConstants; import org.eclipse.jubula.tools.internal.exception.InvalidDataException; import org.eclipse.jubula.tools.internal.exception.JBException; import org.eclipse.jubula.tools.internal.exception.JBFatalException; import org.eclipse.jubula.tools.internal.exception.ProjectDeletedException; import org.eclipse.jubula.tools.internal.messagehandling.MessageIDs; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Class to persist and read nodes * * @author BREDEX GmbH * @created 07.09.2004 */ public class NodePM extends PersistenceManager { /** * Command for parent/child adding and removing */ public abstract static class AbstractCmdHandleChild { /** A bit cumbersome, but this object will be used in some children */ private IPersistentObject m_parent = null; /** * Template for this command. If executed add the child to the parent. * * @param parent * Node where the child should be added. * @param child * Child to be added to parent. * @param pos * where to insert the child, value null means insert after * end */ public abstract void add(INodePO parent, INodePO child, Integer pos); /** * Template for this command. If executed remove the child from the * parent. This method assumes that the child is to be deleted. * Calls dispose() on child! * @param parent * Node where the child should be removed. * @param child * Child to be removed from parent. */ public void delete(INodePO parent, INodePO child) { remove(parent, child); } /** * Like delete, but the child is not to be deleted. * Does <b>not</b> call dispose() on child! * {@inheritDoc} */ public abstract void remove(INodePO parent, INodePO child); /** * Sets the parentProjectId for a node inserted into another node. * @param child the child node * @param parent the parent node */ public void setParentProjectId(INodePO child, INodePO parent) { Long parentProjectId = parent.getParentProjectId(); if (parentProjectId == null) { parentProjectId = GeneralStorage.getInstance().getProject() .getId(); } child.setParentProjectId(parentProjectId); } /** * Sets the alternative parent - used only by ChildIntoSpec and Exec * @param par the parent */ public void setParent(IPersistentObject par) { m_parent = par; } /** * @return guess what... */ public IPersistentObject getParent() { return m_parent; } } /** * {@inheritDoc} */ public static class CmdHandleChildIntoNodeList extends AbstractCmdHandleChild { /** * {@inheritDoc} * org.eclipse.jubula.client.core.model.INodePO) */ public void add(INodePO parent, INodePO child, Integer pos) { parent.addNode(pos == null ? -1 : pos, child); setParentProjectId(child, parent); } /** * {@inheritDoc} * org.eclipse.jubula.client.core.model.INodePO) * @param parent * @param child */ public void remove(INodePO parent, INodePO child) { parent.removeNode(child); } } /** * {@inheritDoc} */ public static class CmdHandleChildIntoSpecList extends AbstractCmdHandleChild { /** * {@inheritDoc} * org.eclipse.jubula.client.core.model.INodePO) */ public void add(INodePO parent, INodePO child, Integer pos) { // pos is not used here if (getParent() != null && getParent() instanceof ISpecObjContPO) { // circumventing the method signature... ((ISpecObjContPO) getParent()).addSpecObject( (ISpecPersistable) child); } else { IProjectPO proj = GeneralStorage.getInstance().getProject(); proj.getSpecObjCont().addSpecObject((ISpecPersistable)child); } setParentProjectId(child, parent); } /** * {@inheritDoc} * org.eclipse.jubula.client.core.model.INodePO) */ public void remove(INodePO parent, INodePO child) { IProjectPO proj = GeneralStorage.getInstance().getProject(); proj.getSpecObjCont().removeSpecObject((ISpecPersistable)child); } } /** * {@inheritDoc} */ public static class CmdHandleChildIntoExecList extends AbstractCmdHandleChild { /** * {@inheritDoc} * org.eclipse.jubula.client.core.model.INodePO) */ public void add(INodePO parent, INodePO child, Integer pos) { // pos is not used here if (getParent() != null && getParent() instanceof IExecObjContPO) { // circumventing the method signature... ((IExecObjContPO) getParent()).addExecObject( (IExecPersistable) child); } else { IProjectPO proj = GeneralStorage.getInstance().getProject(); proj.getExecObjCont().addExecObject((IExecPersistable)child); } setParentProjectId(child, parent); } /** * {@inheritDoc} * org.eclipse.jubula.client.core.model.INodePO) */ public void remove(INodePO parent, INodePO child) { IProjectPO proj = GeneralStorage.getInstance().getProject(); proj.getExecObjCont().removeExecObject((IExecPersistable)child); } } /** * {@inheritDoc} */ public static class CmdHandleEventHandlerIntoMap extends AbstractCmdHandleChild { /** * {@inheritDoc} * @param assocNode specTc which will use the evHandler * @param evHandler evHandler to add to assocNode * @param pos not required (use null) */ public void add(INodePO assocNode, INodePO evHandler, Integer pos) { if (assocNode instanceof ISpecTestCasePO && evHandler instanceof IEventExecTestCasePO) { ISpecTestCasePO usingSpecTc = (ISpecTestCasePO)assocNode; try { usingSpecTc.addEventTestCase( (IEventExecTestCasePO)evHandler); setParentProjectId(usingSpecTc, evHandler); } catch (InvalidDataException e) { log.error(Messages.AttemptToAddAnEventhandlerTwice, e); } } else { throw new JBFatalException( Messages.WrongTypeForAdditionOfEventhandler, MessageIDs.E_UNEXPECTED_EXCEPTION); } } /** * {@inheritDoc} * @param assocNode specTc which contains the evHandler * @param evHandler evHandler to remove from assocNode */ public void remove(INodePO assocNode, INodePO evHandler) { if (assocNode instanceof ISpecTestCasePO && evHandler instanceof IEventExecTestCasePO) { ISpecTestCasePO usingSpecTc = (ISpecTestCasePO)assocNode; usingSpecTc.removeNode(evHandler); } else { throw new JBFatalException( Messages.WrongTypeForRemovalOfEventhandler, MessageIDs.E_UNEXPECTED_EXCEPTION); } } } /** * class variable for Singleton */ private static NodePM nodePersManager = null; /** the logger */ private static Logger log = LoggerFactory.getLogger(NodePM.class); /** cache for project IDs */ private Map<String, Long> m_projectIDCache = null; /** cache for SpecTCs */ private Map<String, Object> m_specTCCache = null; /** Session used in last request */ private EntityManager m_lastSession = null; /** is cache usage enabled */ private boolean m_useCache = false; /** * getter for Singleton * * @return single instance of CapPM problem of database */ public static NodePM getInstance() { if (nodePersManager == null) { nodePersManager = new NodePM(); } return nodePersManager; } /** * Factory for Commands * * @param parent * p * @param child * c * @return C */ public static AbstractCmdHandleChild getCmdHandleChild(INodePO parent, INodePO child) { if (parent == ISpecObjContPO.TCB_ROOT_NODE) { return new CmdHandleChildIntoSpecList(); } else if (parent == IExecObjContPO.TSB_ROOT_NODE) { return new CmdHandleChildIntoExecList(); } else if (parent instanceof ICategoryPO || parent instanceof IAbstractContainerPO) { // category/specTc in category return new CmdHandleChildIntoNodeList(); } else if (parent instanceof ITestSuitePO) { // execTc in testsuite return new CmdHandleChildIntoNodeList(); } else if (parent instanceof ISpecTestCasePO) { if (child instanceof IEventExecTestCasePO) { // eventhandler in using specTc return new CmdHandleEventHandlerIntoMap(); } // execTc or Cap in SpecTestCase return new CmdHandleChildIntoNodeList(); } final String msg = Messages.UnsupportedINodePOSubclass; log.error(msg); throw new JBFatalException(msg, MessageIDs.E_UNSUPPORTED_NODE); } /** * Insert a child and persist to DB * * @param parent * parent of child to insert * @param child * child to insert * @param pos * where to insert the child. if null insert after end * @throws PMSaveException * in case of DB problem or refresh errors * @throws PMAlreadyLockedException in case of locked parent * @throws PMException in case of rollback failed * @throws ProjectDeletedException if the project was deleted in another * instance */ public static void addAndPersistChildNode(INodePO parent, INodePO child, Integer pos) throws PMSaveException, PMAlreadyLockedException, PMException, ProjectDeletedException { final Persistor persistor = Persistor.instance(); final EntityManager sess = persistor.openSession(); try { AbstractCmdHandleChild handler = getCmdHandleChild(parent, child); IProjectPO currProj = GeneralStorage.getInstance().getProject(); EntityTransaction tx = persistor.getTransaction(sess); sess.persist(child); IPersistentObject newParent = null; boolean root = true; if (parent == ISpecObjContPO.TCB_ROOT_NODE) { newParent = currProj.getSpecObjCont(); } else if (parent == IExecObjContPO.TSB_ROOT_NODE) { newParent = currProj.getExecObjCont(); } else { root = false; newParent = parent; } newParent = sess.find(newParent.getClass(), newParent.getId()); persistor.lockPO(sess, newParent); if (root) { handler.setParent(newParent); handler.add(parent, child, pos); } else { handler.add((INodePO) newParent, child, pos); } persistor.commitTransaction(sess, tx); refreshMasterSession(newParent); } catch (PersistenceException e) { PersistenceManager.handleDBExceptionForMasterSession(null, e); } finally { persistor.dropSession(sess); } } /** * Refreshes an object in the master session * @param refr the objects */ public static void refreshMasterSession(IPersistentObject refr) { EntityManager sess = GeneralStorage.getInstance().getMasterSession(); IPersistentObject obj = sess.find(refr.getClass(), refr.getId()); if (obj != null) { try { sess.refresh(obj); } catch (EntityNotFoundException e) { sess.detach(obj); } } } /** * @param node * the node to be renamed * @param newName * the new name * @throws PMDirtyVersionException * in case of dirty version * @throws PMAlreadyLockedException * in case of locked node * @throws PMSaveException * in case of DB save error * @throws PMException in case of general db error * @throws ProjectDeletedException if the project was deleted in another * instance */ public static void renameNode(INodePO node, String newName) throws PMDirtyVersionException, PMAlreadyLockedException, PMSaveException, PMException, ProjectDeletedException { Persistor per = Persistor.instance(); EntityManager sess = per.openSession(); EntityTransaction tx = null; try { INodePO newNode = sess.find(node.getClass(), node.getId()); tx = per.getTransaction(sess); per.lockPO(sess, node); newNode.setName(newName); per.commitTransaction(sess, tx); refreshMasterSession(node); } catch (PersistenceException e) { PersistenceManager.handleDBExceptionForMasterSession(node, e); } finally { per.dropSession(sess); } } /** * @param node * the node to be renamed * @param newComment * the new comment * @throws PMDirtyVersionException * in case of dirty version * @throws PMAlreadyLockedException * in case of locked node * @throws PMSaveException * in case of DB save error * @throws PMException in case of general db error * @throws ProjectDeletedException if the project was deleted in another * instance */ public static void setComment(INodePO node, String newComment) throws PMDirtyVersionException, PMAlreadyLockedException, PMSaveException, PMException, ProjectDeletedException { final Persistor persistor = Persistor.instance(); EntityManager sess = persistor.openSession(); EntityTransaction tx = null; try { tx = persistor.getTransaction(sess); persistor.lockPO(sess, node); node.setComment(newComment); INodePO persNode = sess.find(node.getClass(), node.getId()); persNode.setComment(newComment); persistor.commitTransaction(sess, tx); } catch (PersistenceException e) { PersistenceManager.handleDBExceptionForMasterSession(node, e); } finally { persistor.dropSession(sess); } } /** * @param type * the type of elements to find * @param parentProjectId * ID of the parent project to search in * @param s * The session into which the INodePOs will be loaded. * @return list of param all INodePOs */ @SuppressWarnings({ "unchecked", "rawtypes" }) public static List<? extends INodePO> computeListOfNodes(Class type, Long parentProjectId, EntityManager s) { Assert.isNotNull(type); Assert.isNotNull(s); CriteriaQuery query = s.getCriteriaBuilder().createQuery(); Root from = query.from(type); query.select(from).where( s.getCriteriaBuilder().equal( from.get("hbmParentProjectId"), parentProjectId)); //$NON-NLS-1$ List<INodePO> queryResult = s.createQuery(query).getResultList(); return queryResult; } /** * Returns test cases that reference the test case given information. * Only returns test cases that are in the same project as the given test * case. These test cases are loaded in the Master Session. * Warning: the fetched ExecTestCases have no parent, because the database * doesn't know the parent. * * @param specTcGuid GUID of the test case being reused. * @param parentProjectId ID of the parent project of the test case being * reused. * @return all test cases that reference the test case with the given * information, provided that the cases are also in the same * project. * @see getAllExecTestCases * @see getExternalExecTestCases */ public static List<IExecTestCasePO> getInternalExecTestCases( String specTcGuid, long parentProjectId) { // a SpecTC with guid == null can't be reused if (specTcGuid == null) { return new ArrayList<IExecTestCasePO>(0); } List<Long> parentProjectIds = new ArrayList<Long>(); parentProjectIds.add(parentProjectId); return getExecTestCasesFor(specTcGuid, parentProjectIds, GeneralStorage.getInstance().getMasterSession()); } /** * Returns ref test suites that reference the test suites given information. * Only returns ref test cases that are in the same project as the given * test suite. These ref test suites are loaded in the Master Session. * Warning: the fetched ref test suites have no parent, because the database * doesn't know the parent. * * @param tsGuid * GUID of the test suite being reused. * @param parentProjectId * ID of the parent project of the test case being reused. * @return all ref test suites that reference the test suite with the given * information, provided that the cases are also in the same * project. */ public static List<IRefTestSuitePO> getInternalRefTestSuites( String tsGuid, long parentProjectId) { // a test suite with guid == null can't be reused if (tsGuid == null) { return new ArrayList<IRefTestSuitePO>(0); } List<Long> parentProjectIds = new ArrayList<Long>(); parentProjectIds.add(parentProjectId); return getRefTestSuitesFor(tsGuid, parentProjectIds, GeneralStorage .getInstance().getMasterSession()); } /** * * @param tsGuid The GUID of the reused test suite. * @param parentProjectIds All returned test suites will have one of these as * their project parent ID. * @param s The session into which the test cases will be loaded. * @return list of test suites. */ @SuppressWarnings("unchecked") private static synchronized List<IRefTestSuitePO> getRefTestSuitesFor( String tsGuid, List<Long> parentProjectIds, EntityManager s) { StringBuffer queryBuffer = new StringBuffer( "select ref from RefTestSuitePO as ref where ref.testSuiteGuid = :tsGuid and ref.hbmParentProjectId in :ids"); //$NON-NLS-1$ Query q = s.createQuery(queryBuffer.toString()); q.setParameter("tsGuid", tsGuid); //$NON-NLS-1$ q.setParameter("ids", parentProjectIds); //$NON-NLS-1$ List<IRefTestSuitePO> refTestSuiteList = q.getResultList(); return refTestSuiteList; } /** * Returns test cases that reference the test case given information. Only * returns test cases that are in the same project as the given test case * including test cases from reused projects. These test cases are loaded in * the Master Session. Warning: the fetched ExecTestCases have no parent, * because the database doesn't know the parent. * * @param specTcGuid * GUID of the test case being reused. * @param parentProjectIds * IDs of the parent projects of the test case being reused. * @return all test cases that reference the test case with the given * information, provided that the cases are also in the same * project or reused projects. * @see getAllExecTestCases * @see getExternalExecTestCases * @see getInternalExecTestCases */ public static List<IExecTestCasePO> getExecTestCases( String specTcGuid, List<Long> parentProjectIds) { // a SpecTC with guid == null can't be reused if (specTcGuid == null) { return new ArrayList<IExecTestCasePO>(0); } return getExecTestCasesFor(specTcGuid, parentProjectIds, GeneralStorage.getInstance().getMasterSession()); } /** * Returns test cases that reference the test case given information. Only * returns test cases that are in the same project as the given test case * including test cases from reused projects. These test cases are loaded in * the Master Session. Warning: the fetched ExecTestCases have no parent, * because the database doesn't know the parent. * * @param specTcGuid * GUID of the test case being reused. * @param parentProjectIds * IDs of the parent projects of the test case being reused. * @param session * The session into which the test cases will be loaded. * @return all test cases that reference the test case with the given * information, provided that the cases are also in the same * project or reused projects. * @see getAllExecTestCases * @see getExternalExecTestCases * @see getInternalExecTestCases */ @Nullable public static List<IExecTestCasePO> getExecTestCases(String specTcGuid, List<Long> parentProjectIds, EntityManager session) { // a SpecTC with guid == null can't be reused if (specTcGuid == null) { return null; } return getExecTestCasesFor(specTcGuid, parentProjectIds, session); } /** * * @param specTcGuid The GUID of the reused test case. * @param parentProjectIds All returned test cases will have one of these as * their project parent ID. * @param s The session into which the test cases will be loaded. * @return list of test cases. */ @SuppressWarnings("unchecked") private static synchronized List<IExecTestCasePO> getExecTestCasesFor( String specTcGuid, List<Long> parentProjectIds, EntityManager s) { StringBuffer queryBuffer = new StringBuffer( "select ref from ExecTestCasePO as ref where ref.specTestCaseGuid = :specTcGuid and ref.hbmParentProjectId in :ids"); //$NON-NLS-1$ Query q = s.createQuery(queryBuffer.toString()); q.setParameter("specTcGuid", specTcGuid); //$NON-NLS-1$ q.setParameter("ids", parentProjectIds); //$NON-NLS-1$ List<IExecTestCasePO> execTcList = q.getResultList(); return execTcList; } /** * * @param project the project * @param reused the reused project * @return list of exec test cases in the given project that use * specTestCases from the given reused project */ @SuppressWarnings("unchecked") public static synchronized List<IExecTestCasePO> getUsedTestCaseNames( IProjectPO project, IReusedProjectPO reused) { if (project == null) { return new ArrayList<IExecTestCasePO>(); } EntityManager s = GeneralStorage.getInstance().getMasterSession(); Query q = s.createQuery("select ref from ExecTestCasePO as ref where ref.hbmParentProjectId = :parentProjectId" //$NON-NLS-1$ + " and ref.projectGuid = :projectGuid"); //$NON-NLS-1$ q.setParameter("parentProjectId", project.getId()); //$NON-NLS-1$ q.setParameter("projectGuid", reused.getProjectGuid()); //$NON-NLS-1$ List<IExecTestCasePO> result = q.getResultList(); return result; } /** * * @param project proj * @return read-only list of ISpecPersistable objects */ public static synchronized List<ISpecPersistable> loadSpecObjList( IProjectPO project) { if (project == null) { return new ArrayList<ISpecPersistable>(); } EntityManager s = GeneralStorage.getInstance().getMasterSession(); Query q = s.createQuery("select cont from SpecObjContPO as cont where cont.hbmParentProjectId = :parentProjectId"); //$NON-NLS-1$ q.setParameter("parentProjectId", project.getId()); //$NON-NLS-1$ try { return ((ISpecObjContPO)q.getSingleResult()).getSpecObjList(); } catch (NoResultException nre) { return new ArrayList<ISpecPersistable>(); } } /** * Finds a test case within reused projects. * @param reusedProjects Set of reused projects that are available. * @param projectGuid The GUID of the parent project of the spec testcase * @param specTcGuid The GUID of the spec testcase * @return the spec testcase with the given guid, or <code>null</code> if * the testcase cannot be found */ public static synchronized ISpecTestCasePO getSpecTestCase( Set<IReusedProjectPO> reusedProjects, String projectGuid, String specTcGuid) { ProjectVersion version = null; for (IReusedProjectPO reusedProj : reusedProjects) { if (reusedProj.getProjectGuid().equals(projectGuid)) { version = reusedProj.getProjectVersion(); break; } } if (version == null || (version.getMajorNumber() == null && version.getVersionQualifier() == null)) { return null; } EntityManager s = GeneralStorage.getInstance().getMasterSession(); Long projectId = NodePM.getInstance().findProjectID(s, projectGuid, version); if (projectId == null) { return null; } Object result = NodePM.getInstance().findSpecTC(s, specTcGuid, projectId); if (result instanceof ISpecTestCasePO) { return (ISpecTestCasePO)result; } return null; } /** * find and cache a referenced spec TC * @param s Session * @param specTcGuid GUID of TC * @param projectId ID of project containing TC * @return the resulting TC or null if none was found */ private Object findSpecTC(EntityManager s, String specTcGuid, Long projectId) { validateSession(s); StringBuilder idBuilder = new StringBuilder(50); idBuilder.append(specTcGuid); idBuilder.append(':'); idBuilder.append(projectId); String key = idBuilder.toString(); if (m_useCache) { Object cached = m_specTCCache.get(key); if (cached != null) { if (cached instanceof INodePO) { // check for not found return cached; } return null; } } Query specTcQuery = s.createQuery("select node from SpecTestCasePO as node where node.guid = :guid" //$NON-NLS-1$ + " and node.hbmParentProjectId = :projectId"); //$NON-NLS-1$ specTcQuery.setParameter("guid", specTcGuid); //$NON-NLS-1$ specTcQuery.setParameter("projectId", projectId); //$NON-NLS-1$ Object result = null; try { result = specTcQuery.getSingleResult(); } catch (NoResultException nre) { // No result found. The result remains null. } if (m_useCache) { if (result != null) { m_specTCCache.put(key, result); } else { m_specTCCache.put(key, new Object()); // set a not found marker } } return result; } /** * find and cache a reused projects OID * @param s Session * @param projectGuid GUID of project * @param projVersion the version of project * @return the OID of the project or null if the project cannot be found */ private Long findProjectID(EntityManager s, String projectGuid, ProjectVersion projVersion) { validateSession(s); String key = buildProjectKey(projectGuid, projVersion.getMajorNumber(), projVersion.getMinorNumber(), projVersion.getMicroNumber(), projVersion.getVersionQualifier()); if (m_useCache) { Long id = m_projectIDCache.get(key); if (id != null) { if (id.longValue() != -1) { // means already lookuped but not // found return id; } return null; } } Long projectId = null; try { projectId = ProjectPM.findProjectId(projectGuid, projVersion.getMajorNumber(), projVersion.getMinorNumber(), projVersion.getMicroNumber(), projVersion.getVersionQualifier()); } catch (JBException e) { // ignored - id is therefore null } if (projectId != null) { if (m_useCache) { m_projectIDCache.put(key, projectId); } return projectId; } if (m_useCache) { m_projectIDCache.put(key, new Long(-1)); } return null; } /** * checks if the Session was used before and discards caches if not * @param s Session */ private void validateSession(EntityManager s) { if (m_useCache && m_lastSession != s) { resetCaching(); m_lastSession = s; } } /** * clears all caches */ private void resetCaching() { if (m_projectIDCache != null) { m_projectIDCache.clear(); m_projectIDCache = null; } if (m_specTCCache != null) { m_specTCCache.clear(); m_specTCCache = null; } m_lastSession = null; if (m_useCache) { m_projectIDCache = new HashMap<String, Long>(20); m_specTCCache = new HashMap<String, Object>(500); } } /** * build a key for the cache * * @param projectGuid * part * @param majorNumber * part * @param minorNumber * part * @param microNumber * part * @param versionQualifier * part * @return a key combined from the parts */ private static String buildProjectKey(String projectGuid, Integer majorNumber, Integer minorNumber, Integer microNumber, String versionQualifier) { StringBuilder idBuilder = new StringBuilder(200); idBuilder.append(projectGuid); idBuilder.append(StringConstants.COLON); idBuilder.append(majorNumber); idBuilder.append(StringConstants.COLON); idBuilder.append(minorNumber); idBuilder.append(StringConstants.COLON); idBuilder.append(microNumber); idBuilder.append(StringConstants.COLON); idBuilder.append(versionQualifier); return idBuilder.toString(); } /** * Finds a test case within the project with the given ID. * @param projectId The ID of the parent project of the spec testcase * @param specTcGuid The GUID of the spec testcase * @return the spec testcase with the given guid, or <code>null</code> if * the testcase cannot be found */ public static synchronized ISpecTestCasePO getSpecTestCase(Long projectId, String specTcGuid) { EntityManager s = GeneralStorage.getInstance().getMasterSession(); return (ISpecTestCasePO)NodePM.getInstance().findSpecTC(s, specTcGuid, projectId); } /** * @param child witch parent of node searched * @return the first SpecTestCase parent */ public static ISpecTestCasePO getSpecTestCaseParent(INodePO child) { if (child == null) { return null; } INodePO node = child; if (node instanceof ISpecTestCasePO) { return (ISpecTestCasePO)node; } while (node.getParentNode() != null) { node = node.getParentNode(); if (node instanceof ISpecTestCasePO) { return (ISpecTestCasePO)node; } } return null; } /** * Finds a Test Suite within the currently opened project. * * @param testSuiteGuid The GUID of the Test Suite. * @return the Test Suite with the given GUID, or <code>null</code> if * no such Test Suite can be found. */ @SuppressWarnings({ "unchecked", "rawtypes" }) public static synchronized ITestSuitePO getTestSuite( String testSuiteGuid) { GeneralStorage gs = GeneralStorage.getInstance(); IProjectPO currentProject = gs.getProject(); if (currentProject != null) { EntityManager s = gs.getMasterSession(); CriteriaBuilder builder = s.getCriteriaBuilder(); CriteriaQuery query = builder.createQuery(); Root from = query.from(NodeMaker.getTestSuitePOClass()); query.select(from).where( builder.like(from.get("guid"), testSuiteGuid), //$NON-NLS-1$ builder.equal( from.get("hbmParentProjectId"), currentProject.getId())); //$NON-NLS-1$ try { Object result = s.createQuery(query).getSingleResult(); if (result instanceof ITestSuitePO) { return (ITestSuitePO)result; } } catch (NoResultException nre) { // No result found. Fall through to return null as per javadoc. } } return null; } /** * Finds a node within the project with the given ID. * @param projectId The ID of the parent project of the spec testcase * @param nodeGuid The GUID of the node * @return the spec testcase with the given guid, or <code>null</code> if * the testcase cannot be found */ public static synchronized INodePO getNode(Long projectId, String nodeGuid) { return getNode(projectId, nodeGuid, GeneralStorage.getInstance().getMasterSession()); } /** * Finds a node within the project with the given ID. * @param projectId The ID of the parent project of the spec testcase * @param nodeGuid The GUID of the node * @param session may not be null * @return the spec testcase with the given guid, or <code>null</code> if * the testcase cannot be found */ public static synchronized INodePO getNode(Long projectId, String nodeGuid, EntityManager session) { Validate.notNull(session); Query specTcQuery = session.createQuery("select node from NodePO node where node.guid = :guid" //$NON-NLS-1$ + " and node.hbmParentProjectId = :projectId"); //$NON-NLS-1$ specTcQuery.setParameter("guid", nodeGuid); //$NON-NLS-1$ specTcQuery.setParameter("projectId", projectId); //$NON-NLS-1$ try { Object result = specTcQuery.getSingleResult(); if (result instanceof INodePO) { return (INodePO)result; } } catch (NoResultException nre) { // No result found. Fall through to return null. } return null; } /** * Loads a bag of Nodes into the given session and returns the loaded * Nodes. * * @param projectId The Project in which to search for the Nodes. * @param guids The GUIDs for which to load Nodes. * @param session The session into which to load the Nodes. * @return the loaded Nodes, mapped by GUID. GUIDs for which no node could * be found are mapped to <code>null</code>. */ @SuppressWarnings({ "unchecked", "rawtypes" }) public static synchronized Map<String, INodePO> getNodes(Long projectId, Collection<String> guids, EntityManager session) { CriteriaQuery query = session.getCriteriaBuilder().createQuery(); Root from = query.from(NodeMaker.getNodePOClass()); Predicate parentProjectPred = session.getCriteriaBuilder().equal( from.get("hbmParentProjectId"), projectId); //$NON-NLS-1$ Predicate guidDisjunction = PersistenceUtil.getExpressionDisjunction( guids, from.get("guid"), session.getCriteriaBuilder()); //$NON-NLS-1$ query.select(from).where(parentProjectPred, guidDisjunction); List<INodePO> nodeList = session.createQuery(query).getResultList(); Map<String, INodePO> guidToNodeMap = new HashMap<String, INodePO>(); for (INodePO node : nodeList) { String guid = node.getGuid(); if (!guidToNodeMap.containsKey(guid)) { guidToNodeMap.put(guid, node); } } return guidToNodeMap; } /** * @param parentProjectId The ID of the project for which to find the number * of nodes. * @param sess The session in which to perform the query. * @return The number of nodes that have the project for the given ID as * an absolute parent. */ public static long getNumNodes(long parentProjectId, EntityManager sess) { Query specTcQuery = sess.createQuery("select count(node) from NodePO as " //$NON-NLS-1$ + "node where node.hbmParentProjectId = :parentProjectId"); //$NON-NLS-1$ specTcQuery.setParameter("parentProjectId", parentProjectId); //$NON-NLS-1$ try { return (Long)specTcQuery.getSingleResult(); } catch (NoResultException nre) { return 0; } } /** * @param parentProjectId The ID of the project for which to find the number * of TD managers. * @param sess The session in which to perform the query. * @return The number of TD managers that have the project for the * given ID as an absolute parent. */ public static long getNumTestDataManagers( long parentProjectId, EntityManager sess) { Query tdManQuery = sess.createQuery("select count(tdMan) from TDManagerPO " //$NON-NLS-1$ + "tdMan where tdMan.hbmParentProjectId = :parentProjectId"); //$NON-NLS-1$ tdManQuery.setParameter("parentProjectId", parentProjectId); //$NON-NLS-1$ try { return (Long)tdManQuery.getSingleResult(); } catch (NoResultException nre) { return 0; } } /** * @param parentProjectId The ID of the project for which to find the number * of execTCs. * @param sess The session in which to perform the query. * @return The number of execTCs that have the project for the * given ID as an absolute parent. */ public static long getNumExecTestCases( long parentProjectId, EntityManager sess) { Query execTcQuery = sess.createQuery("select count(execTc) from ExecTestCasePO as " //$NON-NLS-1$ + "execTc where execTc.hbmParentProjectId = :parentProjectId"); //$NON-NLS-1$ execTcQuery.setParameter("parentProjectId", parentProjectId); //$NON-NLS-1$ try { return (Long)execTcQuery.getSingleResult(); } catch (NoResultException nre) { return 0; } } /** * @param parentProjectId The ID of the project for which to find the number * of execTCs. * @param sess The session in which to perform the query. * @return The number of execTCs that have the project for the * given ID as an absolute parent. */ public static long getNumExecTestCasesWithRefTd( long parentProjectId, EntityManager sess) { Query execTcQuery = sess.createQuery("select count(execTc) from ExecTestCasePO as " //$NON-NLS-1$ + "execTc where execTc.hbmParentProjectId = :parentProjectId and execTc.hasReferencedTD = :hasReferencedTD"); //$NON-NLS-1$ execTcQuery.setParameter("parentProjectId", parentProjectId); //$NON-NLS-1$ execTcQuery.setParameter("hasReferencedTD", true); //$NON-NLS-1$ try { return (Long)execTcQuery.getSingleResult(); } catch (NoResultException nre) { return 0; } } /** * @param useCache should the cache be used */ public void setUseCache(boolean useCache) { m_useCache = useCache; resetCaching(); } /** * @return true if the internal cache is in use */ public boolean isUseCache() { return m_useCache; } /** * Class to collect nodes with tracked changes * @author BREDEX GmbH * @created 05.11.2013 */ private static class CollectNodesWithTrackedChangesOperation extends AbstractNonPostOperatingTreeNodeOperation<INodePO> { /** * list of nodes with tracked changes */ private List<INodePO> m_listOfNodesWithTrackedChanges = new ArrayList<INodePO>(); /** * project which contains the nodes */ private IProjectPO m_project; /** * the constructor * @param project the project which contains the nodes */ public CollectNodesWithTrackedChangesOperation(IProjectPO project) { m_project = project; } /** * {@inheritDoc} */ public boolean operate(ITreeTraverserContext<INodePO> ctx, INodePO parent, INodePO node, boolean alreadyVisited) { Long parentProjectId = node.getParentProjectId(); if (parentProjectId != null && !m_project.getId().equals(parentProjectId)) { return false; } if (!node.getTrackedChanges().isEmpty()) { m_listOfNodesWithTrackedChanges.add(node); } return true; } /** * returns the list of nodes with tracked changes * @return the list of nodes with tracked changes */ public List<INodePO> getListOfNodesWithTrackedChanges() { return m_listOfNodesWithTrackedChanges; } } /** * Deletes all tracked changes of a project * @param monitor the monitor * @param project the project * @return the map from changed nodes to whether they were locked (for which tracked changes could not be deleted) * @throws ProjectDeletedException * @throws PMException */ public static Map<INodePO, Boolean> cleanupTrackedChanges( IProgressMonitor monitor, final IProjectPO project) throws PMException, ProjectDeletedException { CollectNodesWithTrackedChangesOperation treeNodeOp = new CollectNodesWithTrackedChangesOperation(project); TreeTraverser treeTraverser = new TreeTraverser( project, treeNodeOp, true, true); treeTraverser.traverse(); List<INodePO> listOfNodesWithTrackedChanges = treeNodeOp.getListOfNodesWithTrackedChanges(); Map<INodePO, Boolean> nodeToWasLockedMap = new HashMap<>( listOfNodesWithTrackedChanges.size()); monitor.beginTask(Messages.DeleteTrackedChangesActionDialog, listOfNodesWithTrackedChanges.size()); final Persistor persistor = Persistor.instance(); final EntityManager session = persistor.openSession(); EntityTransaction tx = null; tx = persistor.getTransaction(session); for (INodePO node: listOfNodesWithTrackedChanges) { try { persistor.lockPO(session, node); node.deleteTrackedChanges(); session.merge(node); nodeToWasLockedMap.put(node, new Boolean(false)); } catch (PMException | PersistenceException e) { // can not delete tracked changes of this node nodeToWasLockedMap.put(node, new Boolean(true)); } monitor.worked(1); } persistor.commitTransaction(session, tx); persistor.dropSession(session); EntityManager master = GeneralStorage.getInstance().getMasterSession(); for (INodePO key : nodeToWasLockedMap.keySet()) { if (!nodeToWasLockedMap.get(key)) { INodePO refr = master.find(key.getClass(), key.getId()); if (refr != null) { master.refresh(refr); } } } monitor.done(); return nodeToWasLockedMap; } }