use of com.evolveum.midpoint.repo.sql.data.common.RObject in project midpoint by Evolveum.
the class ObjectUpdater method modifyObjectAttempt.
public <T extends ObjectType> void modifyObjectAttempt(Class<T> type, String oid, Collection<? extends ItemDelta> modifications, RepoModifyOptions modifyOptions, OperationResult result) throws ObjectNotFoundException, SchemaException, ObjectAlreadyExistsException, SerializationRelatedException {
// clone - because some certification and lookup table related methods manipulate this collection and even their constituent deltas
// TODO clone elements only if necessary
modifications = CloneUtil.cloneCollectionMembers(modifications);
//modifications = new ArrayList<>(modifications);
LOGGER.debug("Modifying object '{}' with oid '{}'.", new Object[] { type.getSimpleName(), oid });
LOGGER_PERFORMANCE.debug("> modify object {}, oid={}, modifications={}", type.getSimpleName(), oid, modifications);
if (LOGGER.isTraceEnabled()) {
LOGGER.trace("Modifications:\n{}", DebugUtil.debugDump(modifications));
}
Session session = null;
OrgClosureManager.Context closureContext = null;
try {
session = baseHelper.beginTransaction();
closureContext = closureManager.onBeginTransactionModify(session, type, oid, modifications);
Collection<? extends ItemDelta> lookupTableModifications = lookupTableHelper.filterLookupTableModifications(type, modifications);
Collection<? extends ItemDelta> campaignCaseModifications = caseHelper.filterCampaignCaseModifications(type, modifications);
if (!modifications.isEmpty() || RepoModifyOptions.isExecuteIfNoChanges(modifyOptions)) {
// JpegPhoto (RFocusPhoto) is a special kind of entity. First of all, it is lazily loaded, because photos are really big.
// Each RFocusPhoto naturally belongs to one RFocus, so it would be appropriate to set orphanRemoval=true for focus-photo
// association. However, this leads to a strange problem when merging in-memory RFocus object with the database state:
// If in-memory RFocus object has no photo associated (because of lazy loading), then the associated RFocusPhoto is deleted.
//
// To prevent this behavior, we've set orphanRemoval to false. Fortunately, the remove operation on RFocus
// seems to be still cascaded to RFocusPhoto. What we have to implement ourselves, however, is removal of RFocusPhoto
// _without_ removing of RFocus. In order to know whether the photo has to be removed, we have to retrieve
// its value, apply the delta (e.g. if the delta is a DELETE VALUE X, we have to know whether X matches current
// value of the photo), and if the resulting value is empty, we have to manually delete the RFocusPhoto instance.
//
// So the first step is to retrieve the current value of photo - we obviously do this only if the modifications
// deal with the jpegPhoto property.
Collection<SelectorOptions<GetOperationOptions>> options;
boolean containsFocusPhotoModification = FocusType.class.isAssignableFrom(type) && containsPhotoModification(modifications);
if (containsFocusPhotoModification) {
options = Collections.singletonList(SelectorOptions.create(FocusType.F_JPEG_PHOTO, GetOperationOptions.createRetrieve(RetrieveOption.INCLUDE)));
} else {
options = null;
}
// get object
PrismObject<T> prismObject = objectRetriever.getObjectInternal(session, type, oid, options, true, result);
// apply diff
LOGGER.trace("OBJECT before:\n{}", prismObject.debugDumpLazily());
PrismObject<T> originalObject = null;
if (closureManager.isEnabled()) {
originalObject = prismObject.clone();
}
ItemDelta.applyTo(modifications, prismObject);
LOGGER.trace("OBJECT after:\n{}", prismObject.debugDumpLazily());
// Continuing the photo treatment: should we remove the (now obsolete) focus photo?
// We have to test prismObject at this place, because updateFullObject (below) removes photo property from the prismObject.
boolean shouldPhotoBeRemoved = containsFocusPhotoModification && ((FocusType) prismObject.asObjectable()).getJpegPhoto() == null;
// merge and update object
LOGGER.trace("Translating JAXB to data type.");
ObjectTypeUtil.normalizeAllRelations(prismObject);
RObject rObject = createDataObjectFromJAXB(prismObject, PrismIdentifierGenerator.Operation.MODIFY);
rObject.setVersion(rObject.getVersion() + 1);
updateFullObject(rObject, prismObject);
LOGGER.trace("Starting merge.");
session.merge(rObject);
if (closureManager.isEnabled()) {
closureManager.updateOrgClosure(originalObject, modifications, session, oid, type, OrgClosureManager.Operation.MODIFY, closureContext);
}
// we have to remove the photo manually.
if (shouldPhotoBeRemoved) {
Query query = session.createQuery("delete RFocusPhoto where ownerOid = :oid");
query.setParameter("oid", prismObject.getOid());
query.executeUpdate();
LOGGER.trace("Focus photo for {} was deleted", prismObject.getOid());
}
}
if (LookupTableType.class.isAssignableFrom(type)) {
lookupTableHelper.updateLookupTableData(session, oid, lookupTableModifications);
}
if (AccessCertificationCampaignType.class.isAssignableFrom(type)) {
caseHelper.updateCampaignCases(session, oid, campaignCaseModifications, modifyOptions);
}
LOGGER.trace("Before commit...");
session.getTransaction().commit();
LOGGER.trace("Committed!");
} catch (ObjectNotFoundException ex) {
baseHelper.rollbackTransaction(session, ex, result, true);
throw ex;
} catch (ConstraintViolationException ex) {
handleConstraintViolationException(session, ex, result);
baseHelper.rollbackTransaction(session, ex, result, true);
LOGGER.debug("Constraint violation occurred (will be rethrown as ObjectAlreadyExistsException).", ex);
//todo improve (we support only 5 DB, so we should probably do some hacking in here)
throw new ObjectAlreadyExistsException(ex);
} catch (SchemaException ex) {
baseHelper.rollbackTransaction(session, ex, result, true);
throw ex;
} catch (DtoTranslationException | RuntimeException ex) {
baseHelper.handleGeneralException(ex, session, result);
} finally {
cleanupClosureAndSessionAndResult(closureContext, session, result);
LOGGER.trace("Session cleaned up.");
}
}
use of com.evolveum.midpoint.repo.sql.data.common.RObject in project midpoint by Evolveum.
the class MetadataFactory method toJAXB.
public static MetadataType toJAXB(Metadata repo, PrismContext context) {
if (isNull(repo)) {
return null;
}
MetadataType jaxb = new MetadataType();
jaxb.setCreateChannel(repo.getCreateChannel());
jaxb.setCreateTimestamp(repo.getCreateTimestamp());
jaxb.setModifyChannel(repo.getModifyChannel());
jaxb.setModifyTimestamp(repo.getModifyTimestamp());
if (repo.getCreatorRef() != null) {
jaxb.setCreatorRef(repo.getCreatorRef().toJAXB(context));
}
if (repo.getModifierRef() != null) {
jaxb.setModifierRef(repo.getModifierRef().toJAXB(context));
}
if (repo instanceof RObject) {
List refs = RUtil.safeSetReferencesToList(repo.getCreateApproverRef(), context);
if (!refs.isEmpty()) {
jaxb.getCreateApproverRef().addAll(refs);
}
refs = RUtil.safeSetReferencesToList(repo.getModifyApproverRef(), context);
if (!refs.isEmpty()) {
jaxb.getModifyApproverRef().addAll(refs);
}
} else {
}
return jaxb;
}
use of com.evolveum.midpoint.repo.sql.data.common.RObject in project midpoint by Evolveum.
the class ObjectRetriever method getObjectInternal.
public <T extends ObjectType> PrismObject<T> getObjectInternal(Session session, Class<T> type, String oid, Collection<SelectorOptions<GetOperationOptions>> options, boolean lockForUpdate, OperationResult operationResult) throws ObjectNotFoundException, SchemaException, DtoTranslationException {
boolean lockedForUpdateViaHibernate = false;
boolean lockedForUpdateViaSql = false;
LockOptions lockOptions = new LockOptions();
//todo fix lock for update!!!!!
if (lockForUpdate) {
if (getConfiguration().isLockForUpdateViaHibernate()) {
lockOptions.setLockMode(LockMode.PESSIMISTIC_WRITE);
lockedForUpdateViaHibernate = true;
} else if (getConfiguration().isLockForUpdateViaSql()) {
LOGGER.trace("Trying to lock object {} for update (via SQL)", oid);
long time = System.currentTimeMillis();
SQLQuery q = session.createSQLQuery("select oid from m_object where oid = ? for update");
q.setString(0, oid);
Object result = q.uniqueResult();
if (result == null) {
return throwObjectNotFoundException(type, oid);
}
if (LOGGER.isTraceEnabled()) {
LOGGER.trace("Locked via SQL (in {} ms)", System.currentTimeMillis() - time);
}
lockedForUpdateViaSql = true;
}
}
if (LOGGER.isTraceEnabled()) {
if (lockedForUpdateViaHibernate) {
LOGGER.trace("Getting object {} with locking for update (via hibernate)", oid);
} else if (lockedForUpdateViaSql) {
LOGGER.trace("Getting object {}, already locked for update (via SQL)", oid);
} else {
LOGGER.trace("Getting object {} without locking for update", oid);
}
}
GetObjectResult fullObject = null;
if (!lockForUpdate) {
Query query = session.getNamedQuery("get.object");
query.setString("oid", oid);
query.setResultTransformer(GetObjectResult.RESULT_STYLE.getResultTransformer());
query.setLockOptions(lockOptions);
fullObject = (GetObjectResult) query.uniqueResult();
} else {
// we're doing update after this get, therefore we load full object right now
// (it would be loaded during merge anyway)
// this just loads object to hibernate session, probably will be removed later. Merge after this get
// will be faster. Read and use object only from fullObject column.
// todo remove this later [lazyman]
Criteria criteria = session.createCriteria(ClassMapper.getHQLTypeClass(type));
criteria.add(Restrictions.eq("oid", oid));
criteria.setLockMode(lockOptions.getLockMode());
RObject obj = (RObject) criteria.uniqueResult();
if (obj != null) {
fullObject = new GetObjectResult(obj.getOid(), obj.getFullObject(), obj.getStringsCount(), obj.getLongsCount(), obj.getDatesCount(), obj.getReferencesCount(), obj.getPolysCount(), obj.getBooleansCount());
}
}
LOGGER.trace("Got it.");
if (fullObject == null) {
throwObjectNotFoundException(type, oid);
}
LOGGER.trace("Transforming data to JAXB type.");
PrismObject<T> prismObject = updateLoadedObject(fullObject, type, oid, options, null, session, operationResult);
validateObjectType(prismObject, type);
return prismObject;
}
use of com.evolveum.midpoint.repo.sql.data.common.RObject in project midpoint by Evolveum.
the class ObjectUpdater method overwriteAddObjectAttempt.
private <T extends ObjectType> String overwriteAddObjectAttempt(PrismObject<T> object, RObject rObject, String originalOid, Session session, OrgClosureManager.Context closureContext, OperationResult result) throws ObjectAlreadyExistsException, SchemaException, DtoTranslationException {
PrismObject<T> oldObject = null;
//check if object already exists, find differences and increment version if necessary
Collection<? extends ItemDelta> modifications = null;
if (originalOid != null) {
try {
oldObject = objectRetriever.getObjectInternal(session, object.getCompileTimeClass(), originalOid, null, true, result);
ObjectDelta<T> delta = object.diff(oldObject);
modifications = delta.getModifications();
LOGGER.trace("overwriteAddObjectAttempt: originalOid={}, modifications={}", originalOid, modifications);
//we found existing object which will be overwritten, therefore we increment version
Integer version = RUtil.getIntegerFromString(oldObject.getVersion());
version = (version == null) ? 0 : ++version;
rObject.setVersion(version);
// } catch (QueryException ex) {
// baseHelper.handleGeneralCheckedException(ex, session, null);
} catch (ObjectNotFoundException ex) {
//it's ok that object was not found, therefore we won't be overwriting it
}
}
updateFullObject(rObject, object);
RObject merged = (RObject) session.merge(rObject);
lookupTableHelper.addLookupTableRows(session, rObject, oldObject != null);
caseHelper.addCertificationCampaignCases(session, rObject, oldObject != null);
if (closureManager.isEnabled()) {
OrgClosureManager.Operation operation;
if (modifications == null) {
operation = OrgClosureManager.Operation.ADD;
modifications = createAddParentRefDelta(object);
} else {
operation = OrgClosureManager.Operation.MODIFY;
}
closureManager.updateOrgClosure(oldObject, modifications, session, merged.getOid(), object.getCompileTimeClass(), operation, closureContext);
}
return merged.getOid();
}
use of com.evolveum.midpoint.repo.sql.data.common.RObject in project midpoint by Evolveum.
the class ObjectUpdater method addObjectAttempt.
public <T extends ObjectType> String addObjectAttempt(PrismObject<T> object, RepoAddOptions options, OperationResult result) throws ObjectAlreadyExistsException, SchemaException {
LOGGER_PERFORMANCE.debug("> add object {}, oid={}, overwrite={}", object.getCompileTimeClass().getSimpleName(), object.getOid(), options.isOverwrite());
String oid = null;
Session session = null;
OrgClosureManager.Context closureContext = null;
// it is needed to keep the original oid for example for import options. if we do not keep it
// and it was null it can bring some error because the oid is set when the object contains orgRef
// or it is org. and by the import we do not know it so it will be trying to delete non-existing object
String originalOid = object.getOid();
try {
if (LOGGER.isTraceEnabled()) {
LOGGER.trace("Object\n{}", object.debugDump());
}
ObjectTypeUtil.normalizeAllRelations(object);
LOGGER.trace("Translating JAXB to data type.");
PrismIdentifierGenerator.Operation operation = options.isOverwrite() ? PrismIdentifierGenerator.Operation.ADD_WITH_OVERWRITE : PrismIdentifierGenerator.Operation.ADD;
RObject rObject = createDataObjectFromJAXB(object, operation);
session = baseHelper.beginTransaction();
closureContext = closureManager.onBeginTransactionAdd(session, object, options.isOverwrite());
if (options.isOverwrite()) {
oid = overwriteAddObjectAttempt(object, rObject, originalOid, session, closureContext, result);
} else {
oid = nonOverwriteAddObjectAttempt(object, rObject, originalOid, session, closureContext);
}
session.getTransaction().commit();
LOGGER.trace("Saved object '{}' with oid '{}'", object.getCompileTimeClass().getSimpleName(), oid);
object.setOid(oid);
} catch (ConstraintViolationException ex) {
handleConstraintViolationException(session, ex, result);
baseHelper.rollbackTransaction(session, ex, result, true);
LOGGER.debug("Constraint violation occurred (will be rethrown as ObjectAlreadyExistsException).", ex);
// to the original oid and prevent of unexpected behaviour (e.g. by import with overwrite option)
if (StringUtils.isEmpty(originalOid)) {
object.setOid(null);
}
String constraintName = ex.getConstraintName();
// Breaker to avoid long unreadable messages
if (constraintName != null && constraintName.length() > SqlRepositoryServiceImpl.MAX_CONSTRAINT_NAME_LENGTH) {
constraintName = null;
}
throw new ObjectAlreadyExistsException("Conflicting object already exists" + (constraintName == null ? "" : " (violated constraint '" + constraintName + "')"), ex);
} catch (ObjectAlreadyExistsException | SchemaException ex) {
baseHelper.rollbackTransaction(session, ex, result, true);
throw ex;
} catch (DtoTranslationException | RuntimeException ex) {
baseHelper.handleGeneralException(ex, session, result);
} finally {
cleanupClosureAndSessionAndResult(closureContext, session, result);
}
return oid;
}
Aggregations