/*
* Copyright (c) 2010-2015 Evolveum
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.evolveum.midpoint.repo.sql.helpers;
import com.evolveum.midpoint.prism.PrismObject;
import com.evolveum.midpoint.repo.sql.SerializationRelatedException;
import com.evolveum.midpoint.repo.sql.SqlRepositoryServiceImpl;
import com.evolveum.midpoint.repo.sql.data.common.RObject;
import com.evolveum.midpoint.repo.sql.util.DtoTranslationException;
import com.evolveum.midpoint.repo.sql.util.PrismIdentifierGenerator;
import com.evolveum.midpoint.schema.result.OperationResult;
import com.evolveum.midpoint.util.exception.ObjectNotFoundException;
import com.evolveum.midpoint.util.exception.SchemaException;
import com.evolveum.midpoint.util.exception.SystemException;
import com.evolveum.midpoint.util.logging.Trace;
import com.evolveum.midpoint.util.logging.TraceManager;
import com.evolveum.midpoint.xml.ns._public.common.common_3.SequenceType;
import org.hibernate.Session;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Collection;
import java.util.Iterator;
/**
* @author mederly
*/
@Component
public class SequenceHelper {
@Autowired
private ObjectRetriever objectRetriever;
@Autowired
private ObjectUpdater objectUpdater;
@Autowired
private BaseHelper baseHelper;
private static final Trace LOGGER = TraceManager.getTrace(SqlRepositoryServiceImpl.class);
private static final Trace LOGGER_PERFORMANCE = TraceManager.getTrace(SqlRepositoryServiceImpl.PERFORMANCE_LOG_NAME);
public long advanceSequenceAttempt(String oid, OperationResult result) throws ObjectNotFoundException,
SchemaException, SerializationRelatedException {
long returnValue;
LOGGER.debug("Advancing sequence with oid '{}'.", oid);
LOGGER_PERFORMANCE.debug("> advance sequence, oid={}", oid);
Session session = null;
try {
session = baseHelper.beginTransaction();
PrismObject<SequenceType> prismObject = objectRetriever.getObjectInternal(session, SequenceType.class, oid, null, true, result);
if (LOGGER.isTraceEnabled()) {
LOGGER.trace("OBJECT before:\n{}", prismObject.debugDump());
}
SequenceType sequence = prismObject.asObjectable();
if (!sequence.getUnusedValues().isEmpty()) {
returnValue = sequence.getUnusedValues().remove(0);
} else {
long counter = sequence.getCounter() != null ? sequence.getCounter() : 0L;
long maxCounter = sequence.getMaxCounter() != null ? sequence.getMaxCounter() : Long.MAX_VALUE;
boolean allowRewind = Boolean.TRUE.equals(sequence.isAllowRewind());
if (counter < maxCounter) {
returnValue = counter;
sequence.setCounter(counter + 1);
} else if (counter == maxCounter) {
returnValue = counter;
if (allowRewind) {
sequence.setCounter(0L);
} else {
sequence.setCounter(counter + 1); // will produce exception during next run
}
} else { // i.e. counter > maxCounter
if (allowRewind) { // shouldn't occur but...
LOGGER.warn("Sequence {} overflown with allowRewind set to true. Rewinding.", oid);
returnValue = 0;
sequence.setCounter(1L);
} else {
// TODO some better exception...
throw new SystemException("No (next) value available from sequence " + oid + ". Current counter = " + sequence.getCounter() + ", max value = " + sequence.getMaxCounter());
}
}
}
if (LOGGER.isTraceEnabled()) {
LOGGER.trace("Return value = {}, OBJECT after:\n{}", returnValue, prismObject.debugDump());
}
// merge and update object
LOGGER.trace("Translating JAXB to data type.");
RObject rObject = objectUpdater.createDataObjectFromJAXB(prismObject, PrismIdentifierGenerator.Operation.MODIFY);
rObject.setVersion(rObject.getVersion() + 1);
objectUpdater.updateFullObject(rObject, prismObject);
session.merge(rObject);
LOGGER.trace("Before commit...");
session.getTransaction().commit();
LOGGER.trace("Committed!");
return returnValue;
} catch (ObjectNotFoundException ex) {
baseHelper.rollbackTransaction(session, ex, result, true);
throw ex;
} catch (SchemaException ex) {
baseHelper.rollbackTransaction(session, ex, result, true);
throw ex;
} catch (DtoTranslationException | RuntimeException ex) {
baseHelper.handleGeneralException(ex, session, result); // should always throw an exception
throw new SystemException("Exception " + ex + " was not handled correctly", ex); // ...so this shouldn't occur at all
} finally {
baseHelper.cleanupSessionAndResult(session, result);
LOGGER.trace("Session cleaned up.");
}
}
public void returnUnusedValuesToSequenceAttempt(String oid, Collection<Long> unusedValues, OperationResult result) throws ObjectNotFoundException,
SchemaException, SerializationRelatedException {
LOGGER.debug("Returning unused values of {} to a sequence with oid '{}'.", unusedValues, oid);
LOGGER_PERFORMANCE.debug("> return unused values, oid={}, values={}", oid, unusedValues);
Session session = null;
try {
session = baseHelper.beginTransaction();
PrismObject<SequenceType> prismObject = objectRetriever.getObjectInternal(session, SequenceType.class, oid, null, true, result);
if (LOGGER.isTraceEnabled()) {
LOGGER.trace("OBJECT before:\n{}", prismObject.debugDump());
}
SequenceType sequence = prismObject.asObjectable();
int maxUnusedValues = sequence.getMaxUnusedValues() != null ? sequence.getMaxUnusedValues() : 0;
Iterator<Long> valuesToReturnIterator = unusedValues.iterator();
while (valuesToReturnIterator.hasNext() && sequence.getUnusedValues().size() < maxUnusedValues) {
Long valueToReturn = valuesToReturnIterator.next();
if (valueToReturn == null) { // sanity check
continue;
}
if (!sequence.getUnusedValues().contains(valueToReturn)) {
sequence.getUnusedValues().add(valueToReturn);
} else {
LOGGER.warn("UnusedValues in sequence {} already contains value of {} - ignoring the return request", oid, valueToReturn);
}
}
if (LOGGER.isTraceEnabled()) {
LOGGER.trace("OBJECT after:\n{}", prismObject.debugDump());
}
// merge and update object
LOGGER.trace("Translating JAXB to data type.");
RObject rObject = objectUpdater.createDataObjectFromJAXB(prismObject, PrismIdentifierGenerator.Operation.MODIFY);
rObject.setVersion(rObject.getVersion() + 1);
objectUpdater.updateFullObject(rObject, prismObject);
session.merge(rObject);
LOGGER.trace("Before commit...");
session.getTransaction().commit();
LOGGER.trace("Committed!");
} catch (ObjectNotFoundException ex) {
baseHelper.rollbackTransaction(session, ex, result, true);
throw ex;
} catch (SchemaException ex) {
baseHelper.rollbackTransaction(session, ex, result, true);
throw ex;
} catch (DtoTranslationException | RuntimeException ex) {
baseHelper.handleGeneralException(ex, session, result); // should always throw an exception
throw new SystemException("Exception " + ex + " was not handled correctly", ex); // ...so this shouldn't occur at all
} finally {
baseHelper.cleanupSessionAndResult(session, result);
LOGGER.trace("Session cleaned up.");
}
}
}