/**
* ***************************************************************************
* Copyright (c) 2010 Qcadoo Limited
* Project: Qcadoo MES
* Version: 1.4
*
* This file is part of Qcadoo.
*
* Qcadoo is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation; either version 3 of the License,
* or (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
* ***************************************************************************
*/
package com.qcadoo.mes.states.aop;
import static com.qcadoo.mes.states.constants.StateChangeStatus.SUCCESSFUL;
import java.util.Date;
import org.apache.log4j.Logger;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.DeclarePrecedence;
import org.springframework.beans.factory.annotation.Configurable;
import org.springframework.transaction.annotation.Transactional;
import com.qcadoo.mes.states.StateChangeContext;
import com.qcadoo.mes.states.StateChangeEntityDescriber;
import com.qcadoo.mes.states.StateEnum;
import com.qcadoo.mes.states.constants.StateChangeStatus;
import com.qcadoo.mes.states.exception.StateChangeException;
import com.qcadoo.mes.states.exception.StateTransitionNotAlloweException;
import com.qcadoo.mes.states.messages.constants.StateMessageType;
import com.qcadoo.mes.states.messages.util.ValidationMessageHelper;
import com.qcadoo.mes.states.service.StateChangePhaseUtil;
import com.qcadoo.mes.states.service.StateChangeService;
import com.qcadoo.model.api.Entity;
/**
* Abstract service for changing entity state which provides default implementation.
*
* @since 1.1.7
*/
@Aspect
@Configurable
@DeclarePrecedence("com.qcadoo.mes.states.aop.StateChangePhaseAspect, com.qcadoo.mes.states.aop.RunInPhaseAspect")
public abstract class AbstractStateChangeAspect implements StateChangeService {
protected static final int DEFAULT_NUM_OF_PHASES = 2;
private static final Logger LOGGER = Logger.getLogger(StateChangeService.class);
@Override
public void changeState(final StateChangeContext stateChangeContext) {
try {
performStateChange(stateChangeContext);
} catch (Exception exception) {
LOGGER.warn("Can't perform state change", exception);
stateChangeContext.setStatus(StateChangeStatus.FAILURE);
stateChangeContext.addMessage("states.messages.change.failure.internalServerError", StateMessageType.FAILURE);
stateChangeContext.save();
throw new StateChangeException(exception);
}
}
@Transactional
private void performStateChange(final StateChangeContext stateChangeContext) {
final StateChangeEntityDescriber describer = stateChangeContext.getDescriber();
describer.checkFields();
for (int phase = stateChangeContext.getPhase() + 1; phase <= getNumOfPhases(); phase++) {
if (StateChangePhaseUtil.canRun(stateChangeContext)) {
stateChangeContext.setPhase(phase);
changeStatePhase(stateChangeContext, phase);
}
}
final Entity owner = stateChangeContext.getOwner();
stateChangeContext.setOwner(owner);
performChangeEntityState(stateChangeContext);
}
/**
* Get number of state change phases. Default value is {@link AbstractStateChangeAspect#DEFAULT_NUM_OF_PHASES}.
*
* @return number of phases
*/
protected int getNumOfPhases() {
return DEFAULT_NUM_OF_PHASES;
}
/**
* Single state change phase join point.
*
* @param stateChangeEntity
* @param phaseNumber
*/
protected abstract void changeStatePhase(final StateChangeContext stateChangeContext, final int phaseNumber);
@Transactional
protected void performChangeEntityState(final StateChangeContext stateChangeContext) {
final StateChangeEntityDescriber describer = stateChangeContext.getDescriber();
if (!StateChangePhaseUtil.canRun(stateChangeContext)) {
if (!stateChangeContext.isOwnerValid()) {
stateChangeContext.setStatus(StateChangeStatus.FAILURE);
stateChangeContext.setField(describer.getDateTimeFieldName(), new Date());
LOGGER.info(String.format("State change : failure. Entity name : %S id : %d.",
stateChangeContext.getOwner().getDataDefinition().getName(), stateChangeContext.getOwner().getId()));
}
return;
}
final Entity owner = stateChangeContext.getOwner();
final StateEnum sourceState = stateChangeContext.getStateEnumValue(describer.getSourceStateFieldName());
final StateEnum targetState = stateChangeContext.getStateEnumValue(describer.getTargetStateFieldName());
if (sourceState != null && !sourceState.canChangeTo(targetState)) {
throw new StateTransitionNotAlloweException(sourceState, targetState);
}
boolean ownerIsValid = stateChangeContext.isOwnerValid();
if (ownerIsValid) {
owner.setField(describer.getOwnerStateFieldName(), targetState.getStringValue());
ownerIsValid = owner.getDataDefinition().save(owner).isValid();
}
if (ownerIsValid) {
stateChangeContext.setStatus(SUCCESSFUL);
LOGGER.info(String.format("State change : successful. Entity name : %S id : %d. Target state : %S",
owner.getDataDefinition().getName(), owner.getId(), targetState));
} else {
LOGGER.info(String.format("State change : failure. Entity name : %S id : %d. Target state : %S",
owner.getDataDefinition().getName(), owner.getId(), targetState));
ValidationMessageHelper.copyErrorsFromEntity(stateChangeContext, owner);
stateChangeContext.setStatus(StateChangeStatus.FAILURE);
}
stateChangeContext.setField(describer.getDateTimeFieldName(), new Date());
stateChangeContext.save();
}
}