/*
* 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.certification.impl;
import com.evolveum.midpoint.certification.api.AccessCertificationEventListener;
import com.evolveum.midpoint.certification.api.CertificationManager;
import com.evolveum.midpoint.certification.api.OutcomeUtils;
import com.evolveum.midpoint.certification.impl.handlers.CertificationHandler;
import com.evolveum.midpoint.model.api.ModelAuthorizationAction;
import com.evolveum.midpoint.model.api.ModelService;
import com.evolveum.midpoint.prism.PrismContext;
import com.evolveum.midpoint.prism.PrismObject;
import com.evolveum.midpoint.prism.delta.ItemDelta;
import com.evolveum.midpoint.prism.query.ObjectQuery;
import com.evolveum.midpoint.repo.api.RepositoryService;
import com.evolveum.midpoint.schema.GetOperationOptions;
import com.evolveum.midpoint.schema.SelectorOptions;
import com.evolveum.midpoint.schema.result.OperationResult;
import com.evolveum.midpoint.schema.util.CertCampaignTypeUtil;
import com.evolveum.midpoint.schema.util.ObjectTypeUtil;
import com.evolveum.midpoint.security.api.SecurityEnforcer;
import com.evolveum.midpoint.security.api.SecurityUtil;
import com.evolveum.midpoint.task.api.Task;
import com.evolveum.midpoint.util.exception.*;
import com.evolveum.midpoint.util.logging.Trace;
import com.evolveum.midpoint.util.logging.TraceManager;
import com.evolveum.midpoint.xml.ns._public.common.common_3.*;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import java.util.*;
import static com.evolveum.midpoint.xml.ns._public.common.common_3.AccessCertificationCampaignStateType.*;
import static com.evolveum.midpoint.xml.ns._public.common.common_3.AccessCertificationCampaignType.F_CASE;
/**
* All operations carried out by CertificationManager have to be authorized by it. ModelController does NOT execute
* any authorizations before passing method calls to this module.
*
* All repository read operations are invoked on repository cache (even if we currently don't enter the cache).
*
* All write operations are passed to model, with the flag of preAuthorized set to TRUE (and currently in raw mode).
* The reason is that we want the changes to be audited.
*
* In the future, the raw mode could be eventually changed to non-raw, in order to allow e.g. lower-level notifications
* to be sent (that means, for example, notifications related to changing certification campaign as a result of carrying
* out open/close stage operations). But currently we are satisfied with notifications that are emitted by the
* certification module itself.
*
* Also, in the future, we could do some higher-level audit by this module. But for now we are OK with the lower-level
* audit generated by the model.
*
* TODO: consider the enormous size of audit events in case of big campaigns (e.g. thousands or tens of thousands
* certification cases).
*
* We try to carry out repo update in one operation to ensure atomicity; unless we need to carry out them on separate
* objects (e.g. certification definition + certification campaign).
*
* Methods in this module (e.g. searchCases) are to be called from outside only, as they carry out the authorization.
* For pre-authorized versions please see various helpers, e.g. AccCertQueryHelper.
*
* @author mederly
*/
@Service(value = "certificationManager")
public class CertificationManagerImpl implements CertificationManager {
private static final transient Trace LOGGER = TraceManager.getTrace(CertificationManager.class);
public static final String INTERFACE_DOT = CertificationManager.class.getName() + ".";
public static final String CLASS_DOT = CertificationManagerImpl.class.getName() + ".";
public static final String OPERATION_CREATE_CAMPAIGN = INTERFACE_DOT + "createCampaign";
public static final String OPERATION_CREATE_AD_HOC_CAMPAIGNS = INTERFACE_DOT + "createAdHocCampaigns";
public static final String OPERATION_OPEN_NEXT_STAGE = INTERFACE_DOT + "openNextStage";
public static final String OPERATION_CLOSE_CURRENT_STAGE = INTERFACE_DOT + "closeCurrentStage";
public static final String OPERATION_RECORD_DECISION = INTERFACE_DOT + "recordDecision";
public static final String OPERATION_SEARCH_OPEN_WORK_ITEMS = INTERFACE_DOT + "searchOpenWorkItems";
public static final String OPERATION_CLOSE_CAMPAIGN = INTERFACE_DOT + "closeCampaign";
public static final String OPERATION_DELEGATE_WORK_ITEMS = INTERFACE_DOT + "delegateWorkItems";
public static final String OPERATION_GET_CAMPAIGN_STATISTICS = INTERFACE_DOT + "getCampaignStatistics";
@Autowired private PrismContext prismContext;
@Autowired @Qualifier("cacheRepositoryService") private RepositoryService repositoryService;
@Autowired private ModelService modelService;
@Autowired protected SecurityEnforcer securityEnforcer;
@Autowired protected AccCertGeneralHelper generalHelper;
@Autowired protected AccCertEventHelper eventHelper;
@Autowired protected AccCertQueryHelper queryHelper;
@Autowired protected AccCertUpdateHelper updateHelper;
@Autowired protected AccCertCaseOperationsHelper caseHelper;
@Autowired private AccessCertificationRemediationTaskHandler remediationTaskHandler;
private Map<String,CertificationHandler> registeredHandlers = new HashMap<>();
public void registerHandler(String handlerUri, CertificationHandler handler) {
if (registeredHandlers.containsKey(handlerUri)) {
throw new IllegalStateException("There is already a handler with URI " + handlerUri);
}
registeredHandlers.put(handlerUri, handler);
}
public CertificationHandler findCertificationHandler(AccessCertificationCampaignType campaign) {
if (StringUtils.isBlank(campaign.getHandlerUri())) {
throw new IllegalArgumentException("No handler URI for access certification campaign " + ObjectTypeUtil.toShortString(campaign));
}
CertificationHandler handler = registeredHandlers.get(campaign.getHandlerUri());
if (handler == null) {
throw new IllegalStateException("No handler for URI " + campaign.getHandlerUri());
}
return handler;
}
@Override
public AccessCertificationCampaignType createCampaign(String definitionOid, Task task, OperationResult parentResult)
throws SchemaException, SecurityViolationException, ObjectNotFoundException, ObjectAlreadyExistsException {
Validate.notNull(definitionOid, "definitionOid");
Validate.notNull(task, "task");
Validate.notNull(parentResult, "parentResult");
OperationResult result = parentResult.createSubresult(OPERATION_CREATE_CAMPAIGN);
try {
PrismObject<AccessCertificationDefinitionType> definition = repositoryService.getObject(AccessCertificationDefinitionType.class, definitionOid, null, result);
securityEnforcer.authorize(ModelAuthorizationAction.CREATE_CERTIFICATION_CAMPAIGN.getUrl(), null, definition, null, null, null, result);
AccessCertificationCampaignType newCampaign = updateHelper.createCampaignObject(definition.asObjectable(), task, result);
updateHelper.addObject(newCampaign, task, result);
return newCampaign;
} catch (RuntimeException e) {
result.recordFatalError("Couldn't create certification campaign: unexpected exception: " + e.getMessage(), e);
throw e;
} finally {
result.computeStatusIfUnknown();
}
}
// This is an action that can be run in unprivileged context. No authorizations are checked. Take care when and where you call it.
// Child result is intentionally created only when a certification campaign is to be started (to avoid useless creation of many empty records)
<O extends ObjectType> void startAdHocCertifications(PrismObject<O> focus,
List<CertificationPolicyActionType> actions, Task task, OperationResult parentResult)
throws SchemaException, ObjectNotFoundException {
Set<String> definitionOids = new HashSet<>();
for (CertificationPolicyActionType action : actions) {
if (action.getDefinitionRef() != null) {
for (ObjectReferenceType definitionRef : action.getDefinitionRef()) {
if (definitionRef.getOid() != null) {
definitionOids.add(definitionRef.getOid());
} else {
// TODO resolve dynamic reference
LOGGER.warn("Certification action having definition reference with no OID; the reference will be ignored: {}", definitionRef);
}
}
} else {
LOGGER.warn("Certification action without definition reference; will be ignored: {}", action);
}
}
if (!definitionOids.isEmpty()) {
OperationResult result = parentResult.createSubresult(OPERATION_CREATE_AD_HOC_CAMPAIGNS);
result.addParam("focus", focus);
result.addCollectionOfSerializablesAsParam("definitionOids", definitionOids);
try {
PrismObject<UserType> administrator = repositoryService
.getObject(UserType.class, SystemObjectsType.USER_ADMINISTRATOR.value(), null, result);
securityEnforcer.runAs(() -> {
for (String definitionOid : definitionOids) {
startAdHocCertification(focus, definitionOid, task, result);
}
parentResult.computeStatus();
return null;
}, administrator);
} catch (RuntimeException e) {
result.recordFatalError(e.getMessage(), e); // TODO
throw e;
}
}
}
private <O extends ObjectType> void startAdHocCertification(PrismObject<O> focus, String definitionOid, Task task,
OperationResult result) {
try {
AccessCertificationDefinitionType definition = repositoryService.getObject(AccessCertificationDefinitionType.class, definitionOid, null, result).asObjectable();
AccessCertificationCampaignType newCampaign = updateHelper.createAdHocCampaignObject(definition, focus, task, result);
updateHelper.addObject(newCampaign, task, result);
openNextStage(newCampaign.getOid(), 1, task, result);
result.computeStatus();
} catch (RuntimeException|SchemaException|ObjectNotFoundException|SecurityViolationException|ObjectAlreadyExistsException e) {
result.recordFatalError("Couldn't create ad-hoc certification campaign: " + e.getMessage(), e);
throw new SystemException("Couldn't create ad-hoc certification campaign: " + e.getMessage(), e);
}
}
@Override
public void openNextStage(String campaignOid, int requestedStageNumber, Task task, OperationResult parentResult) throws SchemaException, SecurityViolationException, ObjectNotFoundException, ObjectAlreadyExistsException {
Validate.notNull(campaignOid, "campaignOid");
Validate.notNull(task, "task");
Validate.notNull(parentResult, "parentResult");
OperationResult result = parentResult.createSubresult(OPERATION_OPEN_NEXT_STAGE);
result.addParam("campaignOid", campaignOid);
result.addParam("requestedStageNumber", requestedStageNumber);
try {
AccessCertificationCampaignType campaign = generalHelper.getCampaign(campaignOid, null, task, result);
result.addParam("campaign", ObjectTypeUtil.toShortString(campaign));
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("openNextStage starting for {}", ObjectTypeUtil.toShortString(campaign));
}
securityEnforcer.authorize(ModelAuthorizationAction.OPEN_CERTIFICATION_CAMPAIGN_REVIEW_STAGE.getUrl(), null,
campaign.asPrismObject(), null, null, null, result);
final int currentStageNumber = campaign.getStageNumber();
final int stages = CertCampaignTypeUtil.getNumberOfStages(campaign);
final AccessCertificationCampaignStateType state = campaign.getState();
LOGGER.trace("openNextStage: currentStageNumber={}, stages={}, requestedStageNumber={}, state={}", currentStageNumber, stages, requestedStageNumber, state);
if (IN_REVIEW_STAGE.equals(state)) {
result.recordFatalError("Couldn't advance to review stage " + requestedStageNumber + " as the stage " + currentStageNumber + " is currently open.");
} else if (IN_REMEDIATION.equals(state)) {
result.recordFatalError("Couldn't advance to review stage " + requestedStageNumber + " as the campaign is currently in the remediation phase.");
} else if (CLOSED.equals(state)) {
result.recordFatalError("Couldn't advance to review stage " + requestedStageNumber + " as the campaign is already closed.");
} else if (!REVIEW_STAGE_DONE.equals(state) && !CREATED.equals(state)) {
throw new IllegalStateException("Unexpected campaign state: " + state);
} else if (REVIEW_STAGE_DONE.equals(state) && requestedStageNumber != currentStageNumber+1) {
result.recordFatalError("Couldn't advance to review stage " + requestedStageNumber + " as the campaign is currently in stage " + currentStageNumber);
} else if (CREATED.equals(state) && requestedStageNumber != 1) {
result.recordFatalError("Couldn't advance to review stage " + requestedStageNumber + " as the campaign was just created");
} else if (requestedStageNumber > stages) {
result.recordFatalError("Couldn't advance to review stage " + requestedStageNumber + " as the campaign has only " + stages + " stages");
} else {
final CertificationHandler handler = findCertificationHandler(campaign);
final AccessCertificationStageType stage = updateHelper.createStage(campaign, currentStageNumber+1);
final List<ItemDelta<?,?>> deltas = updateHelper.getDeltasForStageOpen(campaign, stage, handler, task, result);
updateHelper.modifyObjectViaModel(AccessCertificationCampaignType.class, campaignOid, deltas, task, result);
updateHelper.afterStageOpen(campaignOid, stage, task, result);
}
} catch (RuntimeException e) {
result.recordFatalError("Couldn't move to certification campaign stage " + requestedStageNumber + ": unexpected exception: " + e.getMessage(), e);
throw e;
} finally {
result.computeStatusIfUnknown();
}
}
@Override
public void closeCurrentStage(String campaignOid, int stageNumberToClose, Task task, OperationResult parentResult) throws SchemaException, SecurityViolationException, ObjectNotFoundException, ObjectAlreadyExistsException {
Validate.notNull(campaignOid, "campaignOid");
Validate.notNull(task, "task");
Validate.notNull(parentResult, "parentResult");
OperationResult result = parentResult.createSubresult(OPERATION_CLOSE_CURRENT_STAGE);
result.addParam("campaignOid", campaignOid);
result.addParam("stageNumber", stageNumberToClose);
try {
AccessCertificationCampaignType campaign = generalHelper.getCampaign(campaignOid, null, task, result);
result.addParam("campaign", ObjectTypeUtil.toShortString(campaign));
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("closeCurrentStage starting for {}", ObjectTypeUtil.toShortString(campaign));
}
securityEnforcer.authorize(ModelAuthorizationAction.CLOSE_CERTIFICATION_CAMPAIGN_REVIEW_STAGE.getUrl(), null,
campaign.asPrismObject(), null, null, null, result);
final int currentStageNumber = campaign.getStageNumber();
final int stages = CertCampaignTypeUtil.getNumberOfStages(campaign);
final AccessCertificationCampaignStateType state = campaign.getState();
LOGGER.trace("closeCurrentStage: currentStageNumber={}, stages={}, stageNumberToClose={}, state={}", currentStageNumber, stages, stageNumberToClose, state);
if (stageNumberToClose != currentStageNumber) {
result.recordFatalError("Couldn't close review stage " + stageNumberToClose + " as the campaign is not in that stage");
} else if (!IN_REVIEW_STAGE.equals(state)) {
result.recordFatalError("Couldn't close review stage " + stageNumberToClose + " as it is currently not open");
} else {
List<ItemDelta<?,?>> deltas = updateHelper.getDeltasForStageClose(campaign, result);
updateHelper.modifyObjectViaModel(AccessCertificationCampaignType.class, campaignOid, deltas, task, result);
updateHelper.afterStageClose(campaignOid, task, result);
}
} catch (RuntimeException e) {
result.recordFatalError("Couldn't close certification campaign stage " + stageNumberToClose+ ": unexpected exception: " + e.getMessage(), e);
throw e;
} finally {
result.computeStatusIfUnknown();
}
}
@Override
public void startRemediation(String campaignOid, Task task, OperationResult parentResult) throws ObjectNotFoundException, SchemaException, SecurityViolationException, ObjectAlreadyExistsException {
Validate.notNull(campaignOid, "campaignOid");
Validate.notNull(task, "task");
Validate.notNull(parentResult, "parentResult");
OperationResult result = parentResult.createSubresult(OPERATION_CLOSE_CURRENT_STAGE);
result.addParam("campaignOid", campaignOid);
try {
AccessCertificationCampaignType campaign = generalHelper.getCampaign(campaignOid, null, task, result);
result.addParam("campaign", ObjectTypeUtil.toShortString(campaign));
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("startRemediation starting for {}", ObjectTypeUtil.toShortString(campaign));
}
securityEnforcer.authorize(ModelAuthorizationAction.START_CERTIFICATION_REMEDIATION.getUrl(), null,
campaign.asPrismObject(), null, null, null, result);
final int currentStageNumber = campaign.getStageNumber();
final int lastStageNumber = CertCampaignTypeUtil.getNumberOfStages(campaign);
final AccessCertificationCampaignStateType state = campaign.getState();
LOGGER.trace("startRemediation: currentStageNumber={}, stages={}, state={}", currentStageNumber, lastStageNumber, state);
if (currentStageNumber != lastStageNumber) {
result.recordFatalError("Couldn't start the remediation as the campaign is not in its last stage ("+lastStageNumber+"); current stage: "+currentStageNumber);
} else if (!REVIEW_STAGE_DONE.equals(state)) {
result.recordFatalError("Couldn't start the remediation as the last stage was not properly closed.");
} else {
List<ItemDelta<?,?>> deltas = updateHelper.createDeltasForStageNumberAndState(lastStageNumber + 1, IN_REMEDIATION);
updateHelper.modifyObjectViaModel(AccessCertificationCampaignType.class, campaignOid, deltas, task, result);
if (CertCampaignTypeUtil.isRemediationAutomatic(campaign)) {
remediationTaskHandler.launch(campaign, task, result);
} else {
result.recordWarning("The automated remediation is not configured. The campaign state was set to IN REMEDIATION, but all remediation actions have to be done by hand.");
}
campaign = updateHelper.refreshCampaign(campaign, result);
eventHelper.onCampaignStageStart(campaign, task, result);
}
} catch (RuntimeException e) {
result.recordFatalError("Couldn't start the remediation: unexpected exception: " + e.getMessage(), e);
throw e;
} finally {
result.computeStatusIfUnknown();
}
}
@Deprecated
@Override
public List<AccessCertificationCaseType> searchDecisionsToReview(ObjectQuery caseQuery, boolean notDecidedOnly,
Collection<SelectorOptions<GetOperationOptions>> options,
Task task, OperationResult parentResult)
throws ObjectNotFoundException, SchemaException, SecurityViolationException {
throw new UnsupportedOperationException("not available any more");
}
@Override
public List<AccessCertificationWorkItemType> searchOpenWorkItems(ObjectQuery baseWorkItemsQuery, boolean notDecidedOnly,
Collection<SelectorOptions<GetOperationOptions>> options, Task task, OperationResult parentResult)
throws ObjectNotFoundException, SchemaException, SecurityViolationException {
OperationResult result = parentResult.createSubresult(OPERATION_SEARCH_OPEN_WORK_ITEMS);
try {
securityEnforcer.authorize(ModelAuthorizationAction.READ_OWN_CERTIFICATION_DECISIONS.getUrl(), null,
null, null, null, null, result);
return queryHelper.searchOpenWorkItems(baseWorkItemsQuery, SecurityUtil.getPrincipal(), notDecidedOnly, options, result);
} catch (RuntimeException e) {
result.recordFatalError("Couldn't search for certification work items: unexpected exception: " + e.getMessage(), e);
throw e;
} finally {
result.computeStatusIfUnknown();
}
}
@Override
public void recordDecision(@NotNull String campaignOid, long caseId, long workItemId, @Nullable AccessCertificationResponseType response,
@Nullable String comment, Task task, OperationResult parentResult) throws ObjectNotFoundException, SchemaException,
SecurityViolationException, ObjectAlreadyExistsException {
OperationResult result = parentResult.createSubresult(OPERATION_RECORD_DECISION);
try {
securityEnforcer.authorize(ModelAuthorizationAction.RECORD_CERTIFICATION_DECISION.getUrl(), null,
null, null, null, null, result);
caseHelper.recordDecision(campaignOid, caseId, workItemId, response, comment, task, result);
} catch (RuntimeException e) {
result.recordFatalError("Couldn't record reviewer decision: unexpected exception: " + e.getMessage(), e);
throw e;
} finally {
result.computeStatusIfUnknown();
}
}
public void delegateWorkItems(@NotNull String campaignOid, @NotNull List<AccessCertificationWorkItemType> workItems,
@NotNull DelegateWorkItemActionType delegateAction, Task task,
OperationResult parentResult)
throws SchemaException, SecurityViolationException, ExpressionEvaluationException, ObjectNotFoundException,
ObjectAlreadyExistsException {
OperationResult result = parentResult.createSubresult(OPERATION_DELEGATE_WORK_ITEMS);
result.addParam("campaignOid", campaignOid);
result.addCollectionOfSerializablesAsParam("workItems", workItems); // TODO only IDs?
result.addParam("delegateAction", delegateAction);
try {
// TODO security
securityEnforcer.authorize(ModelAuthorizationAction.DELEGATE_ALL_WORK_ITEMS.getUrl(), null,
null, null, null, null, result);
updateHelper.delegateWorkItems(campaignOid, workItems, delegateAction, task, result);
} catch (RuntimeException|CommonException e) {
result.recordFatalError("Couldn't delegate work items: unexpected exception: " + e.getMessage(), e);
throw e;
} finally {
result.computeStatusIfUnknown();
}
}
@Override
public void closeCampaign(String campaignOid, Task task, OperationResult parentResult) throws ObjectNotFoundException, SchemaException, SecurityViolationException, ObjectAlreadyExistsException {
Validate.notNull(campaignOid, "campaignOid");
Validate.notNull(task, "task");
Validate.notNull(parentResult, "parentResult");
OperationResult result = parentResult.createSubresult(OPERATION_CLOSE_CAMPAIGN);
try {
AccessCertificationCampaignType campaign = generalHelper.getCampaign(campaignOid, null, task, result);
securityEnforcer.authorize(ModelAuthorizationAction.CLOSE_CERTIFICATION_CAMPAIGN.getUrl(), null,
campaign.asPrismObject(), null, null, null, result);
updateHelper.closeCampaign(campaign, task, result);
} catch (RuntimeException e) {
result.recordFatalError("Couldn't close certification campaign: unexpected exception: " + e.getMessage(), e);
throw e;
} finally {
result.computeStatusIfUnknown();
}
}
// this method delegates the authorization to the model
@Override
public AccessCertificationCasesStatisticsType getCampaignStatistics(String campaignOid, boolean currentStageOnly, Task task, OperationResult parentResult) throws ObjectNotFoundException, SchemaException, SecurityViolationException, ObjectAlreadyExistsException {
Validate.notNull(campaignOid, "campaignOid");
Validate.notNull(task, "task");
Validate.notNull(parentResult, "parentResult");
OperationResult result = parentResult.createSubresult(OPERATION_GET_CAMPAIGN_STATISTICS);
try {
AccessCertificationCasesStatisticsType stat = new AccessCertificationCasesStatisticsType(prismContext);
Collection<SelectorOptions<GetOperationOptions>> options = SelectorOptions.createCollection(F_CASE, GetOperationOptions.createRetrieve());
AccessCertificationCampaignType campaign;
try {
campaign = modelService.getObject(AccessCertificationCampaignType.class, campaignOid, options, task, parentResult).asObjectable();
} catch (CommunicationException|ConfigurationException e) {
throw new SystemException("Unexpected exception while getting campaign object: " + e.getMessage(), e);
}
int accept=0, revoke=0, revokeRemedied=0, reduce=0, reduceRemedied=0, noDecision=0, noResponse=0;
for (AccessCertificationCaseType _case : campaign.getCase()) {
AccessCertificationResponseType outcome;
if (currentStageOnly) {
if (_case.getStageNumber() == campaign.getStageNumber()) {
outcome = OutcomeUtils.fromUri(_case.getCurrentStageOutcome());
} else {
continue;
}
} else {
outcome = OutcomeUtils.fromUri(_case.getOutcome());
}
if (outcome == null) {
outcome = AccessCertificationResponseType.NO_RESPONSE;
}
switch (outcome) {
case ACCEPT: accept++; break;
case REVOKE: revoke++;
if (_case.getRemediedTimestamp() != null) {
revokeRemedied++;
}
break;
case REDUCE: reduce++;
if (_case.getRemediedTimestamp() != null) {
reduceRemedied++; // currently not possible
}
break;
case NOT_DECIDED: noDecision++; break;
case NO_RESPONSE: noResponse++; break;
default: throw new IllegalStateException("Unexpected outcome: "+outcome);
}
}
stat.setMarkedAsAccept(accept);
stat.setMarkedAsRevoke(revoke);
stat.setMarkedAsRevokeAndRemedied(revokeRemedied);
stat.setMarkedAsReduce(reduce);
stat.setMarkedAsReduceAndRemedied(reduceRemedied);
stat.setMarkedAsNotDecide(noDecision);
stat.setWithoutResponse(noResponse);
return stat;
} catch (RuntimeException e) {
result.recordFatalError("Couldn't get campaign statistics: unexpected exception: " + e.getMessage(), e);
throw e;
} finally {
result.computeStatusIfUnknown();
}
}
@Override
public void registerCertificationEventListener(AccessCertificationEventListener listener) {
eventHelper.registerEventListener(listener);
}
}