/*******************************************************************************
* 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.businessprocess;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import javax.persistence.EntityManager;
import javax.persistence.EntityTransaction;
import javax.persistence.NoResultException;
import javax.persistence.PersistenceException;
import javax.persistence.Query;
import org.eclipse.jubula.client.core.i18n.Messages;
import org.eclipse.jubula.client.core.model.IProjectNamePO;
import org.eclipse.jubula.client.core.model.PoMaker;
import org.eclipse.jubula.client.core.persistence.PMException;
import org.eclipse.jubula.client.core.persistence.PersistenceManager;
import org.eclipse.jubula.client.core.persistence.Persistor;
import org.eclipse.jubula.client.core.utils.NameValidationUtil;
import org.eclipse.jubula.tools.internal.constants.StringConstants;
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;
/**
* Business processes for the project name <=> GUID mapping information.
*
* @author BREDEX GmbH
* @created Jun 20, 2007
*/
public class ProjectNameBP {
/** standard logging */
private static Logger log = LoggerFactory.getLogger(ProjectNameBP.class);
/**
* The singleton instance.
*/
private static ProjectNameBP instance = null;
/** cache for project names */
private Map<String, String> m_names;
/** cache for work versions of names */
private Map<String, String> m_transientNames;
/**
* private utility constructor
*/
private ProjectNameBP() {
m_names = new HashMap<String, String>();
m_transientNames = new HashMap<String, String>();
}
/**
* Method makes use of the project name cache
* @param guid id for the project name
* @return the name associated with guid or null if no such name exists
*/
public String getName(String guid) {
return getName(guid, true);
}
/**
* @param guid id for the project name
* @param useCache whether to use the project name cache or not
* @return the name associated with guid or null if no such name exists
*/
public String getName(String guid, boolean useCache) {
if (guid == null) {
return null;
}
String res = null;
if (useCache) {
res = m_transientNames.get(guid);
if (res == null && m_names.containsKey(guid)) {
return m_names.get(guid);
}
}
if (res == null) {
try {
res = readProjectNameFromDB(guid);
} catch (PMException e) {
log.warn(e.getLocalizedMessage(), e);
}
}
m_names.put(guid, res);
return res;
}
/**
* @param projectGuid the Project guid
* @throws PMException in cas of DB problem
* @return the string for the GUID freshly read from the DB
*/
private synchronized String readProjectNameFromDB(final String projectGuid)
throws PMException {
if (projectGuid == null) {
return null;
}
final EntityManager session = Persistor.instance().openSession();
try {
final Query q = session.createQuery(
"select projectName from ProjectNamePO as projectName where projectName.hbmGuid = :projectGuid"); //$NON-NLS-1$
q.setParameter("projectGuid", projectGuid); //$NON-NLS-1$
try {
return ((IProjectNamePO)q.getSingleResult()).getName();
} catch (NoResultException nre) {
// No such project name in the database. Fall through to return
// null as per the javadoc.
}
} catch (PersistenceException e) {
PersistenceManager.handleDBExceptionForAnySession(null, e, session);
} finally {
Persistor.instance().dropSessionWithoutLockRelease(session);
}
return null;
}
/**
* @throws PMException in cas of DB problem
* @return mapping between all guids in the DB and their corresponding names
*/
@SuppressWarnings("unchecked")
public synchronized Map<String, String> readAllProjectNamesFromDB()
throws PMException {
final EntityManager session = Persistor.instance().openSession();
try {
final Query q = session.createQuery("select name from ProjectNamePO name"); //$NON-NLS-1$
List<IProjectNamePO> projectNameList = q.getResultList();
Map<String, String> guidToNameMap = new HashMap<String, String>();
for (IProjectNamePO projectName : projectNameList) {
guidToNameMap.put(projectName.getGuid(), projectName.getName());
}
return guidToNameMap;
} catch (PersistenceException e) {
PersistenceManager.handleDBExceptionForAnySession(null, e, session);
} finally {
Persistor.instance().dropSessionWithoutLockRelease(session);
}
return null;
}
/**
* Deletes the current project name from the DB if no other existing project
* is using it.
* @param guid id of the project name
*/
public void checkAndDeleteName(String guid)
throws PMException, ProjectDeletedException {
EntityManager session = null;
try {
session = Persistor.instance().openSession();
final EntityTransaction tx =
Persistor.instance().getTransaction(session);
if (!isGuidBeingUsed(session, guid)) {
deleteName(session, guid);
}
Persistor.instance().commitTransaction(session, tx);
m_names.remove(guid);
m_transientNames.remove(guid);
} catch (PersistenceException he) {
StringBuilder msgbuid = new StringBuilder();
msgbuid.append(Messages.CouldNotDeleteProjectName);
msgbuid.append(StringConstants.LEFT_PARENTHESIS);
msgbuid.append(Messages.ForGuid);
msgbuid.append(guid);
msgbuid.append(StringConstants.RIGHT_PARENTHESIS);
msgbuid.append(Messages.FromTheDatabase);
msgbuid.append(StringConstants.DOT);
log.error(msgbuid.toString(), he);
} finally {
Persistor.instance().dropSession(session);
}
}
/**
* Checks whether any projects exist that use the current name.
* @param session Persistence (JPA / EclipseLink) session context
* @param guid id of the project name
* @return <code>true</code> if at least one project exists with the
* current name. Otherwise <code>false</code>.
*/
private synchronized boolean isGuidBeingUsed(
EntityManager session, String guid) {
Long hits = null;
Query q = session.createQuery(
"select count(project) from ProjectPO as project " //$NON-NLS-1$
+ "where project.guid = :guid"); //$NON-NLS-1$
q.setParameter("guid", guid); //$NON-NLS-1$
hits = (Long)q.getSingleResult();
return (hits != null && hits.intValue() > 0);
}
/**
* Deletes the project name from the DB.
* @param session Persistence (JPA / EclipseLink) session context
* @param guid id of the project name
*/
private synchronized void deleteName(EntityManager session, String guid) {
Query q = session.createQuery("delete from ProjectNamePO name where name.hbmGuid = :guid"); //$NON-NLS-1$
q.setParameter("guid", guid); //$NON-NLS-1$
q.executeUpdate();
}
/**
* Commits a changed Name <=> GUID mapping.
*
* @param guid the guid for which to change the mapped name
* @param newProjectName the new name for all projects with this <code>guid</code>
* @param doPersist shall this name be persisted or just added to the list
* of name waiting to be persisted?
*/
public void setName(String guid, String newProjectName, boolean doPersist) {
if (guid == null || newProjectName == null) {
return;
}
if (doPersist) {
final EntityManager session = Persistor.instance().openSession();
try {
final EntityTransaction tx =
Persistor.instance().getTransaction(session);
setName(session, guid, newProjectName);
Persistor.instance().commitTransaction(session, tx);
} catch (PMException e) {
throw new JBFatalException(Messages.SavingProjectFailed
+ StringConstants.DOT, e,
MessageIDs.E_DATABASE_GENERAL);
} catch (ProjectDeletedException e) {
throw new JBFatalException(Messages.SavingProjectFailed
+ StringConstants.DOT, e,
MessageIDs.E_PROJECT_NOT_FOUND);
} finally {
Persistor.instance().dropSession(session);
}
} else {
setTransientName(guid, newProjectName);
}
}
/**
* @param guid id for name
* @param newProjectName value for name
*/
private void setTransientName(String guid, String newProjectName) {
if (m_names.containsKey(guid)) {
// there is a persistent key for the guid, so just uopate the name
log.debug("setTransientName() " + Messages.CalledForPersistantObject //$NON-NLS-1$
+ StringConstants.DOT);
m_names.put(guid, newProjectName);
} else {
m_transientNames.put(guid, newProjectName);
}
}
/**
* persist all entries in the transient name map
*
* @param s Persistence (JPA / EclipseLink) Session to join
*/
public synchronized void storeTransientNames(EntityManager s) {
Map<String, String> workMap = new HashMap<String, String>(
m_transientNames);
for (Entry<String, String> name : workMap.entrySet()) {
setName(s, name.getKey(), name.getValue());
}
m_transientNames.clear();
}
/**
* Commits a changed Name <=> GUID mapping.
*
* @param session use this specific Persistence (JPA / EclipseLink) session
* @param guid the guid for which to change the mapped name
* @param newProjectName the new name for all projects with this <code>guid</code>
*/
public synchronized void setName(
EntityManager session, String guid, String newProjectName) {
if (guid == null || newProjectName == null) {
return;
}
final Query q = session.createQuery(
"select projectName from ProjectNamePO projectName where projectName.hbmGuid = :projectGuid"); //$NON-NLS-1$
q.setParameter("projectGuid", guid); //$NON-NLS-1$
IProjectNamePO projectName = null;
try {
projectName = (IProjectNamePO)q.getSingleResult();
} catch (NoResultException nre) {
projectName = PoMaker.createProjectNamePO(guid, newProjectName);
session.persist(projectName);
}
projectName.setName(newProjectName);
m_names.put(projectName.getGuid(), projectName.getName());
m_transientNames.remove(guid); // make sure the key is removed
//from the transient map
}
/**
* call if the DB values have changed and it's not clear if the cached names
* are still valid.
*
*/
public void clearCache() {
m_names.clear();
m_transientNames.clear();
}
/**
* @return the singleton instance
*/
public static ProjectNameBP getInstance() {
if (instance == null) {
instance = new ProjectNameBP();
}
return instance;
}
/**
* Checks if a given String is a valid project name. This includes the
* ability to be used as a file name on supported operating systems.
* @param name Name candidate
* @param checkSpaces test for spaces at the beginning or end of name
*
* @return true if the name is considered a valid project name.
*/
public static boolean isValidProjectName(String name, boolean checkSpaces) {
if (checkSpaces) {
if (name.startsWith(StringConstants.SPACE)) {
return false; // no leading spaces
}
if (name.endsWith(StringConstants.SPACE)) {
return false; // no trailing spaces
}
}
return NameValidationUtil.containsNoIllegalChars(name);
}
}