/*
* Speedo: an implementation of JDO compliant personality on top of JORM
* generic I/O sub-system. Copyright (C) 2001-2004 France Telecom R&D
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This library is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; if not, write to the Free Software Foundation,
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Release: 1.0
*
* Created on 1 mars 2004 @author fmillevi@yahoo.com
*
*/
package org.objectweb.speedo.j2eedo.bo;
import java.util.Collection;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.StringTokenizer;
import java.util.Vector;
import javax.jdo.JDOException;
import javax.jdo.JDOFatalException;
import javax.jdo.PersistenceManager;
import javax.jdo.Query;
import org.objectweb.speedo.Alea;
import org.objectweb.speedo.j2eedo.common.PMHolder;
import org.objectweb.speedo.j2eedo.database.Address;
import org.objectweb.speedo.j2eedo.database.DatabaseObjectInterface;
import org.objectweb.speedo.j2eedo.database.Department;
import org.objectweb.speedo.j2eedo.database.Employee;
import org.objectweb.speedo.j2eedo.database.Project;
import org.objectweb.util.monolog.Monolog;
import org.objectweb.util.monolog.api.BasicLevel;
import org.objectweb.util.monolog.api.Logger;
import org.objectweb.util.monolog.api.LoggerFactory;
/**
* This class handle each request on the <b>J2EEDO</b> application.
* The doAction() method performs the current requested action.
* <p>
* It starts and commits a transaction if needed
* </p>
* <p>
* Call the asked action. When the action in the
* {@link DatabaseImpl#actionArray action list}, the 3 local static lists of
* id (department, employee and project) are redefined.
* </p>
*
* @author fmillevi@yahoo.com
*/
public class DatabaseImpl {
/**
* Defines the action <code>PARAMETER_PING</code><b> its value is ("ping")</b>
* <p>
* This action is use just to check if the application can be reached
* </p>
*/
final public static String PARAMETER_PING = "ping";
/**
* Defines the action <code>PARAMETER_NEW_DEPARTMENT</code><b> its value is
* ("newDept")</b>
* <p>
* This action Creates a new department and some new employees (random
* number defined between 10 and 70).
* </p>
*
* @see DepartmentFactory#MIN_EMPLOYEE_PER_DEPARTMENT
* @see DepartmentFactory#MAX_EMPLOYEE_PER_DEPARTMENT
* @see DepartmentFactory#newDepartmentWithEmployees
*/
final public static String PARAMETER_NEW_DEPARTMENT = "newDept";
/**
* Defines the action <code>PARAMETER_NEW_PROJECT</code><b> its value is
* ("newProj")</b>
* <p>
* This action Creates a new project and affect few employees (random
* number defined between 5 and 20).
* </p>
*
* @see ProjectFactory#MIN_MEMBER_PER_PROJECT
* @see ProjectFactory#MAX_MEMBER_PER_PROJECT
* @see ProjectFactory#newProjectWithEmployees
*/
final public static String PARAMETER_NEW_PROJECT = "newProj";
/**
* Defines the action <code>PARAMETER_NEW_EMPLOYEE</code><b> its value is
* ("newEmp")</b>
* <p>
* Selects one of the known departments and creates a new employee.
* </p>
* @see EmployeeFactory#newEmployee
*/
final public static String PARAMETER_NEW_EMPLOYEE = "newEmp";
/**
* Defines the action <code>PARAMETER_SET_BOSS</code><b> its value is
* ("setDeptBoss")</b>
* @see DepartmentFactory#setManagerForADepartment()
*/
final public static String PARAMETER_SET_BOSS = "setDeptBoss";
/**
* Defines the action <code>PARAMETER_REM_EMPLOYEE</code><b> its value is
* ("delEmployee")</b>
* @see EmployeeFactory#deleteEmployee
*/
final public static String PARAMETER_REM_EMPLOYEE = "delEmployee";
/**
* Defines the action <code>PARAMETER_REM_PROJECT</code><b> its value is
* ("delProject")</b>
* @see ProjectFactory#deleteProject
*/
final public static String PARAMETER_REM_PROJECT = "delProject";
/**
* Defines the action <code>PARAMETER_SPLIT_PROJECT</code><b> its value is
* ("splitProject")</b>
* @see ProjectFactory#splitProject
*/
final public static String PARAMETER_SPLIT_PROJECT = "splitProject";
/**
* Defines the action <code>PARAMETER_SPLIT_DEPARTMENT</code><b> its value is
* ("splitDepartment")</b>
* @see DepartmentFactory#splitDepartment
*/
final public static String PARAMETER_SPLIT_DEPARTMENT = "splitDepartment";
/**
* Defines the action <code>PARAMETER_MERGE_DEPARTMENT</code><b> its value is
* ("mergeDept")</b>
* @see DepartmentFactory#mergeDepartment
*/
final public static String PARAMETER_MERGE_DEPARTMENT = "mergeDept";
/**
* Defines the action <code>PARAMETER_INCREASE_SALARY</code><b> its value is
* ("incSalary")</b> for good folsk only...
* @see EmployeeFactory#increaseSalary()
*/
final public static String PARAMETER_INCREASE_SALARY = "incSalary";
/**
* Defines the action <code>PARAMETER_GET_DEPARTMENT</code><b> its value is
* ("getDepartment")</b>
* @see DepartmentFactory#getDepartment()
*/
final public static String PARAMETER_GET_DEPARTMENT = "getDepartment";
/**
* Defines the action <code>PARAMETER_GET_PROJECT</code><b> its value is
* ("getProject")</b>
* @see ProjectFactory#getProject()
*/
final public static String PARAMETER_GET_PROJECT = "getProject";
/**
* Defines the action <code>PARAMETER_GET_EMPLOYEE</code><b> its value is
* ("getEmployee")</b>
* @see EmployeeFactory#getEmployees()
*/
final public static String PARAMETER_GET_EMPLOYEE = "getEmployee";
/**
* Defines the action <code>PARAMETER_QUERY_PROJECTS</code><b> its value is
* ("queryProjects")</b>
* <p>
* Performs one of the four queries:
* <ul>
* <li>Get employee by its id,</li>
* <li>Get employees between min and max,</li>
* <li>Get employees having the same manager,</li>
* <li>Get employees member of a project.</li>
* </ul>
* </p>
* @see EmployeeFactory#getEmployees()
*/
final public static String PARAMETER_QUERY_PROJECTS = "queryProjects";
/**
* Defines the action <code>PARAMETER_QUERY_EMPLOYEES</code><b> its value is
* ("queryEmployees")</b>
* <p>
* Performs one of the two queries:
* <ul>
* <li>Get project by its id,</li>
* <li>Get projects by member.</li>
* </ul>
* </p>
* @see ProjectFactory#getProjects()
*/
final public static String PARAMETER_QUERY_EMPLOYEES = "queryEmployees";
final public static String PARAMETER_EVICTALL = "evictall";
/**
* The String array <code>actionArray</code> gives the liste of actions
* avalaibles
*/
final public static String actionArray[] = {
DatabaseImpl.PARAMETER_NEW_DEPARTMENT,
DatabaseImpl.PARAMETER_NEW_PROJECT,
DatabaseImpl.PARAMETER_NEW_EMPLOYEE,
DatabaseImpl.PARAMETER_SET_BOSS,
DatabaseImpl.PARAMETER_REM_EMPLOYEE,
DatabaseImpl.PARAMETER_REM_PROJECT,
DatabaseImpl.PARAMETER_SPLIT_PROJECT,
DatabaseImpl.PARAMETER_SPLIT_DEPARTMENT,
DatabaseImpl.PARAMETER_MERGE_DEPARTMENT,
DatabaseImpl.PARAMETER_INCREASE_SALARY,
DatabaseImpl.PARAMETER_GET_DEPARTMENT,
DatabaseImpl.PARAMETER_GET_PROJECT,
DatabaseImpl.PARAMETER_GET_EMPLOYEE,
DatabaseImpl.PARAMETER_QUERY_PROJECTS,
DatabaseImpl.PARAMETER_QUERY_EMPLOYEES,
DatabaseImpl.PARAMETER_PING
};
final public static int READ = 1;
final public static int WRITE = 0;
/**
* The double dimension array <code>actionWeightArray</code> gives for
* each action the relative read and write weight
*
* @see #actionArray
*/
final public static int actionWeightArray[][] = { //{write,read}
{ 4, 0 }, //DatabaseImpl.PARAMETER_NEW_DEPARTMENT,
{ 5, 3 }, //DatabaseImpl.PARAMETER_NEW_PROJECT,
{ 1, 1 }, //DatabaseImpl.PARAMETER_NEW_EMPLOYEE,
{ 4, 2 }, //DatabaseImpl.PARAMETER_SET_BOSS,
{ 1, 1 }, //DatabaseImpl.PARAMETER_REM_EMPLOYEE,
{ 4, 2 }, //DatabaseImpl.PARAMETER_REM_PROJECT,
{ 4, 3 }, //DatabaseImpl.PARAMETER_SPLIT_PROJECT,
{ 4, 4 }, //DatabaseImpl.PARAMETER_SPLIT_DEPARTMENT
{ 5, 2 }, //DatabaseImpl.PARAMETER_MERGE_DEPARTMENT,
{ 3, 2 }, //DatabaseImpl.PARAMETER_INCREASE_SALARY,
{ 0, 5 }, //DatabaseImpl.PARAMETER_GET_DEPARTMENT,
{ 0, 3 }, //DatabaseImpl.PARAMETER_GET_PROJECT,
{ 0, 1 }, //DatabaseImpl.PARAMETER_GET_EMPLOYEE,
{ 0, 3 }, //DatabaseImpl.PARAMETER_QUERY_PROJECT,
{ 0, 2 }, //DatabaseImpl.PARAMETER_QUERY_EMPLOYEE
{ 0, 0 } //DatabaseImpl.PARAMETER_PING
};
/**
* The Vector <code>poolOfDepartmentId</code> is a static list of known
* departmentId used to keep in mind the list of department without doing
* any JDO request.
* <p>
* This is a local cache
* </p>
*/
public final static Vector poolOfDepartmentId = new Vector();
/**
* The Vector <code>poolOfProjectId</code> is a static list of known
* projectId used to keep in mind the list of project without doing any JDO
* request.
* <p>
* This is a local cache
* </p>
*/
public final static Vector poolOfProjectId = new Vector();
/**
* The Vector <code>poolOfEmployeeId</code> is a static list of known
* employeeId used to keep in mind the list of employee without doing any
* JDO request.
* <p>
* This is a local cache
* </p>
*/
public final static Vector poolOfEmployeeId = new Vector();
private static boolean resetPools = true;
static Logger logger = Monolog.initialize().getLogger(DatabaseImpl.class.getName());
private static Hashtable needTransactionArray = new Hashtable();;
static {
// define the action needing transaction.
int writeWeight;
for (int i = 0; i < DatabaseImpl.actionWeightArray.length; i++) {
writeWeight = DatabaseImpl.actionWeightArray[i][DatabaseImpl.WRITE];
needTransactionArray.put(
DatabaseImpl.actionArray[i],
Boolean.valueOf(writeWeight != 0));
}
}
private final static int PROJECT_INIT_SIZE = 100;
private final static int DEPARTMENT_INIT_SIZE = 30;
public final static DatabaseImpl instance = new DatabaseImpl();
//private PMHolder persistenceManagerHolder;
private DepartmentFactory departmentFactory = null;
private EmployeeFactory employeeFactory = null;
private ProjectFactory projectFactory = null;
private DatabaseImpl() {
this.departmentFactory = new DepartmentFactory();
this.employeeFactory = new EmployeeFactory();
this.projectFactory = new ProjectFactory();
}
/**
* This method initialize the action to be performed, enables or disables
* transaction management according the parameter withTransaction and calls
* the private doAction() method.
*
* @param parameter
* is the action to be performed
* @param withTransaction
* is a boolean use to enable or disable the use of transaction
* @return threatment result as String
* @throws JDOException
* @throws Exception
*/
public String doAction(String action, boolean performCommit, PMHolder pmHolder) {
if (PARAMETER_PING.equalsIgnoreCase(action)) {
return "Alive...";
}
PersistenceManager pm = pmHolder.getPersistenceManager();
StringBuffer outStr = new StringBuffer();
try {
boolean demarcateJDOTx = (performCommit
|| ((Boolean) needTransactionArray.get(action)).booleanValue())
&& !pm.currentTransaction().isActive();
if (PARAMETER_EVICTALL.equalsIgnoreCase(action)) {
logger.log(BasicLevel.INFO, "Flushing cache ...");
pm.evictAll();
resetPools(pm, outStr);
return "Cache flushed !";
}
initPools(pm);
PollsSynchronizations poolsSync = new PollsSynchronizations();
pm.setUserObject(poolsSync);
pm.currentTransaction().setSynchronization(poolsSync);
if (demarcateJDOTx) {
logger.log(BasicLevel.INFO, "Begin JDO transaction.");
pm.currentTransaction().begin();
}
StringTokenizer st = new StringTokenizer(action.trim(), ", ", false);
while(st.hasMoreTokens()) {
doAction(st.nextToken(), poolsSync, outStr, pm);
}
if (demarcateJDOTx && pm.currentTransaction().isActive()) {
logger.log(BasicLevel.INFO, "Commit JDO transaction.");
pm.currentTransaction().commit();
}
} catch (RuntimeException e) {
logger.log(BasicLevel.WARN, "The action : '" + action
+ "' fails : Action canceled.", e);
if (!pm.isClosed() && pm.currentTransaction().isActive()) {
pm.currentTransaction().rollback();
}
if (!(e instanceof JDOFatalException)) {
outStr.append("Action canceled").append(e.getMessage());
} else {
throw e;
}
} finally {
pmHolder.closePersistenceManager();
}
return outStr.toString();
}
private void doAction(String action,
PollsSynchronizations poolsSync,
StringBuffer outStr,
PersistenceManager pm) {
logger.log(BasicLevel.DEBUG, "do action " + action);
if (PARAMETER_NEW_DEPARTMENT.equalsIgnoreCase(action)) {
departmentFactory.newDepartmentWithEmployees(poolsSync, outStr, pm);
} else if (PARAMETER_NEW_PROJECT.equalsIgnoreCase(action)) {
projectFactory.newProjectWithEmployees(poolsSync, outStr, pm);
} else if (PARAMETER_SET_BOSS.equalsIgnoreCase(action)) {
departmentFactory.setManagerForADepartment(outStr, pm);
} else if (PARAMETER_NEW_EMPLOYEE.equalsIgnoreCase(action)) {
employeeFactory.newEmployee(poolsSync, outStr, pm);
} else if (PARAMETER_REM_EMPLOYEE.equalsIgnoreCase(action)) {
employeeFactory.deleteEmployee(poolsSync, outStr, pm);
} else if (PARAMETER_REM_PROJECT.equalsIgnoreCase(action)) {
projectFactory.deleteProject(poolsSync, outStr, pm);
} else if (PARAMETER_SPLIT_PROJECT.equalsIgnoreCase(action)) {
projectFactory.splitProject(poolsSync, outStr, pm);
} else if (PARAMETER_MERGE_DEPARTMENT.equalsIgnoreCase(action)) {
departmentFactory.mergeDepartment(poolsSync, outStr, pm);
} else if (PARAMETER_SPLIT_DEPARTMENT.equalsIgnoreCase(action)) {
departmentFactory.splitDepartment(poolsSync, outStr, pm);
} else if (PARAMETER_INCREASE_SALARY.equalsIgnoreCase(action)) {
employeeFactory.increaseSalary(outStr, pm);
} else if (PARAMETER_GET_DEPARTMENT.equalsIgnoreCase(action)) {
departmentFactory.getDepartment(outStr, pm);
} else if (PARAMETER_GET_PROJECT.equalsIgnoreCase(action)) {
projectFactory.getProject(outStr, pm);
} else if (PARAMETER_GET_EMPLOYEE.equalsIgnoreCase(action)) {
employeeFactory.getEmployee(outStr, pm);
} else if (PARAMETER_QUERY_PROJECTS.equalsIgnoreCase(action)) {
projectFactory.getProjects(outStr, pm);
} else if (PARAMETER_QUERY_EMPLOYEES.equalsIgnoreCase(action)) {
employeeFactory.getEmployees(outStr, pm);
} else {
resetPools(pm, outStr);
}
logger.log(BasicLevel.DEBUG, "End of action : " + action);
}
private synchronized static void resetPools(
PersistenceManager pm,
StringBuffer outStr) {
logger.log(BasicLevel.DEBUG, "Resets and shows pools");
poolOfDepartmentId.clear();
poolOfEmployeeId.clear();
poolOfProjectId.clear();
resetPools = true;
initPools(pm);
if (outStr != null) {
outStr.append("\nDo nothing and dump static poll contents");
outStr.append("\nDepartments:");
outStr.append(DatabaseImpl.poolOfDepartmentId.toString());
outStr.append("\nEmployees:");
outStr.append(DatabaseImpl.poolOfEmployeeId.toString());
outStr.append("\nProjects:");
outStr.append(DatabaseImpl.poolOfProjectId.toString());
}
}
private static synchronized void initPools(PersistenceManager pm) {
if (!resetPools) {
return;
}
try {
if (poolOfDepartmentId.isEmpty()) {
initPool(poolOfDepartmentId, Department.class, pm);
logger.log(BasicLevel.DEBUG,
"Initialize the static pool of Departments Id : "
+ poolOfDepartmentId);
}
if (poolOfProjectId.isEmpty()) {
initPool(poolOfProjectId, Project.class, pm);
logger.log(BasicLevel.DEBUG,
"Initialize the static pool of Projects Id : "
+ poolOfProjectId);
}
if (poolOfEmployeeId.isEmpty()) {
initPool(poolOfEmployeeId, Employee.class, pm);
logger.log(BasicLevel.DEBUG,
"Initialize the static pool of Employees Id : "
+ poolOfEmployeeId);
}
} finally {
DatabaseImpl.resetPools = false;
}
}
/**
* Initializes a pool of identifier
* @param pool is the pool to fill
* @param c is the persistent class
* @param pm is the persistence manager to use
*/
private static void initPool(Collection pool, Class c, PersistenceManager pm) {
logger.log(BasicLevel.INFO, "(Re)Initialize static pool id for classe : "
+ c.getName());
pm.getObjectIdClass(c);
boolean hasMoreResult = true;
int idx = 0;
final int page_size = 1000;
boolean hasTx = pm.currentTransaction().isActive();
if (hasTx) {
pm.currentTransaction().commit();
}
while(hasMoreResult) {
pm.currentTransaction().begin();
Query query = pm.newQuery(c);
query.setRange(idx, idx + page_size);
int nb = 0;
try {
Collection col = (Collection) query.execute();
for(Iterator it = col.iterator(); it.hasNext();) {
nb ++;
pool.add(new Long(((DatabaseObjectInterface) it.next()).getId()));
}
} finally {
query.closeAll();
}
idx += page_size;
hasMoreResult = nb > 0;
pm.currentTransaction().commit();
}
}
/**
* Gets an element from the pool.
*/
private final static long getIdFromPool(Vector pool) {
// Get an id from the pool.
while (DatabaseImpl.resetPools) {
try {
logger.log(
BasicLevel.DEBUG,
"sleep until the end off static pool id reset...");
Thread.sleep(10);
} catch (InterruptedException e) {
}
}
int alea = Alea.rand(0, Math.max(0, pool.size() -1));
synchronized(pool) {
return ((Long) pool.get(alea)).longValue();
}
}
public static void initTestData(PMHolder pmHolder)
throws JDOException, Exception {
String str;
PersistenceManager pm = pmHolder.getPersistenceManager();
pm.evictAll();
Iterator objIter = null;
DatabaseImpl.poolOfDepartmentId.clear();
DatabaseImpl.poolOfEmployeeId.clear();
DatabaseImpl.poolOfProjectId.clear();
// remove all employee
Class[] classes = new Class[] {
Employee.class,
Address.class,
Project.class,
Department.class
};
pm.currentTransaction().begin();
for (int i = 0; i < classes.length; i++) {
logger.log(BasicLevel.DEBUG, "Removing " + classes[i].getName() + " ...");
pm.deletePersistentAll((Collection) pm.newQuery(classes[i]).execute());
logger.log(BasicLevel.INFO, "All " + classes[i].getName() + " have been removed.");
}
pm.currentTransaction().commit();
pm.evictAll();
pm.currentTransaction().begin();
logger.log(BasicLevel.INFO, "Init departments and employees data.");
for (int i = 0; i < DEPARTMENT_INIT_SIZE; i++) {
str = DatabaseImpl.instance.doAction(
PARAMETER_NEW_DEPARTMENT, false, pmHolder);
logger.log(BasicLevel.DEBUG, str);
}
DatabaseImpl.resetPools = true;
logger.log(BasicLevel.INFO, "Init projects data.");
for (int i = 0; i < PROJECT_INIT_SIZE; i++) {
str = DatabaseImpl.instance.doAction(
PARAMETER_NEW_PROJECT, false, pmHolder);
logger.log(BasicLevel.DEBUG, str);
}
DatabaseImpl.resetPools = true;
pm.currentTransaction().commit();
logger.log(BasicLevel.INFO, "Initial data set.");
resetPools(pm, null);
pm.evictAll();
logger.log(BasicLevel.DEBUG, "Remove all cache entries.");
pmHolder.closePersistenceManager();
logger.log(BasicLevel.DEBUG, "Close the persistenceManager.");
}
/**
* Returns one of the existing department id
*
* @return a department id
*/
public static long getDepartmentIdFromPool() {
return DatabaseImpl.getIdFromPool(DatabaseImpl.poolOfDepartmentId);
}
/**
* Returns one of the existing employee id
*
* @return a employee id
*/
public static long getEmployeeIdFromPool() {
return DatabaseImpl.getIdFromPool(DatabaseImpl.poolOfEmployeeId);
}
/**
* Returns one of the existing project id
*
* @return a project id
*/
public static long getProjectIdFromPool() {
return DatabaseImpl.getIdFromPool(DatabaseImpl.poolOfProjectId);
}
}