/***************************************************
*
* cismet GmbH, Saarbruecken, Germany
*
* ... and it just works.
*
****************************************************/
package Sirius.server.localserver.object;
import Sirius.server.AbstractShutdownable;
import Sirius.server.ServerExitError;
import Sirius.server.Shutdown;
import Sirius.server.localserver.DBServer;
import Sirius.server.localserver.attribute.MemberAttributeInfo;
import Sirius.server.localserver.attribute.ObjectAttribute;
import Sirius.server.middleware.types.MetaClass;
import Sirius.server.middleware.types.MetaObject;
import Sirius.server.newuser.User;
import Sirius.server.newuser.UserGroup;
import Sirius.server.sql.DBConnection;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import com.mchange.v2.c3p0.DataSources;
import com.vividsolutions.jts.geom.Geometry;
import org.apache.log4j.Logger;
import org.openide.util.Lookup;
import org.postgis.PGgeometry;
import java.beans.PropertyVetoException;
import java.sql.ParameterMetaData;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Types;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import de.cismet.cids.trigger.CidsTrigger;
import de.cismet.cids.trigger.CidsTriggerKey;
import de.cismet.cids.trigger.DBAwareCidsTrigger;
import de.cismet.cismap.commons.jtsgeometryfactories.PostGisGeometryFactory;
import de.cismet.tools.CurrentStackTrace;
/**
* DOCUMENT ME!
*
* @author sascha.schlobinski@cismet.de
* @author thorsten.hell@cismet.de
* @author martin.scholl@cismet.de
* @version $Revision$, $Date$
*/
public final class PersistenceManager extends Shutdown {
//~ Static fields/initializers ---------------------------------------------
private static final transient Logger LOG = Logger.getLogger(PersistenceManager.class);
public static final String NULL = "NULL"; // NOI18N
//~ Instance fields --------------------------------------------------------
private final transient DBServer dbServer;
private final transient PersistenceHelper persistenceHelper;
private final Collection<? extends CidsTrigger> allTriggers;
private final Collection<CidsTrigger> generalTriggers = new ArrayList<CidsTrigger>();
private final Collection<CidsTrigger> crossDomainTrigger = new ArrayList<CidsTrigger>();
private final HashMap<CidsTriggerKey, Collection<CidsTrigger>> triggers =
new HashMap<CidsTriggerKey, Collection<CidsTrigger>>();
private final transient ThreadLocal<TransactionHelper> local;
private final transient ComboPooledDataSource pool;
//~ Constructors -----------------------------------------------------------
/**
* Creates a new PersistenceManager object.
*
* @param dbServer DOCUMENT ME!
*
* @throws IllegalStateException DOCUMENT ME!
*/
public PersistenceManager(final DBServer dbServer) {
this.dbServer = dbServer;
persistenceHelper = new PersistenceHelper(dbServer);
final Lookup.Result<CidsTrigger> result = Lookup.getDefault().lookupResult(CidsTrigger.class);
allTriggers = result.allInstances();
for (final CidsTrigger t : allTriggers) {
if (t instanceof DBAwareCidsTrigger) {
((DBAwareCidsTrigger)t).setDbServer(dbServer);
}
if (triggers.containsKey(t.getTriggerKey())) {
final Collection<CidsTrigger> c = triggers.get(t.getTriggerKey());
assert (c != null);
c.add(t);
} else {
final Collection<CidsTrigger> c = new ArrayList<CidsTrigger>();
c.add(t);
triggers.put(t.getTriggerKey(), c);
}
}
try {
pool = new ComboPooledDataSource();
pool.setDriverClass(dbServer.getSystemProperties().getJDBCDriver());
pool.setJdbcUrl(dbServer.getSystemProperties().getDbConnectionString());
pool.setUser(dbServer.getSystemProperties().getDbUser());
pool.setPassword(dbServer.getSystemProperties().getDbPassword());
pool.setMinPoolSize(5);
pool.setAcquireIncrement(5);
pool.setMaxPoolSize(dbServer.getSystemProperties().getPoolSize());
} catch (PropertyVetoException ex) {
throw new IllegalStateException("pool could not be initialized", ex);
}
local = new ThreadLocal<TransactionHelper>() {
@Override
protected TransactionHelper initialValue() {
try {
return new TransactionHelper(pool.getConnection());
} catch (final SQLException ex) {
throw new IllegalStateException("transactionhelper could not be created", ex);
}
}
};
addShutdown(new AbstractShutdownable() {
@Override
protected void internalShutdown() throws ServerExitError {
try {
DataSources.destroy(pool);
} catch (final SQLException ex) {
throw new ServerExitError("cannot bring down c3p0 pool cleanly", ex); // NOI18N
}
}
});
}
//~ Methods ----------------------------------------------------------------
/**
* DOCUMENT ME!
*
* @param user DOCUMENT ME!
* @param mo DOCUMENT ME!
*
* @return DOCUMENT ME!
*
* @throws PersistenceException DOCUMENT ME!
*/
public int insertMetaObject(final User user, final MetaObject mo) throws PersistenceException {
try {
final Collection<CidsTrigger> rightTriggers = getRightTriggers(mo);
final TransactionHelper transactionHelper = local.get();
transactionHelper.beginWork();
final int rtn = insertMetaObjectWithoutTransaction(user, mo);
transactionHelper.commit();
for (final CidsTrigger ct : rightTriggers) {
ct.afterCommittedInsert(mo.getBean(), user);
}
return rtn;
} catch (final Exception e) {
final String message = "cannot insert metaobject"; // NOI18N
LOG.error(message, e);
rollback();
throw new PersistenceException(message, e);
} finally {
local.get().close();
local.remove();
}
}
/**
* DOCUMENT ME!
*
* @param user DOCUMENT ME!
* @param mo DOCUMENT ME!
*
* @throws PersistenceException DOCUMENT ME!
* @throws SQLException DOCUMENT ME!
*/
public void updateMetaObject(final User user, final MetaObject mo) throws PersistenceException, SQLException {
try {
final Collection<CidsTrigger> rightTriggers = getRightTriggers(mo);
final TransactionHelper transactionHelper = local.get();
transactionHelper.beginWork();
updateMetaObjectWithoutTransaction(user, mo);
transactionHelper.commit();
for (final CidsTrigger ct : rightTriggers) {
ct.afterCommittedInsert(mo.getBean(), user);
}
} catch (final Exception e) {
final String message = "cannot update metaobject"; // NOI18N
LOG.error(message, e);
rollback();
throw new PersistenceException(message, e);
} finally {
local.get().close();
local.remove();
}
}
/**
* DOCUMENT ME!
*
* @param user DOCUMENT ME!
* @param mo DOCUMENT ME!
*
* @return DOCUMENT ME!
*
* @throws PersistenceException DOCUMENT ME!
*/
public int deleteMetaObject(final User user, final MetaObject mo) throws PersistenceException {
try {
final Collection<CidsTrigger> rightTriggers = getRightTriggers(mo);
final TransactionHelper transactionHelper = local.get();
transactionHelper.beginWork();
final int rtn = deleteMetaObjectWithoutTransaction(user, mo);
transactionHelper.commit();
for (final CidsTrigger ct : rightTriggers) {
ct.afterCommittedInsert(mo.getBean(), user);
}
return rtn;
} catch (final Exception e) {
final String message = "cannot delete metaobject"; // NOI18N
LOG.error(message, e);
rollback();
throw new PersistenceException(message, e);
} finally {
local.get().close();
local.remove();
}
}
/**
* DOCUMENT ME!
*
* @throws PersistenceException DOCUMENT ME!
*/
private void rollback() throws PersistenceException {
try {
final TransactionHelper transactionHelper = local.get();
transactionHelper.rollback();
} catch (final SQLException ex) {
final String error = "cannot rollback transaction, this can cause inconsistent database state"; // NOI18N
LOG.error(error, ex);
throw new PersistenceException(error, ex);
}
}
/**
* loescht mo und alle Objekte die mo als Attribute hat.
*
* @param user DOCUMENT ME!
* @param mo DOCUMENT ME!
*
* @return DOCUMENT ME!
*
* @throws PersistenceException Throwable DOCUMENT ME!
* @throws SQLException DOCUMENT ME!
* @throws SecurityException DOCUMENT ME!
*/
private int deleteMetaObjectWithoutTransaction(final User user, final MetaObject mo) throws PersistenceException,
SQLException {
fixMissingMetaClass(mo);
if (LOG.isDebugEnabled()) {
LOG.debug(
"deleteMetaObject entered " // NOI18N
+ mo
+ "status :" // NOI18N
+ mo.getStatus()
+ " of class:" // NOI18N
+ mo.getClassID()
+ " isDummy(ArrayContainer) :" // NOI18N
+ mo.isDummy());
}
if (
dbServer.getClassCache().getClass(mo.getClassID()).getPermissions().hasWritePermission(
user.getUserGroup())
&& (mo.isDummy() || mo.getBean().hasObjectWritePermission(user))) { // wenn mo ein dummy ist dann
final Collection<CidsTrigger> rightTriggers = getRightTriggers(mo);
for (final CidsTrigger ct : rightTriggers) {
ct.beforeDelete(mo.getBean(), user);
}
PreparedStatement stmt = null;
try {
// Mo was created artificially (array holder) so there is no object to delete
// directly proceed to subObjects
if (mo == null) {
LOG.error("cannot delete MetaObject == null"); // NOI18N
return 0;
}
final ObjectAttribute[] allAttributes = mo.getAttribs();
boolean deeper = false;
for (final ObjectAttribute oa : allAttributes) {
if (oa.isChanged()) {
deeper = true;
break;
}
}
if (deeper) {
updateMetaObjectWithoutTransaction(user, mo);
}
// intitialize UserGroup
UserGroup ug = null;
// retrieve userGroup is user is not null
if (user != null) {
ug = user.getUserGroup();
}
// retrieve the metaObject's class
final Sirius.server.localserver._class.Class c = dbServer.getClass(ug, mo.getClassID());
// get Tablename from class
final String tableName = c.getTableName();
// get primary Key from class
final String pk = c.getPrimaryKey();
// add tablename and whereclause to the delete statement
final String paramStmt = "DELETE FROM " + tableName + " WHERE " + pk + " = ?"; // NOI18N+
if (LOG.isDebugEnabled()) {
final StringBuilder logMessage = new StringBuilder("Parameterized SQL: ");
logMessage.append(paramStmt);
logMessage.append('\n');
logMessage.append("Primary key: ");
logMessage.append(String.valueOf(mo.getPrimaryKey().getValue()));
LOG.debug(logMessage.toString());
}
final TransactionHelper transactionHelper = local.get();
stmt = transactionHelper.getConnection().prepareStatement(paramStmt);
stmt.setObject(1, mo.getPrimaryKey().getValue());
// execute deletion and retrieve number of affected objects
int result = stmt.executeUpdate();
// now delete all array entries
final Collection<MetaObject> arrays = new ArrayList<MetaObject>();
for (final ObjectAttribute oa : allAttributes) {
final java.lang.Object value = oa.getValue();
if ((value instanceof MetaObject) && oa.isArray()) {
final MetaObject arrayMo = (MetaObject)value;
arrays.add(arrayMo);
result += deleteArrayEntriesWithoutTransaction(user, arrayMo);
}
}
// if the metaobject is deleted it is obviously not persistent anymore
mo.setPersistent(false);
for (final CidsTrigger ct : rightTriggers) {
ct.afterDelete(mo.getBean(), user);
}
return result;
} finally {
DBConnection.closeStatements(stmt);
}
} else {
if (LOG.isDebugEnabled()) {
LOG.debug(
"'" // NOI18N
+ user
+ "' is not allowed to delete MO " // NOI18N
+ mo.getID()
+ "." // NOI18N
+ mo.getClassKey(),
new CurrentStackTrace());
}
// TODO: shouldn't that return -1 or similar to indicate that nothing has been done?
throw new SecurityException("not allowed to delete meta object"); // NOI18N
}
}
/**
* Deletes all link-entries of the array dummy-object.
*
* @param user DOCUMENT ME!
* @param arrayMo DOCUMENT ME!
*
* @return DOCUMENT ME!
*
* @throws SQLException DOCUMENT ME!
*/
private int deleteArrayEntriesWithoutTransaction(final User user, final MetaObject arrayMo) throws SQLException {
fixMissingMetaClass(arrayMo);
if (!arrayMo.isDummy()) {
LOG.error("deleteArrayEntries on a metaobject that is not a dummy");
}
// initialize number of affected objects
PreparedStatement stmt = null;
try {
// intitialize UserGroup
UserGroup ug = null;
// retrieve userGroup is user is not null
if (user != null) {
ug = user.getUserGroup();
}
final Sirius.server.localserver._class.Class c = dbServer.getClass(ug, arrayMo.getClassID());
final String tableName = arrayMo.getMetaClass().getTableName();
final String arrayKeyFieldName = arrayMo.getReferencingObjectAttribute().getMai().getArrayKeyFieldName();
final String paramStmt = "DELETE FROM " + tableName + " WHERE " + arrayKeyFieldName + " = ?"; // NOI18N+
if (LOG.isDebugEnabled()) {
final StringBuilder logMessage = new StringBuilder("Parameterized SQL: ");
logMessage.append(paramStmt);
logMessage.append('\n');
logMessage.append("Primary key: ");
logMessage.append(String.valueOf(arrayMo.getId()));
LOG.debug(logMessage.toString());
}
final TransactionHelper transactionHelper = local.get();
stmt = transactionHelper.getConnection().prepareStatement(paramStmt);
stmt.setObject(1, arrayMo.getId());
// execute deletion and retrieve number of affected objects
final int result = stmt.executeUpdate();
if (LOG.isDebugEnabled()) {
LOG.debug("array elements deleted :: " + result); // NOI18N
}
return result;
} finally {
DBConnection.closeStatements(stmt);
}
}
/**
* Given metaobject and subobjects will be updated if changed.
*
* @param user DOCUMENT ME!
* @param mo DOCUMENT ME!
*
* @throws PersistenceException Throwable DOCUMENT ME!
* @throws SQLException DOCUMENT ME!
* @throws IllegalStateException Exception DOCUMENT ME!
* @throws SecurityException DOCUMENT ME!
*/
private void updateMetaObjectWithoutTransaction(final User user, final MetaObject mo) throws PersistenceException,
SQLException {
fixMissingMetaClass(mo);
if (LOG.isDebugEnabled()) {
LOG.debug(
"updateMetaObject entered " // NOI18N
+ mo
+ "status :" // NOI18N
+ mo.getStatus()
+ " of class:" // NOI18N
+ mo.getClassID()
+ " isDummy(ArrayContainer) :" // NOI18N
+ mo.isDummy()); // NOI18N
}
if (
dbServer.getClassCache().getClass(mo.getClassID()).getPermissions().hasWritePermission(
user.getUserGroup())
&& (mo.isDummy() || mo.getBean().hasObjectWritePermission(user))) { // wenn mo ein dummy ist dann
// existiert gar keine sinnvolle
// bean
// if Array
if (mo.isDummy()) {
updateArrayObjectsWithoutTransaction(user, mo);
return;
}
final Collection<CidsTrigger> rightTriggers = getRightTriggers(mo);
for (final CidsTrigger ct : rightTriggers) {
ct.beforeUpdate(mo.getBean(), user);
}
// variables for sql statement
final StringBuffer paramStmt = new StringBuffer("UPDATE "); // NOI18N
String sep = ""; // NOI18N
// retrieve class object
final MetaClass metaClass = dbServer.getClass(mo.getClassID());
// add table name and set clause
paramStmt.append(metaClass.getTableName()).append(" SET "); // NOI18N
// retrieve object attributes
final ObjectAttribute[] mAttr = mo.getAttribs();
MemberAttributeInfo mai;
// counts fields to update, if 0 no update will be done at all
int updateCounter = 0;
final ArrayList values = new ArrayList(mAttr.length);
// iterate over all attributes
for (int i = 0; i < mAttr.length; ++i) {
// if it is not changed, skip and proceed
if (!mAttr[i].isChanged()) {
continue;
}
mai = mAttr[i].getMai();
if (mai == null) {
throw new IllegalStateException("MAI not found: " + mAttr[i].getName()); // NOI18N
}
// fieldname is now known, find value now
final java.lang.Object value = mAttr[i].getValue();
if (value == null) {
// delete MetaObject???
values.add(NULL);
if (LOG.isDebugEnabled()) {
LOG.debug("valueString set to '" + NULL + "' as value of attribute was null"); // NOI18N
}
} else if (value instanceof MetaObject) {
final MetaObject subObject = (MetaObject)value;
// CUD for the subobject
switch (subObject.getStatus()) {
case MetaObject.NEW: {
// set new key
final int key = insertMetaObjectWithoutTransaction(user, subObject);
if (subObject.isDummy()) {
values.add(mo.getID()); // set value to primary key
insertMetaObjectArrayWithoutTransaction(user, subObject);
} else {
values.add(key);
}
break;
}
case MetaObject.TO_DELETE: {
deleteMetaObjectWithoutTransaction(user, subObject);
values.add(NULL);
break;
}
case MetaObject.NO_STATUS:
// fall through because we define no status as modified status
case MetaObject.MODIFIED: {
updateMetaObjectWithoutTransaction(user, subObject);
values.add(subObject.getID());
break;
}
default: {
// should never occur
// TODO: consider to LOG fatal!
LOG.error(
"error updating subobject '" // NOI18N
+ subObject
+ "' of attribute " // NOI18N
+ mai.getFieldName()
+ ": invalid status: " // NOI18N
+ subObject.getStatus());
// TODO: throw illegalstateexception ?
}
}
} else {
// TODO: try to convert JTS GEOMETRY to PGgeometry directly
if (PersistenceHelper.GEOMETRY.isAssignableFrom(value.getClass())) {
values.add(PostGisGeometryFactory.getPostGisCompliantDbString((Geometry)value));
} else {
values.add(value);
}
}
// add update fieldname = ? and add value to valuelist
paramStmt.append(sep).append(mai.getFieldName()).append(" = ?"); // NOI18N
++updateCounter;
// comma between 'fieldname = ?, ' set in first iteration
sep = ","; // NOI18N
}
if (updateCounter > 0) {
PreparedStatement stmt = null;
try {
// statment done, just append the where clause using the object's primary key
paramStmt.append(" WHERE ").append(metaClass.getPrimaryKey()).append(" = ?"); // NOI18N
values.add(Integer.valueOf(mo.getID()));
if (LOG.isDebugEnabled()) {
final StringBuilder logMessage = new StringBuilder("Parameterized SQL: ");
logMessage.append(paramStmt);
logMessage.append('\n');
final int i = 1;
for (final java.lang.Object value : values) {
if (i > 1) {
logMessage.append("; ");
}
logMessage.append(i);
logMessage.append(". parameter: ");
logMessage.append(value.toString());
}
LOG.debug(logMessage.toString());
}
final TransactionHelper transactionHelper = local.get();
stmt = transactionHelper.getConnection().prepareStatement(paramStmt.toString());
parameteriseStatement(stmt, values);
stmt.executeUpdate();
/*
* since the meta-jdbc driver is obsolete the index must be refreshed by the server explicitly
*/
for (final CidsTrigger ct : rightTriggers) {
ct.afterUpdate(mo.getBean(), user);
}
} finally {
DBConnection.closeStatements(stmt);
}
}
} else {
if (LOG.isDebugEnabled()) {
LOG.debug(
"'" // NOI18N
+ user
+ "' is not allowed to update MetaObject " // NOI18N
+ mo.getID()
+ "." // NOI18N
+ mo.getClassKey(),
new CurrentStackTrace());
}
throw new SecurityException("not allowed to insert meta object"); // NOI18N
}
}
/**
* DOCUMENT ME!
*
* @param stmt DOCUMENT ME!
* @param values DOCUMENT ME!
*
* @return DOCUMENT ME!
*
* @throws SQLException DOCUMENT ME!
*/
private PreparedStatement parameteriseStatement(final PreparedStatement stmt, final List values)
throws SQLException {
final ParameterMetaData metaData = stmt.getParameterMetaData();
for (int i = 0; i < values.size(); ++i) {
final int type = metaData.getParameterType(i + 1);
if (NULL.equals(values.get(i))) {
stmt.setNull(i + 1, type);
} else if (type == Types.OTHER) {
// assume PGgeometry as String
stmt.setObject(i + 1, new PGgeometry(String.valueOf(values.get(i))));
} else {
stmt.setObject(i + 1, values.get(i), type);
}
}
return stmt;
}
/**
* Processes all array elements.
*
* @param user DOCUMENT ME!
* @param mo DOCUMENT ME!
*
* @throws PersistenceException Throwable DOCUMENT ME!
* @throws SQLException DOCUMENT ME!
*/
private void updateArrayObjectsWithoutTransaction(final User user, final MetaObject mo) throws PersistenceException,
SQLException {
fixMissingMetaClass(mo);
if (LOG.isDebugEnabled()) {
LOG.debug("updateArrayObjects called for: " + mo); // NOI18N
}
final ObjectAttribute[] oas = mo.getAttribs();
for (int i = 0; i < oas.length; i++) {
if (oas[i].referencesObject()) {
final MetaObject metaObject = (MetaObject)oas[i].getValue();
final int status = metaObject.getStatus();
switch (status) {
case MetaObject.NEW: {
// arraykey need not to be process
insertMetaObjectWithoutTransaction(user, metaObject);
break;
}
case MetaObject.TO_DELETE: {
deleteMetaObjectWithoutTransaction(user, metaObject);
break;
}
case MetaObject.NO_STATUS:
case MetaObject.MODIFIED: {
updateMetaObjectWithoutTransaction(user, metaObject);
break;
}
default: {
// should never occur
// TODO: consider LOG fatal
LOG.error(
"error for array element "
+ metaObject
+ " has invalid status ::"
+ status); // NOI18N
// TODO: throw illegalstateexception?
}
}
} else {
LOG.warn("ArrayElement is no MetaObject and won't be inserted"); // NOI18N
}
}
// key references for array are set by client
// TODO: why does the client set them?
}
/**
* DOCUMENT ME!
*
* @param user DOCUMENT ME!
* @param dummy DOCUMENT ME!
*
* @throws PersistenceException Throwable DOCUMENT ME!
* @throws SQLException DOCUMENT ME!
*/
private void insertMetaObjectArrayWithoutTransaction(final User user, final MetaObject dummy)
throws PersistenceException, SQLException {
final ObjectAttribute[] oas = dummy.getAttribs();
for (int i = 0; i < oas.length; i++) {
if (LOG.isDebugEnabled()) {
LOG.debug("insertMO arrayelement " + i); // NOI18N
}
final MetaObject arrayElement = (MetaObject)oas[i].getValue();
final int status = arrayElement.getStatus();
// entscheide bei MO ob update/delete/insert
switch (status) {
case MetaObject.NEW: {
// neuer schluessel wird gesetzt
insertMetaObjectWithoutTransaction(user, arrayElement);
break; // war auskommentiert HELL
}
case MetaObject.TO_DELETE: {
deleteMetaObjectWithoutTransaction(user, arrayElement);
break;
}
case MetaObject.NO_STATUS: {
break;
}
case MetaObject.MODIFIED: {
updateMetaObjectWithoutTransaction(user, arrayElement);
break;
}
default: {
break;
}
}
// this causes no problem as it is never on the top level (-1 != object_id:-)
// die notwendigen schl\u00FCsselbeziehungen werden im client gesetzt???
// TODO: is that the case? if so, consider refactoring
}
}
/**
* DOCUMENT ME!
*
* @param user DOCUMENT ME!
* @param mo DOCUMENT ME!
*
* @return DOCUMENT ME!
*
* @throws PersistenceException Throwable DOCUMENT ME!
* @throws SQLException DOCUMENT ME!
*/
private int insertMetaObjectWithoutTransaction(final User user, final MetaObject mo) throws PersistenceException,
SQLException {
fixMissingMetaClass(mo);
if (LOG.isDebugEnabled()) {
LOG.debug(
"insertMetaObject entered " // NOI18N
+ mo
+ "status :" // NOI18N
+ mo.getStatus()
+ " of class:" // NOI18N
+ mo.getClassID()
+ " isDummy(ArrayContainer) :" // NOI18N
+ mo.isDummy()); // NOI18N
}
if (
dbServer.getClassCache().getClass(mo.getClassID()).getPermissions().hasWritePermission(
user.getUserGroup())
&& (mo.isDummy() || mo.getBean().hasObjectWritePermission(user))) { // wenn mo ein dummy ist dann
// existiert gar keine sinnvolle
// bean won't insert history
// here since we assume that the
// object to be inserted is new
final Collection<CidsTrigger> rightTriggers = getRightTriggers(mo);
for (final CidsTrigger ct : rightTriggers) {
ct.beforeInsert(mo.getBean(), user);
}
final StringBuffer paramSql = new StringBuffer("INSERT INTO "); // NOI18N
// class of the new object
final MetaClass metaClass = dbServer.getClass(mo.getClassID());
paramSql.append(metaClass.getTableName()).append(" ("); // NOI18N
// retrieve new ID to be used as primarykey for the new object
final int rootPk;
try {
rootPk = persistenceHelper.getNextID(metaClass.getTableName(), metaClass.getPrimaryKey());
} catch (final SQLException ex) {
final String message = "cannot fetch next id for metaclass: " + metaClass; // NOI18N
LOG.error(message, ex);
throw new PersistenceException(message, ex);
}
final ObjectAttribute[] mAttr = mo.getAttribs();
// set the new primary key as value of the primary key attribute
for (final ObjectAttribute maybePK : mAttr) {
if (maybePK.isPrimaryKey()) {
maybePK.setValue(rootPk);
}
}
// set object's id
mo.setID(rootPk);
// initialis all array attributes with the value of the primary key
mo.setArrayKey2PrimaryKey();
final ArrayList values = new ArrayList(mAttr.length);
String sep = ""; // NOI18N
// iterate all attributes to create insert statement
for (int i = 0; i < mAttr.length; i++) {
// attribute value
final java.lang.Object value = mAttr[i].getValue();
if (LOG.isDebugEnabled()) {
LOG.debug(
"mAttr[" // NOI18N
+ i
+ "].getName() of " // NOI18N
+ mo.getClassKey()
+ ": " // NOI18N
+ mAttr[i].getName());
}
final MemberAttributeInfo mai = mAttr[i].getMai();
// if object does not have mai it cannot be inserted
if (mai == null) {
final String message = ("MAI not found: " + mAttr[i].getName()); // NOI18N
throw new IllegalStateException(message);
}
// add fieldname of this attribute to statement
paramSql.append(sep).append(mai.getFieldName());
if (!mAttr[i].referencesObject()) // does not reference object, so it does not have key
{
if (value == null) {
// use defaultvalue
values.add(persistenceHelper.getDefaultValue(mai, value));
} else {
// TODO: try to convert JTS GEOMETRY to PGgeometry directly
if (PersistenceHelper.GEOMETRY.isAssignableFrom(value.getClass())) {
values.add(PostGisGeometryFactory.getPostGisCompliantDbString((Geometry)value));
} else {
values.add(value);
}
}
} else if (!mAttr[i].isPrimaryKey()) { // references metaobject
final MetaObject moAttr = (MetaObject)value;
try {
// recursion
if (value != null) {
final int status = moAttr.getStatus();
Integer objectID = moAttr.getID();
switch (status) {
case MetaObject.NEW: {
if (moAttr.isDummy()) {
objectID = mo.getID();
// jt ids still to be made
insertMetaObjectArrayWithoutTransaction(user, moAttr);
} else {
objectID = insertMetaObjectWithoutTransaction(user, moAttr);
}
break;
}
case MetaObject.TO_DELETE: {
objectID = null;
deleteMetaObjectWithoutTransaction(user, moAttr);
break;
}
case MetaObject.MODIFIED:
// NOP
default: {
// NOP
}
}
// foreign key will be set
if (status == MetaObject.TEMPLATE) {
values.add(NULL);
} else {
values.add(objectID);
}
} else if (mAttr[i].isArray()) {
values.add(rootPk);
} else {
values.add(NULL);
}
} catch (final Exception e) {
final String error = "interrupted insertMO recursion moAttr::" + moAttr + " MAI" + mai; // NOI18N
LOG.error(error, e);
throw new PersistenceException(error, e);
}
}
// after the first iteration set the seperator to comma
sep = ", "; // NOI18N
}
// finalise param stmt
sep = ""; // NOI18N
paramSql.append(") VALUES ("); // NOI18N
for (int i = 0; i < values.size(); ++i) {
paramSql.append(sep).append('?'); // NOI18N
sep = ", "; // NOI18N
}
paramSql.append(')'); // NOI18N
// set params and execute stmt
PreparedStatement stmt = null;
try {
final TransactionHelper transactionHelper = local.get();
stmt = transactionHelper.getConnection().prepareStatement(paramSql.toString());
if (LOG.isDebugEnabled()) {
final StringBuilder logMessage = new StringBuilder("Parameterized SQL: ");
logMessage.append(paramSql);
logMessage.append('\n');
final int i = 1;
for (final java.lang.Object value : values) {
if (i > 1) {
logMessage.append("; ");
}
logMessage.append(i);
logMessage.append(". parameter: ");
logMessage.append(value.toString());
}
LOG.debug(logMessage.toString());
}
stmt = parameteriseStatement(stmt, values);
stmt.executeUpdate();
for (final CidsTrigger ct : rightTriggers) {
ct.afterInsert(mo.getBean(), user);
}
} finally {
DBConnection.closeStatements(stmt);
}
return rootPk;
} else {
if (LOG.isDebugEnabled()) {
LOG.debug(
"'" // NOI18N
+ user
+ "' is not allowed to insert MO " // NOI18N
+ mo.getID()
+ "." // NOI18N
+ mo.getClassKey(), // NOI18N
new CurrentStackTrace());
}
throw new SecurityException("not allowed to insert meta object"); // NOI18N
}
}
/**
* DOCUMENT ME!
*
* @param mo DOCUMENT ME!
*
* @return DOCUMENT ME!
*/
private Collection<CidsTrigger> getRightTriggers(final MetaObject mo) {
assert (mo != null);
final ArrayList<CidsTrigger> list = new ArrayList<CidsTrigger>();
final String domain = mo.getMetaClass().getDomain().toLowerCase();
final String table = mo.getMetaClass().getTableName().toLowerCase();
final Collection<CidsTrigger> listForAll = triggers.get(CidsTriggerKey.FORALL);
final Collection<CidsTrigger> listAllTablesInOneDomain = triggers.get(new CidsTriggerKey(
domain,
CidsTriggerKey.ALL));
final Collection<CidsTrigger> listOneTableInAllDomains = triggers.get(new CidsTriggerKey(
CidsTriggerKey.ALL,
table));
final Collection<CidsTrigger> listExplicitTableInDomain = triggers.get(new CidsTriggerKey(domain, table));
if (listForAll != null) {
list.addAll(triggers.get(CidsTriggerKey.FORALL));
}
if (listAllTablesInOneDomain != null) {
list.addAll(triggers.get(new CidsTriggerKey(domain, CidsTriggerKey.ALL)));
}
if (listOneTableInAllDomains != null) {
list.addAll(triggers.get(new CidsTriggerKey(CidsTriggerKey.ALL, table)));
}
if (listExplicitTableInDomain != null) {
list.addAll(triggers.get(new CidsTriggerKey(domain, table)));
}
return list;
}
/**
* DOCUMENT ME!
*
* @param mo DOCUMENT ME!
*/
private void fixMissingMetaClass(final MetaObject mo) {
if (mo.getMetaClass() == null) {
mo.setMetaClass(new MetaClass(dbServer.getClassCache().getClass(mo.getClassID()), mo.getDomain()));
}
}
}