Java tutorial
/* * 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.impl.handlers.CertificationHandler; import com.evolveum.midpoint.model.api.ModelService; import com.evolveum.midpoint.model.api.PolicyViolationException; import com.evolveum.midpoint.prism.PrismContainerDefinition; import com.evolveum.midpoint.prism.PrismContainerValue; import com.evolveum.midpoint.prism.PrismContext; import com.evolveum.midpoint.prism.PrismObject; import com.evolveum.midpoint.prism.PrismObjectDefinition; import com.evolveum.midpoint.prism.PrismPropertyValue; import com.evolveum.midpoint.prism.PrismReference; import com.evolveum.midpoint.prism.delta.ContainerDelta; import com.evolveum.midpoint.prism.delta.ItemDelta; import com.evolveum.midpoint.prism.delta.ObjectDelta; import com.evolveum.midpoint.prism.delta.PropertyDelta; import com.evolveum.midpoint.prism.delta.ReferenceDelta; import com.evolveum.midpoint.prism.delta.builder.DeltaBuilder; import com.evolveum.midpoint.prism.parser.QueryConvertor; import com.evolveum.midpoint.prism.path.IdItemPathSegment; import com.evolveum.midpoint.prism.path.ItemPath; import com.evolveum.midpoint.prism.path.NameItemPathSegment; import com.evolveum.midpoint.prism.query.ObjectFilter; import com.evolveum.midpoint.prism.query.ObjectQuery; import com.evolveum.midpoint.prism.util.CloneUtil; import com.evolveum.midpoint.prism.xml.XmlTypeConverter; import com.evolveum.midpoint.repo.api.RepositoryService; import com.evolveum.midpoint.schema.ObjectDeltaOperation; import com.evolveum.midpoint.schema.ResultHandler; import com.evolveum.midpoint.schema.constants.ObjectTypes; 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.task.api.Task; import com.evolveum.midpoint.util.exception.CommunicationException; import com.evolveum.midpoint.util.exception.ConfigurationException; import com.evolveum.midpoint.util.exception.ExpressionEvaluationException; import com.evolveum.midpoint.util.exception.ObjectAlreadyExistsException; import com.evolveum.midpoint.util.exception.ObjectNotFoundException; import com.evolveum.midpoint.util.exception.SchemaException; import com.evolveum.midpoint.util.exception.SecurityViolationException; import com.evolveum.midpoint.util.exception.SystemException; import com.evolveum.midpoint.util.logging.LoggingUtils; import com.evolveum.midpoint.util.logging.Trace; import com.evolveum.midpoint.util.logging.TraceManager; import com.evolveum.midpoint.xml.ns._public.common.common_3.AccessCertificationCampaignStateType; import com.evolveum.midpoint.xml.ns._public.common.common_3.AccessCertificationCampaignType; import com.evolveum.midpoint.xml.ns._public.common.common_3.AccessCertificationCaseType; import com.evolveum.midpoint.xml.ns._public.common.common_3.AccessCertificationDecisionType; import com.evolveum.midpoint.xml.ns._public.common.common_3.AccessCertificationDefinitionType; import com.evolveum.midpoint.xml.ns._public.common.common_3.AccessCertificationObjectBasedScopeType; import com.evolveum.midpoint.xml.ns._public.common.common_3.AccessCertificationResponseType; import com.evolveum.midpoint.xml.ns._public.common.common_3.AccessCertificationReviewerSpecificationType; import com.evolveum.midpoint.xml.ns._public.common.common_3.AccessCertificationScopeType; import com.evolveum.midpoint.xml.ns._public.common.common_3.AccessCertificationStageDefinitionType; import com.evolveum.midpoint.xml.ns._public.common.common_3.AccessCertificationStageType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectReferenceType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType; import com.evolveum.midpoint.xml.ns._public.common.common_3.TriggerType; import com.evolveum.midpoint.xml.ns._public.common.common_3.UserType; import com.evolveum.prism.xml.ns._public.query_3.SearchFilterType; import net.sf.jasperreports.engine.util.ObjectUtils; import org.apache.commons.lang3.Validate; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Component; import javax.xml.datatype.XMLGregorianCalendar; import javax.xml.namespace.QName; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.List; import static com.evolveum.midpoint.xml.ns._public.common.common_3.AccessCertificationCampaignStateType.CLOSED; import static com.evolveum.midpoint.xml.ns._public.common.common_3.AccessCertificationCampaignStateType.IN_REVIEW_STAGE; import static com.evolveum.midpoint.xml.ns._public.common.common_3.AccessCertificationCampaignStateType.REVIEW_STAGE_DONE; import static com.evolveum.midpoint.xml.ns._public.common.common_3.AccessCertificationDefinitionType.F_LAST_CAMPAIGN_CLOSED_TIMESTAMP; import static com.evolveum.midpoint.xml.ns._public.common.common_3.AccessCertificationDefinitionType.F_LAST_CAMPAIGN_ID_USED; import static com.evolveum.midpoint.xml.ns._public.common.common_3.AccessCertificationDefinitionType.F_LAST_CAMPAIGN_STARTED_TIMESTAMP; /** * @author mederly */ @Component public class AccCertUpdateHelper { private static final transient Trace LOGGER = TraceManager.getTrace(AccCertUpdateHelper.class); @Autowired private AccCertReviewersHelper reviewersHelper; @Autowired protected AccCertEventHelper eventHelper; @Autowired private PrismContext prismContext; @Autowired private ModelService modelService; @Autowired protected SecurityEnforcer securityEnforcer; @Autowired protected AccCertGeneralHelper helper; @Autowired protected AccCertResponseComputationHelper computationHelper; @Autowired protected AccCertQueryHelper queryHelper; // TODO temporary hack because of some problems in model service... @Autowired @Qualifier("cacheRepositoryService") protected RepositoryService repositoryService; void addObject(ObjectType objectType, Task task, OperationResult result) throws CommunicationException, ObjectAlreadyExistsException, ExpressionEvaluationException, PolicyViolationException, SchemaException, SecurityViolationException, ConfigurationException, ObjectNotFoundException { ObjectDelta objectDelta = ObjectDelta.createAddDelta(objectType.asPrismObject()); Collection<ObjectDeltaOperation<?>> ops = modelService .executeChanges((Collection) Arrays.asList(objectDelta), null, task, result); ObjectDeltaOperation odo = ops.iterator().next(); objectType.setOid(odo.getObjectDelta().getOid()); } void recordDecision(AccessCertificationCampaignType campaign, long caseId, AccessCertificationDecisionType decision, OperationResult result) throws SecurityViolationException, ObjectNotFoundException, SchemaException, ObjectAlreadyExistsException { String campaignOid = campaign.getOid(); if (!AccessCertificationCampaignStateType.IN_REVIEW_STAGE.equals(campaign.getState())) { throw new IllegalStateException("Campaign is not in review stage; its state is " + campaign.getState()); } decision = decision.clone(); // to not modify the original decision // filling-in missing pieces (if any) int currentStage = campaign.getStageNumber(); if (decision.getStageNumber() == 0) { decision.setStageNumber(currentStage); } else { if (decision.getStageNumber() != currentStage) { throw new IllegalStateException("Cannot add decision with stage number (" + decision.getStageNumber() + ") other than current (" + currentStage + ")"); } } if (decision.getTimestamp() == null) { decision.setTimestamp(XmlTypeConverter.createXMLGregorianCalendar(System.currentTimeMillis())); } if (decision.getReviewerRef() == null) { UserType currentUser = securityEnforcer.getPrincipal().getUser(); decision.setReviewerRef(ObjectTypeUtil.createObjectRef(currentUser)); } AccessCertificationCaseType _case = queryHelper.getCase(campaignOid, caseId, result); if (_case == null) { throw new ObjectNotFoundException( "Case " + caseId + " was not found in campaign " + ObjectTypeUtil.toShortString(campaign)); } // if (!_case.isEnabled()) { // throw new IllegalStateException("Cannot update case because it is not update-enabled in this stage"); // } AccessCertificationDecisionType existingDecision = CertCampaignTypeUtil.findDecision(_case, currentStage, decision.getReviewerRef().getOid()); if (existingDecision != null && existingDecision.equals(decision)) { // very unprobable, but ... to be sure let's check it // we skip the operation because add+delete the same value is unpredictable } else { ItemPath decisionPath = new ItemPath(new NameItemPathSegment(AccessCertificationCampaignType.F_CASE), new IdItemPathSegment(caseId), new NameItemPathSegment(AccessCertificationCaseType.F_DECISION)); Collection<ItemDelta> deltaList = new ArrayList<>(); // let's remove existing decision and add the new one if (existingDecision != null) { ContainerDelta<AccessCertificationDecisionType> decisionDeleteDelta = ContainerDelta .createModificationDelete(decisionPath, AccessCertificationCampaignType.class, prismContext, existingDecision.clone()); deltaList.add(decisionDeleteDelta); } ContainerDelta<AccessCertificationDecisionType> decisionAddDelta = ContainerDelta.createModificationAdd( decisionPath, AccessCertificationCampaignType.class, prismContext, decision); deltaList.add(decisionAddDelta); AccessCertificationResponseType newResponse = computationHelper.computeResponseForStage(_case, decision, campaign); if (!ObjectUtils.equals(newResponse, _case.getCurrentResponse())) { PropertyDelta currentResponseDelta = PropertyDelta.createModificationReplaceProperty( new ItemPath(new NameItemPathSegment(AccessCertificationCampaignType.F_CASE), new IdItemPathSegment(_case.asPrismContainerValue().getId()), new NameItemPathSegment(AccessCertificationCaseType.F_CURRENT_RESPONSE)), helper.getCampaignObjectDefinition(), newResponse); deltaList.add(currentResponseDelta); } // TODO: call model service instead of repository repositoryService.modifyObject(AccessCertificationCampaignType.class, campaignOid, deltaList, result); } } void closeCampaign(AccessCertificationCampaignType campaign, Task task, OperationResult result) throws ObjectAlreadyExistsException, ObjectNotFoundException, SchemaException { LOGGER.info("Closing campaign {}", ObjectTypeUtil.toShortString(campaign)); int currentStageNumber = campaign.getStageNumber(); int lastStageNumber = CertCampaignTypeUtil.getNumberOfStages(campaign); AccessCertificationCampaignStateType currentState = campaign.getState(); // TODO issue a warning if we are not in a correct state PropertyDelta<Integer> stageNumberDelta = createStageNumberDelta(lastStageNumber + 1); PropertyDelta<AccessCertificationCampaignStateType> stateDelta = createStateDelta(CLOSED); ContainerDelta triggerDelta = createTriggerDeleteDelta(); repositoryService.modifyObject(AccessCertificationCampaignType.class, campaign.getOid(), Arrays.asList(stateDelta, stageNumberDelta, triggerDelta), result); AccessCertificationCampaignType updatedCampaign = refreshCampaign(campaign, task, result); LOGGER.info("Updated campaign state: {}", updatedCampaign.getState()); // if (currentState == IN_REMEDIATION) { // eventHelper.onCampaignStageEnd(updatedCampaign, task, result); // } eventHelper.onCampaignEnd(updatedCampaign, task, result); if (campaign.getDefinitionRef() != null) { List<ItemDelta> deltas = DeltaBuilder.deltaFor(AccessCertificationDefinitionType.class, prismContext) .item(F_LAST_CAMPAIGN_CLOSED_TIMESTAMP) .replace(XmlTypeConverter.createXMLGregorianCalendar(new Date())).asItemDeltas(); repositoryService.modifyObject(AccessCertificationDefinitionType.class, campaign.getDefinitionRef().getOid(), deltas, result); } } // TODO implement more efficiently public AccessCertificationCampaignType refreshCampaign(AccessCertificationCampaignType campaign, Task task, OperationResult result) throws ObjectNotFoundException, SchemaException { return repositoryService.getObject(AccessCertificationCampaignType.class, campaign.getOid(), null, result) .asObjectable(); } void setStageNumberAndState(AccessCertificationCampaignType campaign, int number, AccessCertificationCampaignStateType state, Task task, OperationResult result) throws ObjectAlreadyExistsException, ObjectNotFoundException, SchemaException { PropertyDelta<Integer> stageNumberDelta = createStageNumberDelta(number); PropertyDelta<AccessCertificationCampaignStateType> stateDelta = createStateDelta(state); // todo switch to model service repositoryService.modifyObject(AccessCertificationCampaignType.class, campaign.getOid(), Arrays.asList(stateDelta, stageNumberDelta), result); } private void setState(AccessCertificationCampaignType campaign, AccessCertificationCampaignStateType state, Task task, OperationResult result) throws ObjectAlreadyExistsException, ObjectNotFoundException, SchemaException { PropertyDelta<AccessCertificationCampaignStateType> stateDelta = createStateDelta(state); // todo switch to model service repositoryService.modifyObject(AccessCertificationCampaignType.class, campaign.getOid(), Arrays.asList(stateDelta), result); } private PropertyDelta<Integer> createStageNumberDelta(int number) { return PropertyDelta.createReplaceDelta(helper.getCampaignObjectDefinition(), AccessCertificationCampaignType.F_STAGE_NUMBER, number); } private PropertyDelta<AccessCertificationCampaignStateType> createStateDelta( AccessCertificationCampaignStateType state) { return PropertyDelta.createReplaceDelta(helper.getCampaignObjectDefinition(), AccessCertificationCampaignType.F_STATE, state); } private PropertyDelta<XMLGregorianCalendar> createStartTimeDelta(XMLGregorianCalendar date) { return PropertyDelta.createReplaceDelta(helper.getCampaignObjectDefinition(), AccessCertificationCampaignType.F_START, date); } public void moveToNextStage(AccessCertificationCampaignType campaign, AccessCertificationStageType stage, CertificationHandler handler, final Task task, OperationResult result) throws SchemaException, SecurityViolationException, ObjectNotFoundException, CommunicationException, ConfigurationException, ExpressionEvaluationException, PolicyViolationException, ObjectAlreadyExistsException { Validate.notNull(campaign, "certificationCampaign"); Validate.notNull(campaign.getOid(), "certificationCampaign.oid"); int stageNumber = campaign.getStageNumber(); if (LOGGER.isTraceEnabled()) { LOGGER.trace("moveToNextStage starting; campaign = {}, stage number = {}", ObjectTypeUtil.toShortString(campaign), stageNumber); } if (stageNumber == 0) { createCases(campaign, stage, handler, task, result); } else { updateCases(campaign, stage, task, result); } LOGGER.trace("moveToNextStage finishing"); } protected AccessCertificationStageType createStage(AccessCertificationCampaignType campaign, int requestedStageNumber) { AccessCertificationStageType stage = new AccessCertificationStageType(prismContext); stage.setNumber(requestedStageNumber); stage.setStart(XmlTypeConverter.createXMLGregorianCalendar(new Date())); AccessCertificationStageDefinitionType stageDef = CertCampaignTypeUtil.findStageDefinition(campaign, stage.getNumber()); XMLGregorianCalendar end = (XMLGregorianCalendar) stage.getStart().clone(); if (stageDef.getDays() != null) { end.add(XmlTypeConverter.createDuration(true, 0, 0, stageDef.getDays(), 0, 0, 0)); } end.setHour(23); end.setMinute(59); end.setSecond(59); end.setMillisecond(999); stage.setEnd(end); stage.setName(stageDef.getName()); stage.setDescription(stageDef.getDescription()); return stage; } private void createCases(final AccessCertificationCampaignType campaign, AccessCertificationStageType stage, final CertificationHandler handler, final Task task, final OperationResult result) throws SchemaException, ObjectNotFoundException, SecurityViolationException, CommunicationException, ConfigurationException, ExpressionEvaluationException, PolicyViolationException, ObjectAlreadyExistsException { String campaignShortName = ObjectTypeUtil.toShortString(campaign); AccessCertificationScopeType scope = campaign.getScopeDefinition(); LOGGER.trace("Creating cases for scope {} in campaign {}", scope, campaignShortName); if (scope != null && !(scope instanceof AccessCertificationObjectBasedScopeType)) { throw new IllegalStateException("Unsupported access certification scope type: " + scope.getClass() + " for campaign " + campaignShortName); } AccessCertificationObjectBasedScopeType objectBasedScope = (AccessCertificationObjectBasedScopeType) scope; List<AccessCertificationCaseType> existingCases = queryHelper.searchCases(campaign.getOid(), null, null, task, result); if (!existingCases.isEmpty()) { throw new IllegalStateException( "Unexpected " + existingCases.size() + " certification case(s) in campaign object " + campaignShortName + ". At this time there should be none."); } // create a query to find target objects from which certification cases will be created ObjectQuery query = new ObjectQuery(); QName objectType = objectBasedScope != null ? objectBasedScope.getObjectType() : null; if (objectType == null) { objectType = handler.getDefaultObjectType(); } if (objectType == null) { throw new IllegalStateException( "Unspecified object type (and no default one provided) for campaign " + campaignShortName); } PrismObjectDefinition<? extends ObjectType> objectDef = prismContext.getSchemaRegistry() .findObjectDefinitionByType(objectType); if (objectDef == null) { throw new IllegalStateException("Object definition not found for object type " + objectType + " in campaign " + campaignShortName); } Class<? extends ObjectType> objectClass = objectDef.getCompileTimeClass(); if (objectClass == null) { throw new IllegalStateException( "Object class not found for object type " + objectType + " in campaign " + campaignShortName); } SearchFilterType searchFilter = objectBasedScope != null ? objectBasedScope.getSearchFilter() : null; if (searchFilter != null) { ObjectFilter filter = QueryConvertor.parseFilter(searchFilter, objectClass, prismContext); query.setFilter(filter); } final List<AccessCertificationCaseType> caseList = new ArrayList<>(); // create certification cases by executing the query and caseExpression on its results // here the subclasses of this class come into play ResultHandler<ObjectType> resultHandler = new ResultHandler<ObjectType>() { @Override public boolean handle(PrismObject<ObjectType> object, OperationResult parentResult) { try { caseList.addAll(handler.createCasesForObject(object, campaign, task, parentResult)); } catch (ExpressionEvaluationException | ObjectNotFoundException | SchemaException e) { // TODO process the exception more intelligently throw new SystemException( "Cannot create certification case for object " + ObjectTypeUtil.toShortString(object.asObjectable()) + ": " + e.getMessage(), e); } return true; } }; modelService.searchObjectsIterative(objectClass, query, (ResultHandler) resultHandler, null, task, result); AccessCertificationReviewerSpecificationType reviewerSpec = reviewersHelper .findReviewersSpecification(campaign, 1, task, result); // put the cases into repository ContainerDelta<AccessCertificationCaseType> caseDelta = ContainerDelta.createDelta( AccessCertificationCampaignType.F_CASE, AccessCertificationCampaignType.class, prismContext); for (int i = 0; i < caseList.size(); i++) { AccessCertificationCaseType _case = caseList.get(i); _case.setReviewRequestedTimestamp(stage.getStart()); _case.setReviewDeadline(stage.getEnd()); _case.setCurrentResponse(null); _case.setCurrentStageNumber(1); reviewersHelper.setupReviewersForCase(_case, campaign, reviewerSpec, task, result); List<AccessCertificationDecisionType> decisions = createEmptyDecisionsForCase(_case.getReviewerRef(), 1); _case.getDecision().addAll(decisions); PrismContainerValue<AccessCertificationCaseType> caseCVal = _case.asPrismContainerValue(); caseCVal.setId((long) (i + 1)); caseDelta.addValueToAdd(caseCVal); if (LOGGER.isTraceEnabled()) { LOGGER.trace("Adding certification case:\n{}", caseCVal.debugDump()); } } // there are some problems with container IDs when using model - as a temporary hack we go directly into repo // todo fix it and switch to model service // ObjectDelta<AccessCertificationCampaignType> campaignDelta = ObjectDelta.createModifyDelta(campaign.getOid(), // caseDelta, AccessCertificationCampaignType.class, prismContext); // modelService.executeChanges((Collection) Arrays.asList(campaignDelta), null, task, result); ContainerDelta<AccessCertificationStageType> stageDelta = ContainerDelta.createDelta( AccessCertificationCampaignType.F_STAGE, AccessCertificationCampaignType.class, prismContext); stageDelta.addValueToAdd(stage.asPrismContainerValue()); repositoryService.modifyObject(AccessCertificationCampaignType.class, campaign.getOid(), Arrays.asList(caseDelta, stageDelta), result); LOGGER.trace("Created stage and {} cases for campaign {}", caseList.size(), campaignShortName); } // BRUTAL HACK: we fill-in decisions when in stage 1 (to be fixed in repository implementation) private List<AccessCertificationDecisionType> createEmptyDecisionsForCase( List<ObjectReferenceType> forReviewers, int forStage) { long id = 1; List<AccessCertificationDecisionType> decisions = new ArrayList<>(); for (ObjectReferenceType reviewer : forReviewers) { AccessCertificationDecisionType decision = new AccessCertificationDecisionType(prismContext); decision.setReviewerRef(reviewer); decision.setStageNumber(forStage); decision.setResponse(null); decision.setTimestamp(null); if (forStage == 1) { decision.setId(id++); } decisions.add(decision); } return decisions; } private void updateCases(AccessCertificationCampaignType campaign, AccessCertificationStageType stage, Task task, OperationResult result) throws SchemaException, ObjectAlreadyExistsException, ObjectNotFoundException, SecurityViolationException, ConfigurationException, CommunicationException { LOGGER.trace("Updating reviewers and timestamps for cases in {}", ObjectTypeUtil.toShortString(campaign)); List<AccessCertificationCaseType> caseList = queryHelper.searchCases(campaign.getOid(), null, null, task, result); int stageToBe = campaign.getStageNumber() + 1; AccessCertificationReviewerSpecificationType reviewerSpec = reviewersHelper .findReviewersSpecification(campaign, stageToBe, task, result); List<ItemDelta> campaignDeltaList = new ArrayList<>(caseList.size()); for (int i = 0; i < caseList.size(); i++) { AccessCertificationCaseType _case = caseList.get(i); boolean enabled = computationHelper.computeEnabled(campaign, _case); // PropertyDelta enabledDelta = PropertyDelta.createModificationReplaceProperty( // new ItemPath( // new NameItemPathSegment(AccessCertificationCampaignType.F_CASE), // new IdItemPathSegment(_case.asPrismContainerValue().getId()), // new NameItemPathSegment(AccessCertificationCaseType.F_ENABLED)), // helper.getCampaignObjectDefinition(), enabled); // campaignDeltaList.add(enabledDelta); if (enabled) { reviewersHelper.setupReviewersForCase(_case, campaign, reviewerSpec, task, result); } else { _case.getReviewerRef().clear(); } PrismReference reviewersRef = _case.asPrismContainerValue() .findOrCreateReference(AccessCertificationCaseType.F_REVIEWER_REF); ReferenceDelta reviewerDelta = ReferenceDelta.createModificationReplace( new ItemPath(new NameItemPathSegment(AccessCertificationCampaignType.F_CASE), new IdItemPathSegment(_case.asPrismContainerValue().getId()), new NameItemPathSegment(AccessCertificationCaseType.F_REVIEWER_REF)), helper.getCampaignObjectDefinition(), CloneUtil.cloneCollectionMembers(reviewersRef.getValues())); campaignDeltaList.add(reviewerDelta); List<AccessCertificationDecisionType> newDecisions = createEmptyDecisionsForCase(_case.getReviewerRef(), stageToBe); PrismContainerDefinition decisionDef = prismContext.getSchemaRegistry() .findComplexTypeDefinitionByCompileTimeClass(AccessCertificationCaseType.class) .findContainerDefinition(AccessCertificationCaseType.F_DECISION); ContainerDelta decisionDelta = ContainerDelta.createDelta(AccessCertificationCaseType.F_DECISION, decisionDef); for (AccessCertificationDecisionType newDecision : newDecisions) { decisionDelta.addValueToAdd(new PrismPropertyValue<>(newDecision)); } PropertyDelta reviewRequestedTimestampDelta = PropertyDelta .createModificationReplaceProperty( new ItemPath(new NameItemPathSegment(AccessCertificationCampaignType.F_CASE), new IdItemPathSegment(_case.asPrismContainerValue().getId()), new NameItemPathSegment( AccessCertificationCaseType.F_REVIEW_REQUESTED_TIMESTAMP)), helper.getCampaignObjectDefinition(), enabled ? Arrays.asList(stage.getStart()) : new ArrayList(0)); campaignDeltaList.add(reviewRequestedTimestampDelta); ItemPath deadlinePath = new ItemPath(new NameItemPathSegment(AccessCertificationCampaignType.F_CASE), new IdItemPathSegment(_case.asPrismContainerValue().getId()), new NameItemPathSegment(AccessCertificationCaseType.F_REVIEW_DEADLINE)); PropertyDelta deadlineDelta = PropertyDelta.createModificationReplaceProperty(deadlinePath, helper.getCampaignObjectDefinition(), enabled ? Arrays.asList(stage.getEnd()) : new ArrayList(0)); campaignDeltaList.add(deadlineDelta); if (enabled) { PropertyDelta currentResponseDelta = PropertyDelta.createModificationReplaceProperty( new ItemPath(new NameItemPathSegment(AccessCertificationCampaignType.F_CASE), new IdItemPathSegment(_case.asPrismContainerValue().getId()), new NameItemPathSegment(AccessCertificationCaseType.F_CURRENT_RESPONSE)), helper.getCampaignObjectDefinition()); campaignDeltaList.add(currentResponseDelta); PropertyDelta currentResponseStageDelta = PropertyDelta .createModificationReplaceProperty( new ItemPath(new NameItemPathSegment(AccessCertificationCampaignType.F_CASE), new IdItemPathSegment(_case.asPrismContainerValue().getId()), new NameItemPathSegment( AccessCertificationCaseType.F_CURRENT_STAGE_NUMBER)), helper.getCampaignObjectDefinition(), stageToBe); campaignDeltaList.add(currentResponseStageDelta); } } ContainerDelta<AccessCertificationStageType> stageDelta = ContainerDelta.createDelta( AccessCertificationCampaignType.F_STAGE, AccessCertificationCampaignType.class, prismContext); stageDelta.addValueToAdd(stage.asPrismContainerValue()); campaignDeltaList.add(stageDelta); repositoryService.modifyObject(AccessCertificationCampaignType.class, campaign.getOid(), campaignDeltaList, result); LOGGER.debug("Created a stage, updated reviewers and timestamps in {} cases for campaign {}", caseList.size(), ObjectTypeUtil.toShortString(campaign)); } void recordMoveToNextStage(AccessCertificationCampaignType campaign, AccessCertificationStageType newStage, Task task, OperationResult result) throws ObjectNotFoundException, SchemaException, ObjectAlreadyExistsException, SecurityViolationException, ConfigurationException, CommunicationException { boolean campaignJustCreated; // some bureaucracy... stage#, state, start time, trigger List<AccessCertificationCaseType> caseList = queryHelper.searchCases(campaign.getOid(), null, null, task, result); List<ItemDelta> itemDeltaList = new ArrayList<>(); PropertyDelta<Integer> stageNumberDelta = createStageNumberDelta(newStage.getNumber()); itemDeltaList.add(stageNumberDelta); PropertyDelta<AccessCertificationCampaignStateType> stateDelta = createStateDelta(IN_REVIEW_STAGE); itemDeltaList.add(stateDelta); if (newStage.getNumber() == 1) { PropertyDelta<XMLGregorianCalendar> startDelta = createStartTimeDelta( XmlTypeConverter.createXMLGregorianCalendar(new Date())); itemDeltaList.add(startDelta); campaignJustCreated = true; } else { campaignJustCreated = false; } if (newStage.getEnd() != null) { XMLGregorianCalendar end = newStage.getEnd(); AccessCertificationStageDefinitionType stageDef = CertCampaignTypeUtil.findStageDefinition(campaign, newStage.getNumber()); List<TriggerType> triggers = new ArrayList<>(); // pseudo-random ID so this trigger will not be deleted by trigger task handler (if this code itself is executed as part of previous trigger firing) // TODO implement this more seriously! long lastId = (long) (Math.random() * 1000000000); TriggerType triggerClose = new TriggerType(prismContext); triggerClose.setHandlerUri(AccessCertificationCloseStageTriggerHandler.HANDLER_URI); triggerClose.setTimestamp(end); triggerClose.setId(lastId); triggers.add(triggerClose); for (Integer hoursBeforeDeadline : stageDef.getNotifyBeforeDeadline()) { XMLGregorianCalendar beforeEnd = null; beforeEnd = CloneUtil.clone(end); beforeEnd.add(XmlTypeConverter.createDuration(false, 0, 0, 0, hoursBeforeDeadline, 0, 0)); if (XmlTypeConverter.toMillis(beforeEnd) > System.currentTimeMillis()) { TriggerType triggerBeforeEnd = new TriggerType(prismContext); triggerBeforeEnd .setHandlerUri(AccessCertificationCloseStageApproachingTriggerHandler.HANDLER_URI); triggerBeforeEnd.setTimestamp(beforeEnd); triggerBeforeEnd.setId(++lastId); triggers.add(triggerBeforeEnd); } } ContainerDelta<TriggerType> triggerDelta = ContainerDelta.createModificationReplace( ObjectType.F_TRIGGER, AccessCertificationCampaignType.class, prismContext, triggers); itemDeltaList.add(triggerDelta); } repositoryService.modifyObject(AccessCertificationCampaignType.class, campaign.getOid(), itemDeltaList, result); // notifications campaign = refreshCampaign(campaign, task, result); if (campaign.getStageNumber() == 1) { eventHelper.onCampaignStart(campaign, task, result); } eventHelper.onCampaignStageStart(campaign, task, result); Collection<String> reviewers = eventHelper.getCurrentReviewers(campaign, caseList); for (String reviewerOid : reviewers) { List<AccessCertificationCaseType> cases = queryHelper.getCasesForReviewer(campaign, reviewerOid, task, result); ObjectReferenceType reviewerRef = ObjectTypeUtil.createObjectRef(reviewerOid, ObjectTypes.USER); eventHelper.onReviewRequested(reviewerRef, cases, campaign, task, result); } if (campaignJustCreated && campaign.getDefinitionRef() != null) { List<ItemDelta> deltas = DeltaBuilder.deltaFor(AccessCertificationDefinitionType.class, prismContext) .item(F_LAST_CAMPAIGN_STARTED_TIMESTAMP) .replace(XmlTypeConverter.createXMLGregorianCalendar(new Date())).asItemDeltas(); repositoryService.modifyObject(AccessCertificationDefinitionType.class, campaign.getDefinitionRef().getOid(), deltas, result); } } void recordCloseCurrentState(AccessCertificationCampaignType campaign, Task task, OperationResult result) throws ObjectNotFoundException, SchemaException, ObjectAlreadyExistsException, SecurityViolationException, ConfigurationException, CommunicationException { List<ItemDelta> campaignDeltaList = createCurrentResponsesDeltas(campaign, task, result); PropertyDelta<AccessCertificationCampaignStateType> stateDelta = createStateDelta(REVIEW_STAGE_DONE); campaignDeltaList.add(stateDelta); ContainerDelta triggerDelta = createTriggerDeleteDelta(); campaignDeltaList.add(triggerDelta); repositoryService.modifyObject(AccessCertificationCampaignType.class, campaign.getOid(), campaignDeltaList, result); campaign = refreshCampaign(campaign, task, result); eventHelper.onCampaignStageEnd(campaign, task, result); } private ContainerDelta createTriggerDeleteDelta() { return ContainerDelta.createModificationReplace(ObjectType.F_TRIGGER, helper.getCampaignObjectDefinition()); } private List<ItemDelta> createCurrentResponsesDeltas(AccessCertificationCampaignType campaign, Task task, OperationResult result) throws ConfigurationException, ObjectNotFoundException, SchemaException, CommunicationException, SecurityViolationException { List<ItemDelta> campaignDeltaList = new ArrayList<>(); LOGGER.trace("Updating current response for cases in {}", ObjectTypeUtil.toShortString(campaign)); List<AccessCertificationCaseType> caseList = queryHelper.searchCases(campaign.getOid(), null, null, task, result); for (int i = 0; i < caseList.size(); i++) { AccessCertificationCaseType _case = caseList.get(i); if (_case.getCurrentStageNumber() != campaign.getStageNumber()) { continue; } AccessCertificationResponseType newResponse = computationHelper.computeResponseForStage(_case, campaign); if (newResponse != _case.getCurrentResponse()) { if (newResponse == null) { throw new IllegalStateException("Computed currentResponse is null for case id " + _case.asPrismContainerValue().getId()); } PropertyDelta currentResponseDelta = PropertyDelta.createModificationReplaceProperty( new ItemPath(new NameItemPathSegment(AccessCertificationCampaignType.F_CASE), new IdItemPathSegment(_case.asPrismContainerValue().getId()), new NameItemPathSegment(AccessCertificationCaseType.F_CURRENT_RESPONSE)), helper.getCampaignObjectDefinition(), newResponse); campaignDeltaList.add(currentResponseDelta); } } return campaignDeltaList; } // TODO temporary implementation - should be done somehow in batches in order to improve performance public void markCaseAsRemedied(String campaignOid, long caseId, Task task, OperationResult parentResult) throws ObjectAlreadyExistsException, ObjectNotFoundException, SchemaException { Validate.notNull(campaignOid, "campaignOid"); Validate.notNull(task, "task"); Validate.notNull(parentResult, "parentResult"); PropertyDelta reviewRemediedDelta = PropertyDelta.createModificationReplaceProperty( new ItemPath(new NameItemPathSegment(AccessCertificationCampaignType.F_CASE), new IdItemPathSegment(caseId), new NameItemPathSegment(AccessCertificationCaseType.F_REMEDIED_TIMESTAMP)), helper.getCampaignObjectDefinition(), XmlTypeConverter.createXMLGregorianCalendar(new Date())); repositoryService.modifyObject(AccessCertificationCampaignType.class, campaignOid, Arrays.asList(reviewRemediedDelta), parentResult); } public void recordLastCampaignIdUsed(String definitionOid, int lastIdUsed, Task task, OperationResult result) { try { List<ItemDelta> modifications = DeltaBuilder .deltaFor(AccessCertificationDefinitionType.class, prismContext).item(F_LAST_CAMPAIGN_ID_USED) .replace(lastIdUsed).asItemDeltas(); repositoryService.modifyObject(AccessCertificationDefinitionType.class, definitionOid, modifications, result); } catch (SchemaException | ObjectNotFoundException | RuntimeException | ObjectAlreadyExistsException e) { LoggingUtils.logUnexpectedException(LOGGER, "Couldn't update last campaign ID for definition {}", e, definitionOid); } } }