/*******************************************************************************
* 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.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.EntityTransaction;
import javax.persistence.NoResultException;
import javax.persistence.PersistenceException;
import javax.persistence.Query;
import org.apache.commons.lang.Validate;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.jubula.client.core.businessprocess.CompNameManager;
import org.eclipse.jubula.client.core.businessprocess.IComponentNameCache;
import org.eclipse.jubula.client.core.businessprocess.INameMapper;
import org.eclipse.jubula.client.core.businessprocess.IWritableComponentNameCache;
import org.eclipse.jubula.client.core.businessprocess.ParamNameBP;
import org.eclipse.jubula.client.core.businessprocess.ProjectNameBP;
import org.eclipse.jubula.client.core.businessprocess.UsedToolkitBP;
import org.eclipse.jubula.client.core.businessprocess.progress.OperationCanceledUtil;
import org.eclipse.jubula.client.core.businessprocess.progress.ProgressMonitorTracker;
import org.eclipse.jubula.client.core.events.DataEventDispatcher;
import org.eclipse.jubula.client.core.events.DataEventDispatcher.IProjectLoadedListener;
import org.eclipse.jubula.client.core.i18n.Messages;
import org.eclipse.jubula.client.core.model.IExecTestCasePO;
import org.eclipse.jubula.client.core.model.INodePO;
import org.eclipse.jubula.client.core.model.IProjectNamePO;
import org.eclipse.jubula.client.core.model.IProjectPO;
import org.eclipse.jubula.client.core.model.IReusedProjectPO;
import org.eclipse.jubula.client.core.model.ISpecTestCasePO;
import org.eclipse.jubula.client.core.model.NodeMaker;
import org.eclipse.jubula.client.core.model.ProjectVersion;
import org.eclipse.jubula.toolkit.common.businessprocess.ToolkitSupportBP;
import org.eclipse.jubula.toolkit.common.exception.ToolkitPluginException;
import org.eclipse.jubula.toolkit.common.utils.ToolkitUtils;
import org.eclipse.jubula.tools.internal.constants.StringConstants;
import org.eclipse.jubula.tools.internal.exception.JBException;
import org.eclipse.jubula.tools.internal.exception.JBFatalAbortException;
import org.eclipse.jubula.tools.internal.exception.ProjectDeletedException;
import org.eclipse.jubula.tools.internal.messagehandling.MessageIDs;
import org.eclipse.jubula.tools.internal.version.IVersion;
import org.eclipse.osgi.util.NLS;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author BREDEX GmbH
* @created 15.07.2005
*/
public class ProjectPM extends PersistenceManager
implements IProjectLoadedListener {
/**
* number of add/insert-related Persistence event types with
* progress listeners
*/
// Event types:
// save, recreateCollection, postInsert, postUpdate
private static final int NUM_HBM_ADD_PROGRESS_EVENT_TYPES = 4;
/** standard logging */
private static Logger log = LoggerFactory.getLogger(ProjectPM.class);
/** project guid cache */
private static Map<Long, String> guidCache = new HashMap<Long, String>(17);
/** reused projects cache */
private static Map<Long, List<IReusedProjectPO>> rpCache =
new HashMap<Long, List<IReusedProjectPO>>(17);
/**
* constructor must be hidden for class utilities (per CheckStyle)
*/
private ProjectPM() {
DataEventDispatcher ded = DataEventDispatcher.getInstance();
ded.addProjectLoadedListener(this, true);
}
// provide a base for cache clearing when projects are loaded
static {
DataEventDispatcher ded = DataEventDispatcher.getInstance();
ProjectPM anchor = new ProjectPM();
ded.addProjectLoadedListener(anchor, true);
}
/**
* @see IProjectLoadedListener#handleProjectLoaded()
*/
public void handleProjectLoaded() {
clearCaches();
}
/**
* drop all cached data
*/
public static void clearCaches() {
rpCache.clear();
guidCache.clear();
}
/**
* @return list with all available projects from database the project
* instances are detached from their session
* @throws JBException
* ...
*/
public static synchronized List<IProjectPO> findAllProjects()
throws JBException {
ProjectNameBP.getInstance().clearCache();
EntityManager session = null;
try {
session = Persistor.instance().openSession();
return findAllProjects(session);
} catch (PersistenceException e) {
log.error(Messages.PersistenceLoadFailed, e);
throw new JBException(e.getMessage(),
MessageIDs.E_PERSISTENCE_LOAD_FAILED);
} finally {
Persistor.instance().dropSessionWithoutLockRelease(session);
}
}
/**
*
* @param sess The session in which to execute the query.
* @return list with all available projects from database the project
* instances are detached from their session
* @throws PersistenceException if a Persistence error occurs.
*
*/
@SuppressWarnings("unchecked")
public static synchronized List<IProjectPO> findAllProjects(
EntityManager sess) throws PersistenceException {
Query query = sess.createQuery("select project from ProjectPO" //$NON-NLS-1$
+ " as project where project.clientMetaDataVersion = :majorversion"); //$NON-NLS-1$
query.setParameter(
"majorversion", IVersion.JB_CLIENT_METADATA_VERSION); //$NON-NLS-1$
return query.getResultList();
}
/**
* @param guid
* GUID of the project to load
* @param majorVersion
* Major version number of the project to load
* @param minorVersion
* Minor version number of the project to load
* @param microVersion
* Micro version number of the project to load
* @param versionQualifier
* Version qualifier of the project to load
* @return the Project with the given attributes, or <code>null</code> if
* no such Project could be found. The returned Project is not
* associated with an Entity Manager.
* @throws JBException
* ...
*/
public static synchronized IProjectPO loadProjectByGuidAndVersion(
String guid, Integer majorVersion, Integer minorVersion,
Integer microVersion, String versionQualifier) throws JBException {
EntityManager session = null;
try {
session = Persistor.instance().openSession();
StringBuilder sb = new StringBuilder("select project from ProjectPO as project" //$NON-NLS-1$
+ " inner join fetch project.properties where project.guid = :guid"); //$NON-NLS-1$
addCompleteVersionString(sb, majorVersion, minorVersion,
microVersion, versionQualifier);
Query query = session.createQuery(sb.toString());
query.setParameter("guid", guid); //$NON-NLS-1$
attachParameterToVersion(query, majorVersion, minorVersion,
microVersion, versionQualifier);
try {
IProjectPO project = (IProjectPO)query.getSingleResult();
UsedToolkitBP.getInstance().readUsedToolkitsFromDB(project);
return project;
} catch (NoResultException nre) {
// No result found. Return null as per the javadoc.
return null;
}
} catch (PersistenceException e) {
OperationCanceledException oce = checkForCancel(e);
if (oce != null) {
throw oce;
}
log.error(Messages.PersistenceLoadFailed, e);
throw new JBException(e.getMessage(),
MessageIDs.E_PERSISTENCE_LOAD_FAILED);
} finally {
Persistor.instance().dropSessionWithoutLockRelease(session);
}
}
/**
* @param guid
* GUID of the project to load
* @param majorVersion
* Major version number of the project to load
* @param minorVersion
* Minor version number of the project to load
* @param microVersion
* Micro version number of the project to load
* @param versionQualifier
* Version qualifier of the project to load
* @return the Project with the given attributes, or <code>null</code> if
* no such Project could be found. The returned Project is not
* associated with an Entity Manager.
* @throws JBException
* ...
*/
public static synchronized Long findProjectIDByGuidAndVersion(
String guid, Integer majorVersion, Integer minorVersion,
Integer microVersion, String versionQualifier) throws JBException {
EntityManager session = null;
try {
session = Persistor.instance().openSession();
Query query = createProjectQuery(guid, majorVersion, minorVersion,
microVersion, versionQualifier, session, true);
try {
Long projectID = (Long)query.getSingleResult();
return projectID;
} catch (NoResultException nre) {
// No result found. Return null as per the javadoc.
return null;
}
} catch (PersistenceException e) {
OperationCanceledException oce = checkForCancel(e);
if (oce != null) {
throw oce;
}
log.error(Messages.PersistenceLoadFailed, e);
throw new JBException(e.getMessage(),
MessageIDs.E_PERSISTENCE_LOAD_FAILED);
} finally {
Persistor.instance().dropSessionWithoutLockRelease(session);
}
}
/**
* @param name
* name of the project to load
* @param projectVersion
* Version of the project to load
* @return the Project with the given attributes, or <code>null</code> if
* no such Project could be found. The returned Project is not
* associated with an Entity Manager.
* @throws JBException
* ...
*/
public static synchronized IProjectPO loadProjectByNameAndVersion(
String name, ProjectVersion projectVersion) throws JBException {
return loadProjectByNameAndVersion(name,
projectVersion.getMajorNumber(),
projectVersion.getMinorNumber(),
projectVersion.getMicroNumber(),
projectVersion.getVersionQualifier());
}
/**
* @param name
* Name of the project to load
* @param majorVersion
* Major version number of the project to load
* @param minorVersion
* Minor version number of the project to load
* @param microVersion
* Micro version number of the project to load
* @param versionQualifier
* Version qualifier of the project to load
* @return the Project with the given attributes, or <code>null</code> if
* no such Project could be found. The returned Project is not
* associated with an Entity Manager.
* @throws JBException
* ...
*/
public static synchronized IProjectPO loadProjectByNameAndVersion(
String name, Integer majorVersion, Integer minorVersion,
Integer microVersion, String versionQualifier) throws JBException {
EntityManager session = null;
String guid = StringConstants.EMPTY;
try {
session = Persistor.instance().openSession();
Query query = session.createQuery("select name.hbmGuid " //$NON-NLS-1$
+ "from ProjectNamePO as name" //$NON-NLS-1$
+ " where name.hbmName = :name"); //$NON-NLS-1$
query.setParameter("name", name); //$NON-NLS-1$
try {
guid = (String)query.getSingleResult();
} catch (NoResultException nre) {
// No result found. Return null as per the javadoc.
return null;
}
} catch (PersistenceException e) {
log.error(Messages.PersistenceLoadFailed, e);
throw new JBException(e.getMessage(),
MessageIDs.E_PERSISTENCE_LOAD_FAILED);
} finally {
Persistor.instance().dropSessionWithoutLockRelease(session);
}
return loadProjectByGuidAndVersion(guid, majorVersion, minorVersion,
microVersion, versionQualifier);
}
/**
* @return the project with the given name and the highest version number,
* or <code>null</code> if no project with the given name is found.
* The project instance is detached from the session.
* @param name
* Name of the project to load
* @throws JBException
* ...
*/
public static synchronized IProjectPO loadLatestVersionOfProjectByName(
String name) throws JBException {
EntityManager session = null;
String guid = StringConstants.EMPTY;
Integer majorVersion = null;
Integer minorVersion = null;
Integer microVersion = null;
String versionQualifier = null;
try {
session = Persistor.instance().openSession();
Query query = session.createQuery("select project.hbmGuid " //$NON-NLS-1$
+ "from ProjectNamePO as project" //$NON-NLS-1$
+ " where project.hbmName = :name"); //$NON-NLS-1$
query.setParameter("name", name); //$NON-NLS-1$
try {
guid = (String)query.getSingleResult();
} catch (NoResultException nre) {
return null;
}
ProjectVersion versionNumber = findHighestVersionNumber(guid);
majorVersion = versionNumber.getMajorNumber();
minorVersion = versionNumber.getMinorNumber();
microVersion = versionNumber.getMicroNumber();
versionQualifier = versionNumber.getVersionQualifier();
} catch (NumberFormatException nfe) {
log.error(Messages.InvalidProjectVersionNumber, nfe);
throw new JBException(nfe.getMessage(),
MessageIDs.E_INVALID_PROJECT_VERSION);
} catch (PersistenceException e) {
log.error(Messages.PersistenceLoadFailed, e);
throw new JBException(e.getMessage(),
MessageIDs.E_PERSISTENCE_LOAD_FAILED);
} finally {
Persistor.instance().dropSessionWithoutLockRelease(session);
}
return loadProjectByGuidAndVersion(guid, majorVersion, minorVersion,
microVersion, versionQualifier);
}
/**
* Loads the project in a new session, then closes the session. The returned
* IProjectPO is therefore detached from its session.
* @param reused
* The reused project information for this project.
* @return the ProjectPO or null if no project in db
* @throws JBException in case of general db access errors (db disconnect, shutdown, etc)
*/
public static synchronized IProjectPO loadProject(IReusedProjectPO reused)
throws JBException {
EntityManager session = Persistor.instance().openSession();
try {
return loadProjectInSession(reused, session);
} finally {
Persistor.instance().dropSessionWithoutLockRelease(session);
}
}
/**
* Loads the project from the master session. The returned
* IProjectPO MUST only be used in the master session or read only.
* @param reused
* The reused project information for this project.
* @return the ProjectPO or null if no project in db
* @throws JBException in case of general db access errors (db disconnect, shutdown, etc)
*/
public static synchronized IProjectPO loadProjectFromMaster(
IReusedProjectPO reused) throws JBException {
EntityManager session = GeneralStorage.getInstance().getMasterSession();
return loadProjectInSession(reused, session);
}
/**
* Loads the project in a session. This is shared code for detached in
* master session loading.
* @param reused
* The reused project information for this project.
* @param session
* Session context for db ops
* @return the ProjectPO or null if no project in db
* @throws JBFatalAbortException
* @throws OperationCanceledException
* @throws JBException in case of general db access errors (db disconnect, shutdown, etc)
*/
private static IProjectPO loadProjectInSession(IReusedProjectPO reused,
EntityManager session) throws JBFatalAbortException,
OperationCanceledException, JBException {
try {
Query query = createProjectQuery(reused.getProjectGuid(),
reused.getMajorNumber(), reused.getMinorNumber(),
reused.getMicroNumber(), reused.getVersionQualifier(),
session, false);
try {
return (IProjectPO)query.getSingleResult();
} catch (NoResultException nre) {
return null;
}
} catch (PersistenceException e) {
OperationCanceledUtil.checkForOperationCanceled(e);
log.error(Messages.PersistenceLoadFailed, e);
throw new JBException(e.getMessage(),
MessageIDs.E_PERSISTENCE_LOAD_FAILED);
}
}
/**
* Loads the project represented by the ReusedProjectPO into the master
* session.
*
* @param reused The reused project information for the project.
* @return the loaded project.
* @throws JBException
*/
public static synchronized IProjectPO loadReusedProjectInMasterSession(
IReusedProjectPO reused) throws JBException {
EntityManager masterSession =
GeneralStorage.getInstance().getMasterSession();
try {
Integer major = reused.getMajorNumber();
Integer minor = reused.getMinorNumber();
Integer micro = reused.getMicroNumber();
String qualifier = reused.getVersionQualifier();
StringBuilder sb = new StringBuilder("select project from ProjectPO project" //$NON-NLS-1$
+ " inner join fetch project.properties where project.guid = :guid"); //$NON-NLS-1$
addCompleteVersionString(sb, major, minor,
micro, qualifier);
Query query = masterSession.createQuery(sb.toString());
query.setParameter("guid", reused.getProjectGuid()); //$NON-NLS-1$
attachParameterToVersion(query, major, minor,
micro, qualifier);
IProjectPO project = null;
try {
project = (IProjectPO)query.getSingleResult();
} catch (NoResultException nre) {
// No result found. project remains null.
}
ParamNameBP.getInstance().initParamNamesOfReusedProject(reused);
UsedToolkitBP.getInstance().readUsedToolkitsFromDB(project);
return project;
} catch (PersistenceException e) {
log.error(Messages.PersistenceLoadFailed, e);
throw new JBException(e.getMessage(),
MessageIDs.E_PERSISTENCE_LOAD_FAILED);
}
}
/**
* Loads the project in a new session and then closes the session.
*
* @return the project with the given identifying information, or
* <code>null</code> if the project could not be found.
* @param reusedProjectInfo
* Information for finding the reused project
* @throws JBException
* ...
*/
public static synchronized IProjectPO loadReusedProject(
IReusedProjectPO reusedProjectInfo) throws JBException {
EntityManager session = null;
try {
session = Persistor.instance().openSession();
return loadReusedProject(reusedProjectInfo, session);
} catch (PersistenceException e) {
log.error(Messages.PersistenceLoadFailed, e);
throw new JBException(e.getMessage(),
MessageIDs.E_PERSISTENCE_LOAD_FAILED);
} finally {
Persistor.instance().dropSessionWithoutLockRelease(session);
}
}
/**
* Loads the project in the given session.
*
* @return the project with the given identifying information, or
* <code>null</code> if the project could not be found.
* @param reusedProjectInfo
* Information for finding the reused project
* @param session
* The session into which the Project will be loaded
* @throws JBException
* ...
*/
public static synchronized IProjectPO loadReusedProject(
IReusedProjectPO reusedProjectInfo, EntityManager session)
throws JBException {
try {
Query query = createProjectQuery(
reusedProjectInfo.getProjectGuid(),
reusedProjectInfo.getMajorNumber(),
reusedProjectInfo.getMinorNumber(),
reusedProjectInfo.getMicroNumber(),
reusedProjectInfo.getVersionQualifier(), session, false);
try {
return (IProjectPO)query.getSingleResult();
} catch (NoResultException nre) {
return null;
}
} catch (PersistenceException e) {
log.error(Messages.PersistenceLoadFailed, e);
throw new JBException(e.getMessage(),
MessageIDs.E_PERSISTENCE_LOAD_FAILED);
}
}
/**
* Finds the Project ID of the Project with the given GUID, Major Version
* and Minor Version.
* @param projGuid the GUID
* @param projMajVers the Major Version
* @param projMinVers the Minor Version
* @param projMicVers the Micro Version
* @param projVersQual the Version Qualifier
* @return an ID or null if not found
* @throws JBException ...
*/
public static final synchronized Long findProjectId(String projGuid,
Integer projMajVers, Integer projMinVers,
Integer projMicVers, String projVersQual) throws JBException {
EntityManager session = null;
try {
session = Persistor.instance().openSession();
Query query = createProjectQuery(projGuid, projMajVers,
projMinVers, projMicVers, projVersQual, session, true);
try {
return (Long)query.getSingleResult();
} catch (NoResultException nre) {
return null;
}
} catch (PersistenceException e) {
log.error(Messages.PersistenceLoadFailed, e);
throw new JBException(e.getMessage(),
MessageIDs.E_PERSISTENCE_LOAD_FAILED);
} finally {
Persistor.instance().dropSessionWithoutLockRelease(session);
}
}
/**
* Gets a List of IReusedProjectPO of the Project with the given GUID,
* MajorVersion and MinorVersion.
* @param projGuid the GUID of the Project which IReusedProjectPOs are wanted.
* @param projMajVers the Major Version of the Project which IReusedProjectPOs are wanted.
* @param projMinVers the Minor Version of the Project which IReusedProjectPOs are wanted.
* @param projMicVers the Micro Version of the Project which IReusedProjectPOs are wanted.
* @param projQualVers the Version Qualifier of the Project which IReusedProjectPOs are wanted.
* @return a List of IReusedProjectPO or an empty List of nothing found.
* @throws JBException ...
*/
public static final synchronized List<IReusedProjectPO>
loadReusedProjectsRO(
String projGuid, Integer projMajVers, Integer projMinVers,
Integer projMicVers, String projQualVers)
throws JBException {
return loadReusedProjectsRO(
findProjectId(projGuid, projMajVers, projMinVers, projMicVers,
projQualVers));
}
/**
* Gets a List of IReusedProjectPO of the Project with the given ID.
* @param projectId the ID of the Project which IReusedProjectPOs are wanted.
* @return a List of IReusedProjectPO or an empty List of nothing found.
* @throws JBException ...
*/
public static final List<IReusedProjectPO> loadReusedProjectsRO(
Long projectId) throws JBException {
final List<IReusedProjectPO> cachedList = rpCache.get(projectId);
if (cachedList != null && !cachedList.isEmpty()) {
return cachedList;
}
EntityManager session = GeneralStorage.getInstance().getMasterSession();
final List<IReusedProjectPO> list = new ArrayList<IReusedProjectPO>();
try {
if (projectId != null) {
final Query query =
session.createQuery(
"select reusedProj from ReusedProjectPO reusedProj" //$NON-NLS-1$
+ " where reusedProj.hbmParentProjectId = :parentProjId"); //$NON-NLS-1$
query.setParameter("parentProjId", projectId); //$NON-NLS-1$
list.addAll(query.getResultList());
}
} catch (PersistenceException e) {
log.error(Messages.PersistenceLoadFailed, e);
throw new JBException(e.getMessage(),
MessageIDs.E_PERSISTENCE_LOAD_FAILED);
}
rpCache.put(projectId, list);
return list;
}
/**
* Load an instance of ProjectPO into the readonly session. The read
* instance is db identical to the key supplied.
* used for open project
*
* @param project
* Look for this instance in the db.
* @throws PMReadException
* if loading from db failed
*
*/
public static void loadProjectInROSession(IProjectPO project)
throws PMReadException {
GeneralStorage.getInstance().reset();
EntityManager s = GeneralStorage.getInstance().getMasterSession();
s.clear(); // get rid of all session (cached) data
try {
preloadData(s, project);
IProjectPO p = s.find(NodeMaker.getProjectPOClass(),
project.getId());
GeneralStorage.getInstance().setProjectLoadReused(p);
ParamNameBP.getInstance().initMap();
CompNameManager.getInstance().init();
} catch (PersistenceException e) {
GeneralStorage.getInstance().nullProject();
OperationCanceledException cancel = checkForCancel(e);
if (cancel != null) {
throw cancel;
}
String msg = Messages.CantReadProjectFromDatabase
+ StringConstants.DOT;
log.error(Messages.UnexpectedPersistenceErrorIgnored
+ StringConstants.DOT, e);
throw new PMReadException(msg + e.getMessage(),
MessageIDs.E_CANT_READ_PROJECT);
} catch (PMException e) {
String msg = Messages.CouldNotReadParamNamesFromDB
+ StringConstants.DOT;
log.error(msg, e);
throw new PMReadException(msg + e.getMessage(),
MessageIDs.E_CANT_READ_PROJECT);
} catch (JBException e) {
GeneralStorage.getInstance().nullProject();
String msg = Messages.CantReadProjectFromDatabase
+ StringConstants.DOT;
log.error(Messages.UnexpectedPersistenceErrorIgnored, e);
throw new PMReadException(msg + e.getMessage(),
MessageIDs.E_CANT_READ_PROJECT);
}
}
/**
* Check if the cause of the PersistenceException was a
* OperationCanceledException
* @param e origonal exception
* @return instance of OperationCanceledException if e was cause by it or
* null.
*/
private static OperationCanceledException checkForCancel(
PersistenceException e) {
Throwable cause = e.getCause();
while (cause != null) {
if (cause instanceof OperationCanceledException) {
return (OperationCanceledException)cause;
}
cause = cause.getCause();
}
return null;
}
/**
* iterate over a the reused list and check for reused project inside
* @param reused A Set of accumulated IDs of reused project
* @param check the current set of reused project under inspection
* @throws JBException in case of DB problem
*/
private static void findReusedProjects(Set<Long> reused,
Set<IReusedProjectPO> check) throws JBException {
for (IReusedProjectPO ru : check) {
IProjectPO ruP = loadProjectFromMaster(ru);
if (ruP != null) { // check for dangling reference
if (reused.add(ruP.getId())) {
findReusedProjects(reused,
ruP.getProjectProperties().getUsedProjects());
}
}
}
}
/**
* @see ProjectPM#getReusedProjectsForProject(EntityManager, long)
* @param projectID
* Object id for the project to be used
* @return A List of IReusedProjectPOs for the submitted projectID. The Set
* may be empty. Since the session is closed after this call the
* resulting entities are detached.
*/
public static List<IReusedProjectPO> getReusedProjectsForProject(
long projectID) throws PMException {
EntityManager session = null;
try {
session = Persistor.instance().openSession();
return getReusedProjectsForProject(session, projectID);
} catch (PersistenceException e) {
log.error(Messages.PersistenceLoadFailed, e);
throw new PMException(e.getMessage(),
MessageIDs.E_PERSISTENCE_LOAD_FAILED);
} finally {
Persistor.instance().dropSessionWithoutLockRelease(session);
}
}
/**
* @see ProjectPM#getReusedProjectsForProject(EntityManager, long)
* @param projectID
* Object id for the project to be used
* @return A List of IReusedProjectPOs for the submitted projectID. The Set
* may be empty.
*/
public static List<IReusedProjectPO> getReusedProjectsForProjectRO(
long projectID) throws PMException {
EntityManager session = GeneralStorage.getInstance().getMasterSession();
try {
return getReusedProjectsForProject(session, projectID);
} catch (PersistenceException e) {
log.error(Messages.PersistenceLoadFailed, e);
throw new PMException(e.getMessage(),
MessageIDs.E_PERSISTENCE_LOAD_FAILED);
}
}
/**
* Loads a Set<IReusedProject> in a session.
*
* @param s Session to be used for DB access
* @param projectID Object id for the project to be used
* @return A Set of IReusedProjectPOs for the submitted projectID. The Set may be empty.
*/
public static List<IReusedProjectPO> getReusedProjectsForProject(
EntityManager s, long projectID) {
Query q =
s.createQuery("select project from ReusedProjectPO project where project.hbmParentProjectId = :projectID"); //$NON-NLS-1$
q.setParameter("projectID", projectID); //$NON-NLS-1$
@SuppressWarnings("unchecked")
List<IReusedProjectPO> result = q.getResultList();
return result;
}
/**
* @param s Session to use
* @param key If of Project to preload
*/
@SuppressWarnings({ "nls", "unchecked" })
private static void preloadData(EntityManager s, IProjectPO key)
throws JBException {
// Determine Sub Projects
Set<Long> projectIds = new HashSet<Long>(17);
projectIds.add(key.getId());
// adds all project ids of reused projects to set
findReusedProjects(projectIds,
key.getProjectProperties().getUsedProjects());
preloadDataForClass(s, projectIds, "CompNamesPairPO"); //$NON-NLS-1$
preloadDataForClass(s, projectIds, "CompIdentifierPO"); //$NON-NLS-1$
preloadDataForClass(s, projectIds, "AUTConfigPO"); //$NON-NLS-1$
preloadDataForClass(s, projectIds, "AUTMainPO"); //$NON-NLS-1$
preloadDataForClass(s, projectIds, "ReusedProjectPO"); //$NON-NLS-1$
preloadDataForClass(s, projectIds, "UsedToolkitPO"); //$NON-NLS-1$
preloadDataForClass(s, projectIds, "AUTContPO"); //$NON-NLS-1$
preloadDataForClass(s, projectIds, "ParamDescriptionPO"); //$NON-NLS-1$
preloadDataForClass(s, projectIds, "CondStructPO"); //$NON-NLS-1$
// Special pre-load due to http://eclip.se/432394
preloadDistinctDataForClass(s, projectIds, "TestDataCubePO"); //$NON-NLS-1$
preloadDataForClass(s, projectIds, "CapPO"); //$NON-NLS-1$
List<ISpecTestCasePO> testCases =
preloadDataForClass(s, projectIds, "SpecTestCasePO"); //$NON-NLS-1$
preloadDataForClass(
s, projectIds, "EventExecTestCasePO"); //$NON-NLS-1$
preloadDataForClass(s, projectIds, "TestSuitePO"); //$NON-NLS-1$
List<IExecTestCasePO> testCaseRefs =
preloadDataForClass(s, projectIds, "ExecTestCasePO"); //$NON-NLS-1$
preloadDataForClass(s, projectIds, "CategoryPO"); //$NON-NLS-1$
// for performance reasons, we prefill the cachedSpecTestCase
// in ExecTestCasePOs
Map<String, ISpecTestCasePO> sTc =
new HashMap<String, ISpecTestCasePO>();
for (ISpecTestCasePO testCase : testCases) {
sTc.put(testCase.getGuid(), testCase);
}
for (IExecTestCasePO testCaseRef : testCaseRefs) {
ISpecTestCasePO spec = sTc.get(testCaseRef.getSpecTestCaseGuid());
if (spec != null) {
testCaseRef.setCachedSpecTestCase(spec);
}
}
}
/**
* The Class for the given simple name must have the JPA attribute
* "hbmParentProjectId", as this attribute will be used to identify
* which elements should be preloaded.
*
* @param s Session to use
* @param projectIds Ids of projects
* @param simpleClassName class name for the prefetch
* @return List loaded data
*/
private static List preloadDataForClass(EntityManager s, Set projectIds,
String simpleClassName) {
StringBuilder qString = new StringBuilder(100);
qString.append("select e from "); //$NON-NLS-1$
qString.append(simpleClassName);
qString.append(" as e where e.hbmParentProjectId in :ids"); //$NON-NLS-1$
Query q = s.createQuery(qString.toString());
q.setParameter("ids", projectIds); //$NON-NLS-1$
return q.getResultList();
}
/**
* The Class for the given simple name must have the JPA attribute
* "hbmParentProjectId", as this attribute will be used to identify
* which elements should be preloaded.
*
* @param s Session to use
* @param projectIds Ids of projects
* @param simpleClassName class name for the prefetch
* @return list of distinctly pre-loaded data
*/
private static List preloadDistinctDataForClass(
EntityManager s, Set projectIds, String simpleClassName) {
StringBuilder qString = new StringBuilder(100);
qString.append("select DISTINCT e from "); //$NON-NLS-1$
qString.append(simpleClassName);
qString.append(" as e where e.hbmParentProjectId in :ids"); //$NON-NLS-1$
Query q = s.createQuery(qString.toString());
q.setParameter("ids", projectIds); //$NON-NLS-1$
return q.getResultList();
}
/**
*
* @param proj
* ProjectPO to be attached and saved.
* @param newProjectName
* name part of the ProjectNamePO. If there is no new name, this
* parameter must be null (same project, different version)
* @param mapperList mapper to resolve Parameter Names
* @param compNameBindingList cache to resolve Component Names
* @param monitor The progress monitor for this potentially long-running
* operation.
* @throws PMException
* in case of any db error
* @throws ProjectDeletedException if project is already deleted
* @throws InterruptedException if the operation was canceled.
*/
public static void attachProjectToROSession(IProjectPO proj,
String newProjectName, List<INameMapper> mapperList,
List<IWritableComponentNameCache> compNameBindingList,
IProgressMonitor monitor)
throws PMException, ProjectDeletedException, InterruptedException {
monitor.beginTask(NLS.bind(Messages.ProjectWizardCreatingProject,
newProjectName),
getTotalWorkForSave(proj));
// Register Persistence progress listeners
setHbmProgressMonitor(monitor);
GeneralStorage.getInstance().reset();
EntityManager s = GeneralStorage.getInstance().getMasterSession();
EntityTransaction tx = null;
try {
tx = Persistor.instance().getTransaction(s);
s.persist(proj);
proj.setParentProjectId(proj.getId());
if (newProjectName != null) {
ProjectNameBP.getInstance().setName(s, proj.getGuid(),
newProjectName);
}
ProjectNameBP.getInstance().storeTransientNames(s);
for (INameMapper mapper : mapperList) {
mapper.persist(s, proj.getId());
}
for (IWritableComponentNameCache compNameBinding
: compNameBindingList) {
CompNamePM.flushCompNamesImport(s, proj.getId(),
compNameBinding);
}
if (!monitor.isCanceled()) {
Persistor.instance().commitTransaction(s, tx);
GeneralStorage.getInstance().setProjectLoadReused(proj);
for (INameMapper mapper : mapperList) {
mapper.updateStandardMapperAndCleanup(proj.getId());
}
for (IComponentNameCache compNameBinding
: compNameBindingList) {
compNameBinding.updateStandardMapperAndCleanup(
proj.getId());
}
} else {
Persistor.instance().rollbackTransaction(s, tx);
GeneralStorage.getInstance().reset();
for (INameMapper mapper : mapperList) {
mapper.clearAllNames();
}
for (IComponentNameCache compNameBinding
: compNameBindingList) {
compNameBinding.clear();
}
throw new InterruptedException();
}
} catch (PMException pme) {
handleAlreadyLockedException(mapperList, s, tx);
} catch (OperationCanceledException oce) {
handleOperationCanceled(mapperList, s, tx);
} catch (PersistenceException | JBException e) {
handleOtherException(mapperList, s, tx, e);
} finally {
// Remove Persistence progress listeners
setHbmProgressMonitor(null);
}
initBPs(proj);
}
/**
* Handles an "object locked" situation
*
* @param mapperList The Parameter Name mapping list.
* @param s The session.
* @param tx The transaction.
* @throws PMException if the transaction cannot be rolled back.
* @throws PMException to indicate that the operation couldn't be
* completed because another lock existed.
*/
private static void handleAlreadyLockedException(
List<INameMapper> mapperList, EntityManager s,
EntityTransaction tx) throws PMException {
if (tx != null) {
Persistor.instance().rollbackTransaction(s, tx);
}
GeneralStorage.getInstance().reset();
for (INameMapper mapper : mapperList) {
mapper.clearAllNames();
}
String msg = Messages.CantAttachProject + StringConstants.DOT;
throw new PMSaveException(msg, MessageIDs.E_OBJECT_IN_USE);
}
/**
* Handles a <code>PersistenceException</code>.
*
* @param mapperList The Parameter Name mapping list.
* @param s The session.
* @param tx The transaction.
* @param e The exception.
* @throws PMException if the rollback of the transaction fails.
* @throws InterruptedException if the cause of the given exception was
* that the operation was canceled.
* @throws PMSaveException wrapper for the Persistence exception.
*/
private static void handleOtherException(List<INameMapper> mapperList,
EntityManager s, EntityTransaction tx, Exception e)
throws PMException, InterruptedException, PMSaveException {
if (tx != null) {
Persistor.instance().rollbackTransaction(s, tx);
}
if (e.getCause() instanceof InterruptedException) {
GeneralStorage.getInstance().reset();
for (INameMapper mapper : mapperList) {
mapper.clearAllNames();
}
// Operation was canceled.
throw new InterruptedException();
}
String msg = Messages.CantAttachProject + StringConstants.DOT;
throw new PMSaveException(msg + e.getMessage(),
MessageIDs.E_ATTACH_PROJECT);
}
/**
* Handles the cancellation of an operation.
*
* @param mapperList The Parameter Name mapping list.
* @param s The session.
* @param tx The transaction.
* @throws PMException if the transaction cannot be rolled back.
* @throws InterruptedException to indicate that the operation was
* successfully canceled.
*/
private static void handleOperationCanceled(List<INameMapper> mapperList,
EntityManager s, EntityTransaction tx)
throws PMException, InterruptedException {
if (tx != null) {
Persistor.instance().rollbackTransaction(s, tx);
}
GeneralStorage.getInstance().reset();
for (INameMapper mapper : mapperList) {
mapper.clearAllNames();
}
// Operation was canceled.
throw new InterruptedException();
}
/**
* @param proj
* the Project
* @throws PMException
* @throws ProjectDeletedException
* @throws PMSaveException
*/
private static void initBPs(IProjectPO proj) throws PMException,
ProjectDeletedException, PMSaveException {
try {
CompNameManager.getInstance().init();
ParamNameBP.getInstance().initMap();
} catch (PMException e) {
throw new PMException(
Messages.ReadingOfProjectNameOrParamNamesFailed
+ StringConstants.COLON + StringConstants.SPACE
+ e.toString(), MessageIDs.E_ATTACH_PROJECT);
}
try {
UsedToolkitBP.getInstance().refreshToolkitInfo(proj);
} catch (PMException e) {
throw new PMSaveException(
Messages.PMExceptionWhileWritingUsedToolkitsInDB
+ StringConstants.COLON + StringConstants.SPACE
+ e.toString(), MessageIDs.E_ATTACH_PROJECT);
}
}
/**
* Sets the progress monitor for Persistence progress listeners/interceptors.
*
* @param monitor The progress monitor to use, or <code>null</code> to clear
* the monitor.
*/
private static void setHbmProgressMonitor(IProgressMonitor monitor) {
ProgressMonitorTracker.SINGLETON.setProgressMonitor(monitor);
}
/**
* Persists the given project to the DB. This is performed in a new session.
* When this method returns, the project will not be attached to any session.
* @param proj ProjectPO to be saved.
* @param newProjectName
* name part of the ProjectNamePO. If there is no new name, this
* parameter must be null (same project, different version)
* @param paramMapper an INameMapper to persist names (Parameter).
* @param compNameCache the Component Name cache to persist
* names (Component).
* @throws PMException in case of any db error
* @throws ProjectDeletedException if project is already deleted
* @throws InterruptedException if the operation is canceled
*/
public static void saveProject(IProjectPO proj, String newProjectName,
INameMapper paramMapper,
IWritableComponentNameCache compNameCache)
throws PMException, ProjectDeletedException,
InterruptedException {
final EntityManager saveSession = Persistor.instance().openSession();
EntityTransaction tx = null;
try {
tx = Persistor.instance().getTransaction(saveSession);
saveSession.persist(proj);
proj.setParentProjectId(proj.getId());
saveSession.flush();
if (newProjectName != null) {
ProjectNameBP.getInstance().setName(
saveSession, proj.getGuid(), newProjectName);
}
ProjectNameBP.getInstance().storeTransientNames(saveSession);
paramMapper.persist(saveSession, proj.getId());
CompNamePM.flushCompNamesImport(saveSession,
proj.getId(), compNameCache);
Persistor.instance().commitTransaction(saveSession, tx);
paramMapper.updateStandardMapperAndCleanup(proj.getId());
compNameCache.updateStandardMapperAndCleanup(proj.getId());
} catch (PersistenceException e) {
if (tx != null) {
Persistor.instance().rollbackTransaction(saveSession, tx);
}
if (e.getCause() instanceof InterruptedException) {
// Operation was canceled.
throw new InterruptedException();
}
String msg = Messages.CantSaveProject + StringConstants.DOT;
throw new PMSaveException(msg + e.getMessage(),
MessageIDs.E_ATTACH_PROJECT);
} finally {
Persistor.instance().dropSession(saveSession);
}
}
/**
* Check if there is a ProjectPO whith the supplied name in the DB.
*
* @param name
* Name to check
* @return wether the name denotes a ProjectPO in the DB
*/
public static synchronized boolean doesProjectNameExist(String name) {
EntityManager session = null;
Long hits = null;
try {
session = Persistor.instance().openSession();
Query q = session.createQuery("select name from ProjectNamePO as name " //$NON-NLS-1$
+ "where name.hbmName = :name"); //$NON-NLS-1$
q.setParameter("name", name); //$NON-NLS-1$
IProjectNamePO namePO = (IProjectNamePO)q.getSingleResult();
if (namePO != null) {
q = session.createQuery("select count(project.id) from ProjectPO project " //$NON-NLS-1$
+ "where project.guid = :guid"); //$NON-NLS-1$
q.setParameter("guid", namePO.getGuid()); //$NON-NLS-1$
hits = (Long)q.getSingleResult();
}
} catch (NoResultException nre) {
return false;
} catch (PersistenceException e) {
log.error(Messages.PersistenceLoadFailed, e);
} finally {
Persistor.instance().dropSessionWithoutLockRelease(session);
}
return (hits != null && hits.intValue() > 0);
}
/**
* Check if there is a ProjectPO with the supplied guid and version in the
* DB.
*
* @param guid GUID to check
* @param projectVersion to check
* @return wether the ProjectPO currently exists in the DB
*/
public static synchronized boolean doesProjectVersionExist(String guid,
ProjectVersion projectVersion) {
return doesProjectVersionExist(guid, projectVersion.getMajorNumber(),
projectVersion.getMinorNumber(),
projectVersion.getMicroNumber(),
projectVersion.getVersionQualifier());
}
/**
* Check if there is a ProjectPO with the supplied guid and version in the
* DB.
*
* @param guid
* GUID to check
* @param majorNumber
* Major version number to check
* @param minorNumber
* Minor version number to check
* @param microNumber
* Micro version number of check
* @param versionQualifier
* Version qualifier of the project to check
* @return wether the ProjectPO currently exists in the DB
*/
public static synchronized boolean doesProjectVersionExist(String guid,
Integer majorNumber, Integer minorNumber, Integer microNumber,
String versionQualifier) {
EntityManager session = null;
Long hits = null;
try {
session = Persistor.instance().openSession();
StringBuilder sb = new StringBuilder(
"select count(project) from ProjectPO as project" //$NON-NLS-1$
+ " inner join project.properties properties where project.guid = :guid"); //$NON-NLS-1$
addCompleteVersionString(sb, majorNumber, minorNumber, microNumber,
versionQualifier);
Query q = session.createQuery(sb.toString());
q.setParameter("guid", guid); //$NON-NLS-1$
attachParameterToVersion(q, majorNumber, minorNumber, microNumber,
versionQualifier);
hits = (Long) q.getSingleResult();
} catch (PersistenceException e) {
log.error(Messages.PersistenceLoadFailed, e);
} finally {
Persistor.instance().dropSessionWithoutLockRelease(session);
}
return (hits != null && hits.intValue() > 0);
}
/**
*
* @param sb the string build with the select statement
* @param majorNumber the major version
* @param minorNumber the minor version
* @param microNumber the micro version
* @param versionQualifier the qualifier version
*/
public static void addCompleteVersionString(StringBuilder sb,
Integer majorNumber, Integer minorNumber, Integer microNumber,
String versionQualifier) {
addVersionString(majorNumber, sb, "majorNumber"); //$NON-NLS-1$
addVersionString(minorNumber, sb, "minorNumber"); //$NON-NLS-1$
addVersionString(microNumber, sb, "microNumber"); //$NON-NLS-1$
addVersionString(versionQualifier, sb, "versionQualifier"); //$NON-NLS-1$
}
/**
*
* @param q the select query
* @param majorNumber the major version
* @param minorNumber the minor version
* @param microNumber the micro version
* @param versionQualifier the qualifier version
*/
public static void attachParameterToVersion(Query q, Integer majorNumber,
Integer minorNumber, Integer microNumber, String versionQualifier) {
if (majorNumber != null) {
q.setParameter("majorNumber", majorNumber); //$NON-NLS-1$
}
if (minorNumber != null) {
q.setParameter("minorNumber", minorNumber); //$NON-NLS-1$
}
if (microNumber != null) {
q.setParameter("microNumber", microNumber); //$NON-NLS-1$
}
if (versionQualifier != null) {
q.setParameter("versionQualifier", versionQualifier); //$NON-NLS-1$
}
}
/**
*
* @param version the version (only checked if it is null)
* @param sb the StringBuilder which should be extended um further options
* @param versionText the text which should be the same as the property
* in the project properties
*/
private static void addVersionString(Object version, StringBuilder sb,
String versionText) {
if (version == null) {
sb.append(" and project.properties." + versionText + " is NULL"); //$NON-NLS-1$ //$NON-NLS-2$
} else {
sb.append(" and project.properties." + versionText + " = :" //$NON-NLS-1$ //$NON-NLS-2$
+ versionText);
}
}
/**
*
* @param guid the guid which should be bind to the select query
* @param majorVersion the major version which should be bind the the query
* @param minorVersion the minor version which should be bind the the query
* @param microVersion the micro version which should be bind the the query
* @param versionQualifier the qualifier version which should be bind the the query
* @param session the session for the id
* @param selectID do we want only the id or the whole project
* @return returns a select project or select project.id
* which is searching for a specific project
*/
private static Query createProjectQuery(String guid, Integer majorVersion,
Integer minorVersion, Integer microVersion,
String versionQualifier, EntityManager session, boolean selectID) {
StringBuilder sb = new StringBuilder("select project"); //$NON-NLS-1$
if (selectID) {
sb.append(".id"); //$NON-NLS-1$
}
sb.append(" from ProjectPO project" //$NON-NLS-1$
+ " inner join fetch project.properties where project.guid = :guid"); //$NON-NLS-1$
addCompleteVersionString(sb, majorVersion, minorVersion,
microVersion, versionQualifier);
Query query = session.createQuery(sb.toString());
query.setParameter("guid", guid); //$NON-NLS-1$
attachParameterToVersion(query, majorVersion, minorVersion,
microVersion, versionQualifier);
return query;
}
/**
* Check if there is a TestSuite whith the supplied name in the DB.
*
* @param name
* Name to check
* @param projectId
* Long
* @return wether the name denotes a ProjectPO in the DB
*/
public static synchronized boolean doesTestSuiteExists(
Long projectId, String name) {
EntityManager session = null;
List hits = null;
try {
session = Persistor.instance().openSession();
Query q = session.createQuery("select node from TestSuitePO as node where node.hbmName = ?1 and node.hbmParentProjectId = ?2"); //$NON-NLS-1$
q.setParameter(1, name);
q.setParameter(2, projectId);
hits = q.getResultList();
} catch (PersistenceException e) {
log.error(Messages.PersistenceLoadFailed, e);
} finally {
Persistor.instance().dropSessionWithoutLockRelease(session);
}
return ((hits != null) && (hits.size() > 0));
}
/**
* Check if there is a TestJob whith the supplied name in the DB.
*
* @param name
* Name to check
* @param projectId
* Long
* @return whether the name denotes a ProjectPO in the DB
*/
public static synchronized boolean doesTestJobExists(
Long projectId, String name) {
EntityManager session = null;
List hits = null;
try {
session = Persistor.instance().openSession();
Query q = session.createQuery("select node from TestJobPO as node where node.hbmName = ?1 and node.hbmParentProjectId = ?2"); //$NON-NLS-1$
q.setParameter(1, name);
q.setParameter(2, projectId);
hits = q.getResultList();
} catch (PersistenceException e) {
log.error(Messages.PersistenceLoadFailed, e);
} finally {
Persistor.instance().dropSessionWithoutLockRelease(session);
}
return ((hits != null) && (hits.size() > 0));
}
/**
* delete a project
*
* @param proj
* project to delete
* @param isActProject
* flag to label the actual project
* @throws PMAlreadyLockedException
* if project is already locked in db
* @throws PMDirtyVersionException
* if project to delete is modified in the meantime
* @throws JBException
* if a session cannot closed
* @throws PMExtProjDeletedException
* if a project (but not the current) was deleted by another
* user
* @throws ProjectDeletedException
* if the current project was deleted by another user
* @throws InterruptedException
* if the operation was canceled
*/
public static void deleteProject(IProjectPO proj, boolean isActProject)
throws PMDirtyVersionException, PMAlreadyLockedException,
PMExtProjDeletedException, ProjectDeletedException, JBException,
InterruptedException {
Validate.notNull(proj, "Project to delete is null"); //$NON-NLS-1$
EntityManager deleteSess = null;
IProjectPO p = null;
final Long projId = proj.getId();
try {
if (isActProject) {
EntityManager s = GeneralStorage.getInstance()
.getMasterSession();
IProjectPO currProj = s.find(NodeMaker.getProjectPOClass(),
projId);
if (currProj == null) {
throw new ProjectDeletedException(
Messages.ProjectWasDeleted,
MessageIDs.E_CURRENT_PROJ_DEL);
}
}
} catch (PersistenceException e) {
handleDBExceptionForMasterSession(proj, e);
}
final Persistor persistor = Persistor.instance();
try {
deleteSess = persistor.openSession();
EntityTransaction tx = persistor.getTransaction(deleteSess);
p = deleteSess.find(NodeMaker.getProjectPOClass(), projId);
if (p == null) {
if (isActProject) {
throw new ProjectDeletedException(
"Current Project was deleted", //$NON-NLS-1$
MessageIDs.E_CURRENT_PROJ_DEL);
}
throw new PMExtProjDeletedException(Messages.ProjectWasDeleted
+ StringConstants.DOT,
MessageIDs.E_DELETED_OBJECT);
}
preloadData(deleteSess, proj);
persistor.lockPO(deleteSess, p);
deleteProjectIndependentDBObjects(deleteSess, p);
// FIXME zeb Workaround for EclipseLink deleting the objects in the
// wrong order. Test Cases that reference Test Data Cubes
// were being deleted *after* the Test Data Cubes
// themselves.
List<ISpecPersistable> specObjList =
new ArrayList<ISpecPersistable>(
p.getSpecObjCont().getSpecObjList());
List<IExecPersistable> execObjList =
new ArrayList<IExecPersistable>(
p.getExecObjCont().getExecObjList());
for (ISpecPersistable po : specObjList) {
p.getSpecObjCont().removeSpecObject(po);
persistor.deletePO(deleteSess, po);
}
for (IExecPersistable po : execObjList) {
p.getExecObjCont().removeExecObject(po);
persistor.deletePO(deleteSess, po);
}
deleteSess.flush();
// FIXME zeb end workaround
persistor.deletePO(deleteSess, p);
CompNamePM.deleteCompNames(deleteSess, projId);
persistor.commitTransaction(deleteSess, tx);
tx = null;
} catch (PersistenceException e) {
handleDBExceptionForAnySession(p, e, deleteSess);
} finally {
persistor.dropSession(deleteSess);
}
ProjectNameBP.getInstance().checkAndDeleteName(proj.getGuid());
}
/**
* delete all database objects which not associated with project
*
* @param s
* session to use for delete operation
* @param p
* project, whose independent objects to be deleted
* @throws PMException
* in case of failed delete operation
* @throws ProjectDeletedException
* in case of already deleted exception
*
*/
private static void deleteProjectIndependentDBObjects(EntityManager s,
IProjectPO p) throws PMException, ProjectDeletedException {
UsedToolkitBP.getInstance().deleteToolkitsFromDB(s, p.getId(), false);
ParamNamePM.deleteParamNames(s, p.getId(), false);
}
/**
*
* @param proj The project for which to find the required work
* amount.
* @return the amount of work required to save the given project to the
* database.
*/
private static int getTotalWorkForSave(IProjectPO proj) {
// (project_node=1)
int totalWork = 1;
// (INodePO=1)
for (IExecPersistable exec
: proj.getExecObjCont().getExecObjList()) {
totalWork += getWorkForNode(exec);
}
for (ISpecPersistable spec
: proj.getSpecObjCont().getSpecObjList()) {
totalWork += getWorkForNode(spec);
}
// 1 for each event type
totalWork *= NUM_HBM_ADD_PROGRESS_EVENT_TYPES;
return totalWork;
}
/**
* Recursively determines the amount of work involved in saving the
* given node to the database.
*
* @param node The node for which to determine the amount of work.
* @return the amount of work required to save the given node to the
* database.
*/
private static int getWorkForNode(INodePO node) {
int work = 1;
if (!(node instanceof IExecTestCasePO)) {
Iterator<INodePO> childIter = node.getNodeListIterator();
while (childIter.hasNext()) {
work += getWorkForNode(childIter.next());
}
}
if (node instanceof ISpecTestCasePO) {
work += ((ISpecTestCasePO)node).getAllEventEventExecTC().size();
}
return work;
}
/**
* @param guid
* The GUID to search.
* @return a String representing the highest version number for this project
* GUID
*/
public static synchronized ProjectVersion findHighestVersionNumber(
String guid) throws JBException {
EntityManager session = null;
try {
session = Persistor.instance().openSession();
Query query = session.createQuery("select project from ProjectPO project" //$NON-NLS-1$
+ " inner join fetch project.properties where project.guid = :guid " //$NON-NLS-1$
+ " order by project.properties.majorNumber desc, project.properties.minorNumber desc," //$NON-NLS-1$
+ " project.properties.microNumber desc, project.properties.versionQualifier desc"); //$NON-NLS-1$
query.setParameter("guid", guid); //$NON-NLS-1$
query.setMaxResults(1);
final List projList = query.getResultList();
if (projList.isEmpty()) {
return null;
}
IProjectPO project = (IProjectPO)projList.get(0);
return new ProjectVersion(project.getMajorProjectVersion(),
project.getMinorProjectVersion(),
project.getMicroProjectVersion(),
project.getProjectVersionQualifier());
} catch (PersistenceException e) {
log.error(Messages.PersistenceLoadFailed, e);
throw new JBException(e.getMessage(),
MessageIDs.E_PERSISTENCE_LOAD_FAILED);
} finally {
Persistor.instance().dropSessionWithoutLockRelease(session);
}
}
/**
* Returns a list of all projects that can be used by a project with the
* given properties. This query is executed in a new session, so the
* objects in the returned list are detached from their session.
* @param guid The GUID of the project that may use projects from the
* returned list.
* @param majorVersionNumber The major version number of the project
* wishing to reuse.
* @param minorVersionNumber The minor version number of the project
* wishing to reuse.
* @param microVersionNumber The micro version number of the project
* wishing to reuse.
* @param versionQualifier The version qualifier of the project
* wishing to reuse.
* @param toolkit The toolkit of the project that may use projects from the
* returned list.
* @param toolkitLevel The toolkit level of the project that may use
* projects from the returned list.
* @return a list of all reusable projects that could be used by a project
* with the given properties.
*/
@SuppressWarnings("unchecked")
public static synchronized List<IProjectPO> findReusableProjects(
String guid, Integer majorVersionNumber, Integer minorVersionNumber,
Integer microVersionNumber, String versionQualifier,
String toolkit, String toolkitLevel)
throws JBException {
EntityManager session = null;
try {
session = Persistor.instance().openSession();
Query query = session.createQuery("select project from ProjectPO project" //$NON-NLS-1$
+ " inner join fetch project.properties where project.properties.isReusable = :isReusable" //$NON-NLS-1$
+ " and project.guid != :guid"); //$NON-NLS-1$
query.setParameter("isReusable", true); //$NON-NLS-1$
query.setParameter("guid", guid); //$NON-NLS-1$
List<IProjectPO> projects = query.getResultList();
Iterator<IProjectPO> iter = projects.iterator();
while (iter.hasNext()) {
IProjectPO project = iter.next();
String reusedToolkit = project.getToolkit();
try {
String reusedToolkitLevel =
ToolkitSupportBP.getToolkitLevel(reusedToolkit);
if (!(reusedToolkit.equals(toolkit)
|| ToolkitUtils.doesToolkitInclude(
toolkit, reusedToolkit)
|| ToolkitUtils.isToolkitMoreConcrete(
toolkitLevel, reusedToolkitLevel))) {
iter.remove();
}
} catch (ToolkitPluginException tpe) {
StringBuilder msg = new StringBuilder();
msg.append(Messages.Project);
msg.append(StringConstants.SPACE);
msg.append(project.getName());
msg.append(StringConstants.SPACE);
msg.append(
Messages.CouldNotBeLoadedAnUnavailableToolkitPlugin);
msg.append(StringConstants.DOT);
// Plugin for toolkit could not be loaded.
log.error(msg.toString());
// Remove the project using the unavailable toolkit
// from the available projects list.
iter.remove();
}
}
// We have a list of reusable projects with compatible toolkits.
// Now we need to remove from the list any projects that would lead
// to circular dependencies.
Set<IProjectPO> checkedProjects = new HashSet<IProjectPO>();
Set<IProjectPO> illegalProjects = new HashSet<IProjectPO>();
IProjectPO givenProject = loadProjectByGuidAndVersion(
guid, majorVersionNumber, minorVersionNumber,
microVersionNumber, versionQualifier);
if (givenProject == null) {
log.debug(Messages.TriedFindProjectsForNonExistantProject);
return new ArrayList<IProjectPO>();
}
// Project can't reuse itself
illegalProjects.add(givenProject);
checkedProjects.add(givenProject);
for (IProjectPO proj : projects) {
findIllegalProjects(proj, checkedProjects,
illegalProjects, null);
}
projects.removeAll(illegalProjects);
return projects;
} catch (PersistenceException e) {
log.error(Messages.PersistenceLoadFailed, e);
throw new JBException(e.getMessage(),
MessageIDs.E_PERSISTENCE_LOAD_FAILED);
} finally {
Persistor.instance().dropSessionWithoutLockRelease(session);
}
}
/**
* Recursively searches for projects that cannot be used by the given
* project.
*
* The current rules are as follows:
* 1. No circular dependencies. This means that a project 'A' can not use
* itself nor any project that uses 'A' (this rule is, of course,
* transitive).
*
* @param projectToCheck The project for which reused/reusable projects are
* being checked.
* @param checkedProjects Projects that have already been examined by this
* method.
* @param illegalProjects Projects marked as not reusable by this method.
* @param projectsToImport Projects that are currently being imported.
* Reused projects will be searched for by first
* looking through this collection, and, if the
* project is not found there, looking in the #
* database.
* May be <code>null</code>.
*/
public static void findIllegalProjects(
IProjectPO projectToCheck,
Set<IProjectPO> checkedProjects,
Set<IProjectPO> illegalProjects,
Set<IProjectPO> projectsToImport) {
if (!checkedProjects.contains(projectToCheck)) {
checkedProjects.add(projectToCheck);
for (IReusedProjectPO reused : projectToCheck.getUsedProjects()) {
try {
String reusedGuid = reused.getProjectGuid();
IProjectPO reusedProject = null;
if (projectsToImport != null) {
for (IProjectPO importedProject : projectsToImport) {
// First, try to find the project among the imported
// projects.
if (reusedGuid.equals(importedProject.getGuid())
&& reused.getProjectVersion().equals(
importedProject.getProjectVersion())) {
reusedProject = importedProject;
break;
}
}
}
if (reusedProject == null) {
// Try to load the project from the db.
reusedProject = loadProjectByGuidAndVersion(
reused.getProjectGuid(),
reused.getMajorNumber(),
reused.getMinorNumber(),
reused.getMicroNumber(),
reused.getVersionQualifier());
}
if (reusedProject != null) {
// recurse if we were able to find the project
findIllegalProjects(reusedProject, checkedProjects,
illegalProjects, projectsToImport);
}
} catch (JBException e) {
// Error occurred while attempting to load the project
illegalProjects.add(projectToCheck);
}
// if the project uses an illegal project, then it is also illegal
for (IProjectPO project : illegalProjects) {
if (project.getGuid().equals(reused.getProjectGuid())
&& project.getMajorProjectVersion().equals(
reused.getMajorNumber())
&& project.getMinorProjectVersion().equals(
reused.getMinorNumber())) {
illegalProjects.add(projectToCheck);
// This break statement prevents a
// ConcurrentModificationException from occurring
// because it stops iteration immediately when
// a project is added to the set.
break;
}
}
}
}
}
/**
* @return The project for the given (database) ID
* @param projectId
* (database) ID of the project to load
* @param session
* The session to use for loading. The returned project will be
* attached to this session. It is the responsibility of the
* caller to close the session.
* @throws JBException
* if the session cannot be loaded.
*/
public static synchronized IProjectPO loadProjectById(
Long projectId, EntityManager session) throws JBException {
if (projectId == null) {
return null;
}
try {
Query query = session.createQuery("select project from ProjectPO project" //$NON-NLS-1$
+ " where project.id = :id"); //$NON-NLS-1$
query.setParameter("id", projectId); //$NON-NLS-1$
try {
return (IProjectPO)query.getSingleResult();
} catch (NoResultException nre) {
return null;
}
} catch (PersistenceException e) {
OperationCanceledException oce = checkForCancel(e);
if (oce != null) {
throw oce;
}
log.error(Messages.PersistenceLoadFailed, e);
throw new JBException(e.getMessage(),
MessageIDs.E_PERSISTENCE_LOAD_FAILED);
}
}
/**
* this methods is also pre-loading most of the project data
* @return The project for the given (database) ID
* @param projectId
* (database) ID of the project to load
* @param session
* The session to use for loading. The returned project will be
* attached to this session. It is the responsibility of the
* caller to close the session.
* @throws JBException
* if the session cannot be loaded.
*/
public static synchronized IProjectPO loadProjectByIdAndPreLoad(
Long projectId, EntityManager session) throws JBException {
IProjectPO project = loadProjectById(projectId, session);
if (project != null) {
preloadData(session, project);
}
return project;
}
/**
* Loads the project a new session and closes the session.
* @return The project for the given (database) ID
* @param projectId
* (database) ID of the project to load
* @throws JBException
* if the session cannot be loaded or closed.
*/
public static IProjectPO loadProjectById(Long projectId)
throws JBException {
EntityManager session = null;
try {
session = Persistor.instance().openSession();
return loadProjectById(projectId, session);
} finally {
Persistor.instance().dropSessionWithoutLockRelease(session);
}
}
/**
* Gets the GUID of the project with the given ID.
* @param projId the ID of the project
* @return the GUID of the project with the given ID or null if
* no project with the given ID was found.
* @throws JBException if the session cannot be loaded or closed.
*/
public static final synchronized String getGuidOfProjectId(Long projId)
throws JBException {
String cachedGuid = guidCache.get(projId);
if (cachedGuid != null) {
return cachedGuid;
}
EntityManager session = null;
String projGuid = null;
final Persistor persistor = Persistor.instance();
try {
session = persistor.openSession();
final Query query =
session.createQuery("select project.guid from ProjectPO project where project.id = :projectID"); //$NON-NLS-1$
query.setParameter("projectID", projId); //$NON-NLS-1$
projGuid = (String)query.getSingleResult();
} catch (NoResultException nre) {
// No result found. Fall through to return null.
} catch (PersistenceException e) {
throw new JBException(e.getMessage(),
MessageIDs.E_PERSISTENCE_LOAD_FAILED);
} finally {
persistor.dropSessionWithoutLockRelease(session);
}
guidCache.put(projId, projGuid);
return projGuid;
}
/**
*
* @param name project name
* @param versionNrs project version numbers
* @return returns project guid, if project is found
* @throws JBException if the session cannot be loaded or closed.
*/
public static synchronized String getGuidByProjectName(String name,
ProjectVersion versionNrs) throws JBException {
EntityManager session = null;
try {
session = Persistor.instance().openSession();
StringBuilder projectGuidQueryBuilder = new StringBuilder("select project.hbmGuid"); //$NON-NLS-1$
projectGuidQueryBuilder.append(" from ProjectNamePO as project"); //$NON-NLS-1$
projectGuidQueryBuilder.append(" where project.hbmName = :name"); //$NON-NLS-1$
Query query = session
.createQuery(projectGuidQueryBuilder.toString());
query.setParameter("name", name); //$NON-NLS-1$
String projectGuid = (String)query.getSingleResult();
if (versionNrs == null) {
return projectGuid;
}
StringBuilder versionedGuidQueryBuilder = new StringBuilder("select project.guid"); //$NON-NLS-1$
versionedGuidQueryBuilder.append(" from ProjectPO project"); //$NON-NLS-1$
versionedGuidQueryBuilder.append(" inner join fetch project.properties where project.guid = :guid"); //$NON-NLS-1$
addCompleteVersionString(versionedGuidQueryBuilder,
versionNrs.getMajorNumber(), versionNrs.getMinorNumber(),
versionNrs.getMicroNumber(),
versionNrs.getVersionQualifier());
final Query versionQuery = session
.createQuery(versionedGuidQueryBuilder.toString());
versionQuery.setParameter("guid", projectGuid); //$NON-NLS-1$
attachParameterToVersion(versionQuery, versionNrs.getMajorNumber(),
versionNrs.getMinorNumber(), versionNrs.getMicroNumber(),
versionNrs.getVersionQualifier());
try {
return (String) versionQuery.getSingleResult();
} catch (NoResultException nre) {
return null;
}
} catch (PersistenceException e) {
log.error(Messages.PersistenceLoadFailed, e);
throw new JBException(e.getMessage(),
MessageIDs.E_PERSISTENCE_LOAD_FAILED);
} finally {
Persistor.instance().dropSessionWithoutLockRelease(session);
}
}
/**
* Checks if a project with a given ID exists in the DB
* @param projectId Object ID of project
* @return true if the project exist, fails if not or in case of errors
*/
public static boolean doesProjectExist(Long projectId) {
EntityManager session = null;
List hits = null;
final Persistor persistor = Persistor.instance();
try {
session = persistor.openSession();
Query q = session.createQuery("select node from ProjectPO as node where node.id = ?1"); //$NON-NLS-1$
q.setParameter(1, projectId);
hits = q.getResultList();
} catch (PersistenceException e) {
log.error(Messages.PersistenceLoadFailed, e);
} finally {
persistor.dropSessionWithoutLockRelease(session);
}
return ((hits != null) && (hits.size() > 0));
}
}